Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 115 additions & 14 deletions docs/API_KEYS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,88 @@ API keys authenticate via the same `requireAuth` entry point as session JWTs. Th

## Scopes

| Scope | Description |
|-------|-------------|
| `portfolio:read` | Read portfolio data |
| `transactions:read` | Read transaction history |
| `deposit:write` | Create deposits |
| `withdraw:write` | Create withdrawals (opt-in per key) |
| `alerts:manage` | Manage alert rules |
| `fiat:write` | Create fiat orders |
| `recurring_deposits:write` | Manage recurring deposit plans |
| `goals:write` | Manage savings goals |
| `strategies:write` | Manage strategies |
| `webhooks:manage` | Manage webhook subscriptions |
| `vault:read` | Read vault data |
| `vault:write` | Write vault operations |
Every scope is described in a machine-readable catalogue
(`src/auth/scopes.ts` for user keys, `src/middleware/adminAuth.ts` for admin
keys). `GET /api/admin/keys/scopes` serves it for the key-creation UI, and
the tables below are generated from it — `tests/unit/auth/scopeCatalog.test.ts`
fails CI if the catalogue, the call sites and this document disagree.

The **Enforcement** column says whether the scope actually gates anything:

| Enforcement | Meaning |
| --- | --- |
| `enforced` | At least one route requires it. Granting it is meaningful; omitting it is restrictive. |
| `declared-only` | Declared and reservable, but no route requires it today — granting it currently grants nothing extra. |

The **Implies** column lists the scopes a scope grants on its own. `super`
implies every admin scope, and the legacy `agent` implies `agent:read` and
`agent:write`.

### User key scopes

<!-- BEGIN GENERATED: user-scope-catalog -->
| Scope | Risk | Enforcement | Implies | What it does | Guardrail | Endpoints |
| --- | --- | --- | --- | --- | --- | --- |
| `loans:read` | read | enforced | — | Read loan state and borrowing capacity. | — | `GET /api/v1/loans`, `GET /api/v1/loans/:loanId`, `GET /api/v1/loans/capacity` |
| `portfolio:read` | read | enforced | — | Read portfolio positions, balances, history, earnings and tax reports. | — | `GET /api/v1/portfolio/:userId`, `GET /api/v1/portfolio/:userId/earnings`, `GET /api/v1/portfolio/:userId/history`, `GET /api/v1/portfolio/:userId/suggestions`, `GET /api/v1/portfolio/:userId/tax-report` |
| `transactions:read` | read | enforced | — | Read transaction history. | — | `GET /api/v1/transactions/:id/events`, `GET /api/v1/transactions/:userId`, `GET /api/v1/transactions/detail/:txHash` |
| `vault:read` | read | enforced | — | Read vault state and balance. | — | `GET /api/v1/vault/balance` |
| `alerts:manage` | write | enforced | — | Manage alert rules. | — | `DELETE /api/v1/alerts/:id`, `PATCH /api/v1/alerts/:id`, `POST /api/v1/alerts`, `POST /api/v1/alerts/:id/restore` |
| `goals:write` | write | enforced | — | Manage savings goals. | — | `DELETE /api/v1/portfolio/goals/:id`, `PATCH /api/v1/portfolio/goals/:id`, `POST /api/v1/portfolio/goals` |
| `portfolio:write` | write | enforced | — | Change portfolio state. | — | `DELETE /api/v1/net-worth/external-wallets/:id`, `POST /api/v1/net-worth/external-wallets` |
| `strategies:write` | write | enforced | — | Publish, follow and unfollow strategies. | — | `POST /api/v1/strategies/publish` |
| `webhooks:manage` | write | enforced | — | Manage webhook endpoints and deliveries. | — | `DELETE /api/webhooks/:id`, `PATCH /api/webhooks/:id`, `POST /api/webhooks` |
| `deposit:write` | money | enforced | — | Create deposits. | Deposits still pass the compliance freeze and sub-account permission checks. | `POST /api/v1/deposit` |
| `fiat:write` | money | enforced | — | Create fiat on/off-ramp orders. | — | `POST /api/v1/fiat/orders` |
| `loans:write` | money | enforced | — | Originate and repay collateral loans. | Origination and repayment still pass the compliance freeze and sub-account permission checks. | `POST /api/v1/loans`, `POST /api/v1/loans/:loanId/repay` |
| `recurring_deposits:write` | money | enforced | — | Manage recurring deposit plans. | — | `DELETE /api/v1/deposit/recurring/:id`, `PATCH /api/v1/deposit/recurring/:id`, `POST /api/v1/deposit/recurring` |
| `vault:write` | money | enforced | — | Build vault transactions. | — | `POST /api/v1/vault/build-transaction` |
| `withdraw:write` | money | enforced | — | Create withdrawals. | Requires allowWithdrawals on the key AND the platform kill-switch USER_API_KEY_WITHDRAWALS_ENABLED to be on. | `DELETE /api/v1/recurring-withdrawals/:id`, `PATCH /api/v1/recurring-withdrawals/:id`, `POST /api/v1/recurring-withdrawals`, `POST /api/v1/withdraw` |
<!-- END GENERATED: user-scope-catalog -->

