Filed by the triage seat as the framework leg of objectstack-ai/cloud#1509 (measured p0: on a walled multi-tenant SaaS with OS_TENANCY_POSTURE=isolated + OS_AUTH_MEMBERSHIP_POLICY=invite-only, the FIRST self-registrant received platform_admin + isPlatformAdmin: true and was merged into the deployment's Default Organization — full curl evidence on the cloud card).
Maintainer ruling (2026-08-23, live PM chat, verbatim: 「1509 选择 env 指定 owner 邮箱」— option A with the env-owner-email bootstrap):
- Under walled postures (
isolated / group), the "first registrant becomes owner/platform admin" path is REMOVED. Platform admin is granted only to the account matching an env-declared owner email (exact variable name is the implementer's to propose in the PR — follow existing OS_* naming conventions and document it in the composed template).
- Under walled +
invite-only, NO self-registrant is auto-merged into the Default Organization — it belongs to the operator only. (Second registrant behavior measured today — 0 orgs, routed to create-workspace — is the correct shape for ALL self-registrants including the first.)
- Single-org posture keeps "first user is owner" — that default is ruled reasonable and out of scope here.
- Fail-closed clause: walled posture + no owner email declared ⇒ refuse startup with a message naming the variable (matching the existing walled "undeclared knob ⇒ refuse" style), rather than silently reverting to first-registrant elevation.
Establish-first
Locate the elevation site(s) (plugin-auth bootstrap / first-user logic) and the Default-Org auto-join site before writing; report the map in the PR. Pin both directions: (a) walled + declared owner email ⇒ only that account elevates, self-signups get positions: ["user"] and no org; (b) single-org posture unchanged.
Security surface: dispatch per lane discipline at judgment tier; refusal cases carry code+status.
Downstream: objectstack-ai/cloud#1509 is the acceptance leg (blocked on this card) — composed template docs (composed.env.example wording) and the live re-verification happen there after the pin bump.
Refs: objectstack-ai/cloud#1509 (measurement + ruling record).
Filed by the triage seat as the framework leg of objectstack-ai/cloud#1509 (measured p0: on a walled multi-tenant SaaS with
OS_TENANCY_POSTURE=isolated+OS_AUTH_MEMBERSHIP_POLICY=invite-only, the FIRST self-registrant receivedplatform_admin+isPlatformAdmin: trueand was merged into the deployment's Default Organization — full curl evidence on the cloud card).Maintainer ruling (2026-08-23, live PM chat, verbatim: 「1509 选择 env 指定 owner 邮箱」— option A with the env-owner-email bootstrap):
isolated/group), the "first registrant becomes owner/platform admin" path is REMOVED. Platform admin is granted only to the account matching an env-declared owner email (exact variable name is the implementer's to propose in the PR — follow existingOS_*naming conventions and document it in the composed template).invite-only, NO self-registrant is auto-merged into the Default Organization — it belongs to the operator only. (Second registrant behavior measured today — 0 orgs, routed to create-workspace — is the correct shape for ALL self-registrants including the first.)Establish-first
Locate the elevation site(s) (plugin-auth bootstrap / first-user logic) and the Default-Org auto-join site before writing; report the map in the PR. Pin both directions: (a) walled + declared owner email ⇒ only that account elevates, self-signups get
positions: ["user"]and no org; (b) single-org posture unchanged.Security surface: dispatch per lane discipline at judgment tier; refusal cases carry
code+status.Downstream: objectstack-ai/cloud#1509 is the acceptance leg (blocked on this card) — composed template docs (
composed.env.examplewording) and the live re-verification happen there after the pin bump.Refs: objectstack-ai/cloud#1509 (measurement + ruling record).