Phase 4 operator platform

Full CRUD for organizations and users — the root of the tenancy tree everything else depends on. This resource was left as a stub in the original roadmap; this milestone specifies and implements it fully.

Milestone 4.A.2 — Organization & User Management API

Status: Planned
Goal: Track A — Management API Server
Phase: 4 — Operator Platform & Multi-Provider
Estimated effort: 3 days
Track: Track A — Management API Server
ADR required: ADR-0045 — Organization lifecycle and cascading suspension


Why This Milestone Exists

This resource was left as a stub in the original roadmap ("detailed milestone specification is not yet published") despite being the root of the tenancy tree everything else depends on. This redesign specifies it fully: org lifecycle (active/suspended/deleted), user invitation, role enforcement, and all cross-tenant isolation tests.


Non-Goals

  • Org creation (that belongs in a separate billing-integrated onboarding flow)
  • OAuth / SSO for user login (Phase 4.5)
  • Bulk user import

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-2-org-user-management
  • PR title: feat(api): organization and user management API with suspension propagation (m4.A.2)

Endpoints (illustrative)

Route shapes below are a planning sketch — names, nesting, and payloads may change during implementation.

GET /v1/organizations/{id} → OrganizationResponse
PATCH /v1/organizations/{id} → OrganizationResponse (name, billing_email, settings)
POST /v1/organizations/{id}/suspend → OrganizationResponse (status: suspended — blocks all proxy traffic)
DELETE /v1/organizations/{id} → 202 Accepted (async GDPR full-deletion job, cascade pattern)

GET /v1/users → CursorPage[UserResponse] (org-scoped)
POST /v1/users → UserResponse (201) — invites a user, sends email via async job
GET /v1/users/{id} → UserResponse
PATCH /v1/users/{id} → UserResponse (role, name)
DELETE /v1/users/{id} → 204 (soft delete, revokes their PATs)

Business Rules

  • POST /v1/organizations (creation) is not exposed here — orgs are created via a separate signup/provisioning flow, keeping this API "manage what exists" only.
  • Suspending an org must propagate to the proxy's auth cache within the same 5-second bound already guaranteed for token revocation — reuse the existing Redis pub/sub revocation-propagation channel (ADR-0029) rather than inventing a second mechanism.
  • Role model: owner (one per org, cannot be demoted below the last owner), admin, member, read_only — mapped onto the existing 64-bit permission bitmap (ADR-0009), not a parallel RBAC system.
  • User deletion revokes all PATs owned by that user (calls AuthService.RevokeToken in a loop; add RevokeAllTokensForUser batch RPC only if p95 exceeds 200ms with realistic token counts).

ADR-0045 — Organization Lifecycle and Cascading Suspension

Document as a new ADR:

  • Suspension vs. deletion: suspension is reversible and blocks proxy traffic immediately (checked in the same auth-cache path as revocation); deletion is a one-way async GDPR cascade.
  • Why suspension reuses the token-revocation Redis channel: one propagation mechanism to reason about, tested once (ADR-0029), rather than two.
  • Owner-cannot-be-demoted-below-one invariant: enforced at the API layer with a row lock (SELECT ... FOR UPDATE on the owners count) rather than only a DB constraint (constraint alone can't express "last owner" without a trigger, which is harder to test/debug than a locked API-layer check).

Success signals

Outcome-oriented signals that the milestone is in good shape. Exact filenames, package layouts, and commands may differ from any sketches above.

  • Cross-tenant isolation test through _08: cross-org user/org access all return 404 (not 403 — anti-enumeration, matching Phase 1's convention)
  • Org suspension observed in proxy auth-cache within 5s in an integration test (start a chat request loop, suspend, assert 503/403 within bound)
  • Last-owner demotion attempt returns 409 LAST_OWNER_PROTECTED
  • User deletion triggers revocation of all the user's PATs (verified by integration test checking auth cache)

Prerequisites

  • M4.A.1 skeleton merged
  • AuthService.RevokeToken RPC available
Edit on GitHub

Last updated on

On this page

0%