# Authentication and permissions

## Credential layers

| Credential | Form | Purpose | Can read data |
|---|---|---|---|
| Site ID | `st_...`, public | Attributing collected data | No |
| Browser session | HttpOnly cookie (production: `__Host-`, Secure, SameSite=Strict) | Web dashboard | Yes (by role) |
| Recovery key | `strk_...`, 256-bit random, only a digest is stored | Restore the workspace on a new device, recent strong authentication | Only to establish a session |
| API credential | `stk_...` | REST and MCP (Bearer) | Yes (by scope, site and access window) |
| Ingest credential | `sti_...` + signing secret | Server / edge reporting | No (write-only) |
| Bootstrap token | `stb_...`, 1 hour | Programmatic onboarding | Summary only |
| OAuth token | Issued by /oauth/token | Generic MCP clients | Yes (as granted on the consent page) |

## Scopes

| scope | Description |
|---|---|
| `summary:read` | Read summary reports (PV, UV, sources, top pages, AI summary, etc.), excluding visitor/request-level details and revenue |
| `details:read` | Read visitor, session and single-request details and classification evidence (including untrusted raw fields such as path and UA) |
| `conversions:read` | Read goals, funnels and conversion counts (excluding revenue amounts) |
| `revenue:read` | Read revenue amounts and orders |
| `exports:write` | Create and download export files (export content is still limited by other read scopes) |
| `imports:write` | Upload and submit historical data imports |
| `sites:write` | Add/edit sites, groups, installation and verification |
| `settings:write` | Edit site settings, goals, funnels, filters, custom dimensions, campaign costs and retention settings |
| `reports:write` | Save reports, create schedules and delivery targets |
| `shares:write` | Create/revoke public shares (public sharing must still be enabled on the server) |
| `members:admin` | Invite, downgrade and remove members |
| `credentials:admin` | Create, rotate and revoke API credentials and grants |
| `audit:read` | Read audit logs |
| `usage:read` | Read usage, quotas and cost estimates |

By default an AI credential only has `summary:read`. Revenue, visitor details, and member and credential management must be granted separately; a grantor cannot grant more than they hold.

## Rules

- Every path containing an ID is re-checked for ownership; anything outside the granted scope returns 404, without revealing whether someone else's site exists.
- Write operations over a cookie session must be same-origin (Origin check); Bearer requests do not need cookies.
- Private endpoints do not allow cross-origin requests with credentials. `/api/v1/collect/browser` accepts any origin but is write-only, and validates Origin against the site's domain.
- High-risk actions such as deleting data, shortening retention or enabling public sharing can only be performed in a browser session, via "preview → re-enter the recovery key within 10 minutes → type the confirmation text". API credentials and MCP can only preview them.
- Mutating endpoints support `Idempotency-Key`; the same key with different parameters returns 409.
- Rate limiting returns 429 with `Retry-After`.

## Ingest signatures (server / edge)

```
Authorization: Bearer sti_...
X-OpenstatX-Timestamp: <unix seconds, ±300 seconds>
X-OpenstatX-Nonce: <16-64 random characters, single-use>
X-OpenstatX-Signature: v1=hex(HMAC-SHA256(signing_secret, "<ts>.<nonce>.<sha256(body)>"))
```

## OAuth (/mcp only)

- Discovery: `/.well-known/oauth-protected-resource/mcp`, `/.well-known/oauth-authorization-server`
- Authorization: `/authorize` (authorization code + PKCE S256); token: `/oauth/token`; dynamic registration: `/oauth/register` (rate-limited)
- Resource (audience): `/mcp`. Token permissions are re-checked against the workspace grant record on every request, so revocation takes effect immediately.
