Scoped PAT management: permission strings, plaintext-once creation, IP restrictions, expiry, and revocation via gRPC to the Auth service — never direct DB access.
Milestone 4.A.4 — Token Management API
Status: Done Goal: Track A — Management API Server Phase: 4 — Operator Platform & Multi-Provider Estimated effort: 2 days Track: Track A — Management API Server
Why This Milestone Exists
Operators need a self-service surface for issuing, scoping, and revoking Personal Access Tokens (PATs). The API documentation already specifies the schema and permission model; this milestone wires those specifications into the management API. One critical invariant: the API server should never touch the token hash table directly — all revocation goes through AuthService.RevokeToken gRPC, mirroring the proxy's own "gRPC to auth, not direct DB" rule.
Non-Goals
- OAuth/SSO session tokens (Phase 4.5)
- Token rotation automation (future)
- Batch token issuance
Orientation (indicative)
Named paths, package layouts, libraries, schemas, env vars, and commands anywhere on this page are rough sketches for orientation — inspiration and a baseline, not a required change list.
During implementation, expect to:
- open the live tree and follow existing patterns before inventing new ones
- research current constraints (latency, tenancy, deploy shape, libraries) more deeply than this page can
- advance the design beyond the sketch where measurement or code reality says so
- land work in different filenames, merged packages, deferred docs, or new surfaces when the situation calls for it
Prefer outcomes over matching any particular file tree or command sequence.
Areas that may be involved (situational — not a checklist):
- Management API
- Auth service
- Database schema / migrations
Suggested naming (provisional)
Rename freely to match the change that actually lands.
- Branch:
feature/m4-a-4-token-management - PR title:
feat(api): PAT management API with scoped permissions and gRPC revocation (m4.A.4)
Endpoints (illustrative)
Route shapes below are a planning sketch — names, nesting, and payloads may change during implementation.
GET /v1/tokens → CursorPage[TokenResponse] (org-scoped; plaintext never returned in list)
POST /v1/tokens → TokenCreateResponse (201; includes plaintext token — shown exactly once)
GET /v1/tokens/{token_id} → TokenResponse (no plaintext; via ListTokens filter)
DELETE /v1/tokens/{token_id} → 204 (revocation via AuthService.RevokeToken gRPC)PATCH deferred.
Token Schema
class TokenCreateRequest(BaseModel):
name: str # human-readable label
permissions: list[str] # ADR-0009 strings
allowed_ips: list[str] | None = None # CIDR syntax only; not persisted
expires_at: datetime | None = None
agent_id: UUID | None = None
class TokenCreateResponse(BaseModel):
id: UUID
name: str
token: str # plaintext — shown once
permissions: list[str]
prefix: str
expires_at: datetime | None
created_at: datetime
class TokenResponse(BaseModel):
id: UUID
name: str
permissions: list[str]
prefix: str
expires_at: datetime | None
created_at: datetime
revoked_at: datetime | None
is_revoked: boolPermission Strings
Scoped permission strings map to the existing 64-bit permission bitmap (ADR-0009):
| Permission String | Bitmap Bit | Description |
|---|---|---|
memory:read | bit 0 | Read memories |
memory:write | bit 1 | Create/update memories |
memory:delete | bit 2 | Delete memories |
session:create | bit 16 | Start proxy sessions |
admin:user_manage | bit 32 | Manage users |
admin:org_manage | bit 35 | Org settings (alias OrgSettingsWrite) |
admin:token_create | bit 36 | Issue tokens |
admin:token_revoke | bit 37 | Revoke tokens |
Full catalog is in API_DOCUMENTATION.md Permissions Reference and app/permissions_codec.py.
Business Rules
- Plaintext shown exactly once: on
POST, Auth generates/hashes (Argon2id) and returns plaintext once. - Revocation via gRPC only:
DELETEcallsAuthService.RevokeToken— never direct token-table writes. - IP restriction validation:
allowed_ipsCIDRs validated syntactically; not stored (follow-up for persistence). - Permission scope enforcement: caller cannot grant permissions they do not hold →
403 PERMISSION_ELEVATION_DENIED.
Success signals
Outcome-oriented signals that the milestone is in good shape. Exact filenames, package layouts, and commands may differ from any sketches above.
- Plaintext token present in create response, absent in all list/get responses
- Token revocation reflected in proxy auth-cache within 5s (SEC7_2b; RevokeToken gRPC path)
- Permission elevation attempt returns 403
PERMISSION_ELEVATION_DENIED - Invalid CIDR in
allowed_ipsreturnsVALIDATION_ERROR(HTTP 400 per apierror) -
TestAPI_ISO_TOKEN_*: org A cannot read/revoke org B's tokens (404)
Prerequisites
- M4.A.1 skeleton merged
AuthService.RevokeTokengRPC available
Last updated on