New keys are **read-only by default**. Write scopes must be explicitly requested.

### Admin key scopes

<!-- BEGIN GENERATED: admin-scope-catalog -->
| Scope | Risk | Enforcement | Implies | What it does | Guardrail | Endpoints |
| --- | --- | --- | --- | --- | --- | --- |
| `agent:read` | read | enforced | — | Read agent decisions and circuit-breaker state. | — | `GET /api/v1/admin/agent/breakers`, `GET /api/v1/admin/agent/decisions` |
| `dlq:read` | read | enforced | — | Inspect the dead-letter queue. | — | `GET /api/v1/admin/dlq/inspect`, `GET /api/v1/admin/notifications/dlq` |
| `erasure:read` | read | declared-only | — | Preview what an erasure would delete. | No route requires this scope today — the erasure preview route is gated by the generic read scope. Declared so a key can be provisioned ahead of a route that needs it. | — |
| `fiat:read` | read | enforced | — | Read fiat providers and their health. | — | `GET /api/v1/admin/fiat/providers` |
| `keys:read` | read | enforced | — | Read admin API keys and wallet rotation status. | — | `GET /api/v1/admin/keys`, `GET /api/v1/admin/keys/scopes`, `GET /api/v1/admin/wallets/rotation-status` |
| `messages:read` | read | enforced | — | Read message delivery state. | — | `GET /api/v1/admin/messages`, `GET /api/v1/admin/messages/:id`, `GET /api/v1/admin/messages/stats` |
| `metrics:read` | read | enforced | — | Read operational metrics and dashboards. | — | `GET /api/v1/admin/jobs/dashboard`, `GET /api/v1/admin/stellar/metrics` |
| `outbox:read` | read | enforced | — | Read the durable outbox. | — | `GET /api/v1/admin/outbox`, `GET /api/v1/admin/outbox/:id`, `GET /api/v1/admin/outbox/stats` |
| `read` | read | enforced | — | General read access to admin resources. | — | `GET /api/v1/admin/erasure/preview/:userId`, `GET /api/v1/admin/erasure/requests`, `GET /api/v1/admin/referrals/review` |
| `referrals:read` | read | enforced | — | Read flagged referral conversions. | — | `GET /api/v1/admin/referrals/flagged` |
| `reserves:read` | read | enforced | — | Read reserve and sponsorship state. | — | `GET /api/v1/admin/reserves` |
| `risk-metadata:read` | read | declared-only | — | Read protocol risk metadata. | No route requires this scope today — the risk-metadata review workflow is administered from src/config/riskMetadataAdmin.ts, not through the API. Declared so a key can be provisioned ahead of a route that needs it. | — |
| `sessions:read` | read | enforced | — | Read user sessions. | — | `GET /api/v1/admin/users/:id/sessions` |
| `support:read` | read | enforced | — | Read support tickets. | — | `GET /api/v1/admin/support/tickets`, `GET /api/v1/admin/support/tickets/:id` |
| `treasury:read` | read | enforced | — | Read treasury sweep policies. | — | `GET /api/v1/admin/treasury/policies` |
| `approvals:write` | write | enforced | — | Cancel pending approval requests. | — | `POST /api/v1/admin/approvals/:id/cancel` |
| `sessions:write` | write | enforced | — | Revoke user sessions. | — | `POST /api/v1/admin/users/:id/sessions/revoke-all` |
| `support:write` | write | enforced | — | Reply to and update support tickets. | — | `PATCH /api/v1/admin/support/tickets/:id`, `POST /api/v1/admin/support/tickets/:id/reply` |
| `write` | write | enforced | — | General write access to admin resources. | — | `POST /api/v1/admin/erasure/requests`, `POST /api/v1/admin/referrals/review/:id/approve`, `POST /api/v1/admin/referrals/review/:id/reject` |
| `outbox:write` | money | enforced | — | Force-retry and cancel queued outbox operations. | — | `POST /api/v1/admin/outbox/:id/cancel`, `POST /api/v1/admin/outbox/:id/retry` |
| `referrals:write` | money | enforced | — | Approve or reject flagged referral conversions. | — | `POST /api/v1/admin/referrals/:id/review` |
| `treasury:write` | money | enforced | — | Change treasury sweep policies and run a sweep. | Sweep policies and signer rotations are written to the admin audit log. | `POST /api/v1/admin/treasury/emergency-sweep`, `POST /api/v1/admin/treasury/policies`, `POST /api/v1/admin/treasury/signer-rotations`, `POST /api/v1/admin/treasury/signer-rotations/:id/finalize` |
| `wallet` | money | declared-only | — | Custodial wallet operations. | No route requires this scope today — the wallet rotation-status route is gated by keys:read. Declared so a key can be provisioned ahead of a route that needs it. | — |
| `erasure:write` | destructive | enforced | — | Create, approve, reject and execute erasure requests. | Execution requires super — approval and execution are deliberately separated so one operator cannot both authorise and carry out a deletion. | `POST /api/v1/admin/erasure` |
| `keys:write` | destructive | enforced | — | Create, rotate and revoke admin API keys. | Minting an admin key is a takeover vector — every use is written to the admin audit log. | `DELETE /api/v1/admin/keys/:id`, `POST /api/v1/admin/keys`, `POST /api/v1/admin/keys/:id/rotate` |
| `super` | destructive | enforced | `support:read`, `support:write`, `read`, `write`, `wallet`, `agent`, `agent:read`, `agent:write`, `sessions:read`, `sessions:write`, `reserves:read`, `metrics:read`, `dlq:read`, `dlq:write`, `backfill:write`, `keys:read`, `keys:write`, `fiat:read`, `fiat:write`, `outbox:read`, `outbox:write`, `approvals:write`, `referrals:read`, `referrals:write`, `erasure:write`, `erasure:read`, `messages:read`, `messages:write`, `treasury:read`, `treasury:write`, `risk-metadata:read`, `risk-metadata:write`, `admin:impersonate`, `super` | Every admin scope, including erasure execution. | Held by as few keys as possible: it is a superset of every destructive scope in this catalogue. | `GET /api/v1/admin/audit/prove`, `GET /api/v1/admin/audit/verify`, `POST /api/v1/admin/erasure/requests/:id/approve`, `POST /api/v1/admin/erasure/requests/:id/execute`, `POST /api/v1/admin/erasure/requests/:id/reject` |
| `admin:impersonate` | operational | declared-only | — | View the API as a user, read-only. | No route requires this scope today — the impersonation controller exists but is not yet exposed through the admin API. Every impersonated request is attributed to the operator in the admin audit log. | — |
| `agent` | operational | declared-only | `agent:read`, `agent:write` | Agent loop operations. | — | — |
| `agent:write` | operational | enforced | — | Change agent circuit breakers. | — | `POST /api/v1/admin/agent/breakers`, `POST /api/v1/admin/agent/breakers/:id/reset` |
| `backfill:write` | operational | enforced | — | Run Stellar event backfills. | — | `POST /api/v1/admin/stellar/backfill` |
| `dlq:write` | operational | enforced | — | Replay, retry and resolve dead-lettered work. | — | `POST /api/v1/admin/dlq/replay`, `POST /api/v1/admin/dlq/resolve`, `POST /api/v1/admin/dlq/retry`, `POST /api/v1/admin/notifications/dlq/:id/retry` |
| `fiat:write` | operational | enforced | — | Change fiat provider failover state. | — | `POST /api/v1/admin/fiat/providers/:name/failover` |
| `messages:write` | operational | enforced | — | Retry and recover failed message deliveries. | — | `DELETE /api/v1/admin/messages/:id`, `POST /api/v1/admin/messages/:id/retry`, `POST /api/v1/admin/messages/retry-dead-letters` |
| `risk-metadata:write` | operational | declared-only | — | Review and publish protocol risk metadata. | No route requires this scope today — the risk-metadata review workflow is administered from src/config/riskMetadataAdmin.ts, not through the API. Declared so a key can be provisioned ahead of a route that needs it. | — |
<!-- END GENERATED: admin-scope-catalog -->

