Self-create organization for signed-in users

Task 5068 · Done · Coder · created · updated

## Epic ask (verbatim) > "Allow user to be a member of several orgs, including Operator. At any given moment user acts on behalf of the currently selected org, no permissions mix or leak. User can switch between orgs by clicking on his icon and selecting a switch icon next to the org. Same model as in Stripe and other multitenant systems" ## Requirement (PRD, verbatim) **R6 — Self-create an org.** A signed-in user can create a new org from the account menu. It goes through exactly today's self-service sign-up admission: the org lands `pending`, an operator approves it (ADR-0118 gates), and the creator is its Owner. Until approval it shows in their list as pending and cannot be acted as. ## Acceptance (PRD AC10, verbatim) "'Create organization' from the menu creates an org in the `pending` state, the same as self-service sign-up, with U as Owner. It shows as pending and cannot be switched to until an operator approves it; after approval it can be switched to." (This child: the API half; the menu entry is the web child.) ## Approach (architecture §3.7, §5.6) `POST /api/orgs/self-create` (any signed-in user). Body = today's sign-up `Application` fields minus the person. Reuse `signup.create_signup` (`signup.py:508`) with a new `identity` parameter: creates the pending org + `org_signups` row + an owner `admin_users` row for the caller's EXISTING identity (Org Owner binding, `is_superadmin=false`) + clickwrap — the same ADR-0118 admission; the email-signup claim path is skipped for the identity path. Response `201 {"org_id","state":"pending_approval"}`. Cap: one pending self-created org per identity — a second request while one is pending → `409 signup_pending` (enforce by query; add NO schema migration in this child). The new membership appears in `GET /api/auth/me` `memberships` with `state: "pending_approval"`. Security surface: auth/authz — under the security-surface gate. Out of scope: No web UI (the web child builds the dialog). No switch endpoint. Do NOT add a schema migration. Epic out-of-scope (PRD §5, verbatim): "No change to ADR-0118 admission gates or the approval flow, and no change to the node/org immutability or the placement org floor." "No change to billing/plans/seat pricing semantics beyond each membership being a member of that org as today." "Do not weaken the read/write seams." Done means: - API test: signed-in U calls self-create → 201, org lifecycle `pending`, `org_signups` row exists, U's new row in that org holds the Org Owner binding and `is_superadmin` false, U's original membership untouched; `me.memberships` lists the new org as `pending_approval`. - API test: second self-create while one is pending → 409 `signup_pending`. - API test: after the operator approve flow, the membership state becomes `active`. - Identity path never runs the email-signup claim — test. - AC17 — `./dc check full` green, including the tenancy bypass/scope censuses and the authz policy suite. Required checks: gate

Outcome

Built POST /api/orgs/self-create: pending org + new non-superadmin Owner membership via create_signup identity path, 1-pending cap (409). Full gate green; operator-approved; merged #4503. Deferred: verified-email requirement (M1), reject cooldown.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Awaiting response
  • Awaiting response
  • Runner resumed
  • Runner ended (done)