### Withdrawal guardrails

- `withdraw:write` requires `allowWithdrawals: true` at key creation.
Expand All @@ -55,6 +120,42 @@ New keys are **read-only by default**. Write scopes must be explicitly requested

An API key **cannot** create, rotate, or revoke other keys.

## Scope catalogue endpoint (admin auth)

| Method | Path | Description | Scope |
|--------|------|-------------|-------|
| `GET` | `/api/v1/admin/keys/scopes` | The machine-readable scope catalogue, with the endpoints each scope covers | `keys:read` |

The response carries both scope families:

```jsonc
{
"success": true,
"data": {
"user": {
"defaultReadScopes": ["portfolio:read", "transactions:read", "vault:read"],
"scopes": [
{
"scope": "withdraw:write",
"description": "Create withdrawals.",
"risk": "money",
"grants": "Submits withdrawals to the durable outbox for on-chain settlement.",
"enforcement": "enforced",
"requires": "Requires allowWithdrawals on the key AND the platform kill-switch USER_API_KEY_WITHDRAWALS_ENABLED to be on.",
"endpoints": ["POST /api/v1/withdraw"]
}
]
},
"admin": {
"scopes": [/* the same shape, for admin key scopes */]
}
}
}
```

The `endpoints` list is derived from the live route table, so a scope cannot
claim to unlock an endpoint that stopped requiring it.

## Errors

| Status | Error | Meaning |
Expand Down
2 changes: 2 additions & 0 deletions docs/DOCUMENTATION_INDEX.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Documentation Index - Issue #23: Vault Events Persistence

## Quick Navigation
Expand All @@ -22,6 +22,8 @@
- **[PRISMA_QUERY_PERFORMANCE.md](PRISMA_QUERY_PERFORMANCE.md)** - Hot webhook query plans, supporting indexes, and the query-index regression check
- **[NET_WORTH.md](NET_WORTH.md)** - Read-only external Stellar links, valuation/staleness semantics, and private scope
- **[CONSENT_AND_PRIVACY_POLICY.md](CONSENT_AND_PRIVACY_POLICY.md)** - User consent boundaries, high-risk workflow checks & auditable bypass policy (#510)
- **[RATE_LIMITING.md](RATE_LIMITING.md)** - Rate-limit tier catalogue & route→tier matrix: auth-type mapping, effective budgets, fail-open/fail-closed policy (#614)
- **[API_KEYS.md](API_KEYS.md)** - User & admin API key scope catalogue: descriptions, risk levels, enforcement status, and the endpoints each scope covers (#615)

### For DevOps/Deployment

Expand Down
Loading
Loading