Organization membership
Govern the projected organization membership registry that replaces install-time authorization rows after bootstrap.
Organization membership
Airlift roles (viewer, operator, approver, validator, admin, and the narrow
automation role for service accounts) are granted through a governed organization
membership registry: a projected admin registry built from the durable event stream,
one projection per organization.
Install-time configuration (AIRLIFT_AUTHORIZATION_JSON) seeds the registry exactly
once. At startup the console adopts the configured rows for each active organization
into the governed stream through the platform-installer system principal, using a
digest-bound idempotency key so a restart replays one logical command. From the moment
any governed row exists for an organization, the environment directory is never
consulted again for that organization — not even as a fallback. If the governed
reader exists and returns no rows, every principal is denied (fail closed), and
that organization is marked unavailable when it has no bootstrap members to adopt;
other correctly initialized organizations remain healthy.
After bootstrap, membership changes are governed mutations, not configuration edits.
The action contract
airlift.organization_membership_set uses a revisioned version-2 request and applies one
atomic batch of changes:
{
"submissionId": "team-access-7c9f6d",
"changes": [
{
"principal": "teammate@example.com",
"principalType": "natural_person",
"role": "operator",
"status": "active",
"expectedRevision": "absent"
},
{
"principal": "departed-admin@example.com",
"principalType": "natural_person",
"role": "admin",
"status": "revoked",
"expectedRevision": "event:01JEXAMPLE"
},
{
"principal": "onboarding-runner",
"principalType": "service_account",
"role": "automation",
"status": "active",
"expectedRevision": "absent"
}
]
}- Tenant-free payload. The strict schema accepts
submissionIdpluschanges(1–100 entries). Actor and organization are never payload fields; the authenticated API derives the organization from the caller's single resolved membership. - Typed principals. A
natural_personholdsviewer,operator,approver,validator, oradmin. Aservice_accountholds onlyautomation. That role covers bounded operational recording and execution across assessment, inventory, planning, transfer, validation, discrepancy, observation, and evidence lanes; it never carries approval, waiver, certificate, policy, membership, or cutover-execution authority.statusisactiveorrevoked. - Permission. The action requires
airlift:membership:manage, granted to theadminrole only. Theautomationrole is excluded, and no agent, worker, or evidence producer is admitted. The platform installer is admitted only for the startup bootstrap path. - Last-admin protection. The handler projects the resulting registry and fails
the whole batch when it would leave zero active natural-person admins. Grants and
revocations in one batch are evaluated against the projected result, never per row.
The API returns
last_admin_conflictwith HTTP 409. - Optimistic concurrency. Every row carries
expectedRevision(absentfor a new principal or the projectedevent:...revision for an update). A stale row rejects the whole batch asmembership_conflictwith HTTP 409; no partial event is appended. - Governed revocation. A revoked row stops authorizing as soon as the projection updates, and Platform Host re-authorizes at execution time, so a durable invocation cannot retain a revoked grant.
- Idempotency.
submissionId, actor, tenant, canonical ordered batch, and expected revisions bind one logical attempt. An exact transport retry collapses to the original invocation; a new user gesture uses a new submission ID.
Canonical automation permissions
This block is machine-checked against AIRLIFT_ROLE_PERMISSIONS.automation:
[
"airlift:read",
"airlift:assessment:record",
"airlift:assessment:start",
"airlift:assessment:export",
"airlift:object:register",
"airlift:object:exclude",
"airlift:dependency:record",
"airlift:plan:generate",
"airlift:engagement:create",
"airlift:engagement:update",
"airlift:engagement:activate",
"airlift:engagement:freeze",
"airlift:connection:register",
"airlift:connection:verify",
"airlift:connection:retire",
"airlift:artifact:register",
"airlift:residue:create",
"airlift:residue:estimate",
"airlift:residue:resolve",
"airlift:transfer:start",
"airlift:transfer:checkpoint",
"airlift:transfer:reconcile",
"airlift:transfer:failure",
"airlift:deployment:observe",
"airlift:deployment:reconcile",
"airlift:validation:execute",
"airlift:discrepancy:create",
"airlift:discrepancy:triage",
"airlift:discrepancy:verify",
"airlift:cutover:rehearsal",
"airlift:cutover:observe",
"airlift:cutover:incident",
"airlift:modernization:recommend",
"airlift:modernization:evidence",
"airlift:value:record",
"airlift:connection:diagnose",
"airlift:access:observe",
"airlift:evaluator:observe"
]CLI
fa organization membership set \
--file membership-changes.json \
--idempotency-key membership-grant-2026-08
fa organization membership set \
--file membership-changes.json \
--idempotency-key membership-grant-2026-08 \
--jsonThe command is a transport client for the action above: it forwards the file as the
action params unchanged and lets the server schema reject anything outside the
governed shape. Use a stable, non-secret idempotency prefix; the CLI binds it to the
canonical request digest. Exit codes follow the shared remote contract (0 applied,
2 usage or invalid request, 3 unauthenticated or forbidden, 5 blocked, 6
unavailable).
There is deliberately no fa organization membership list: the authenticated read
surface has no membership resource, and the CLI never opens a second read path. Read
the projected registry in the App instead.
Team access page (App)
The Team access page of the Airlift App is the admin surface for this registry. It renders the projected memberships for the current organization — principal, principal type, role, and an active/revoked status pill — sorted by principal, with an explicit empty state when no governed rows have been adopted yet.
Who can manage members, and what a denied state means:
- Natural-person tenant admins only. The page requires
airlift:membership:managebefore listing any principal or role, and the governed action independently re-checks it at execution time. Ordinary members receive no tenant roster. A service account — including one holding theautomationrole — is denied with "Only a signed-in tenant administrator can manage team access." Databricks workspace identity can deny access to the App, but it never grants an Airlift organization role. - One change per submission. The form takes the exact Databricks UC principal or
service-principal ID, a principal type, a role, and a status (
activeorrevoked). Choosing Service account pins the role toautomation; people holdviewer,operator,approver,validator, oradmin. Principal strings with whitespace, control characters, or asecret:/token:/credential:prefix are rejected before the action is invoked — credentials are never membership input. - Governed mutation, never a store write. The submission invokes
airlift.organization_membership_setwith a content-digest idempotency key, so an exact resubmission collapses to the original invocation. A governed denial (including last-admin protection) is returned verbatim as the form message; nothing is applied partially. - Recovery. If a batch fails because it would leave zero active natural-person
admins, grant
adminto another person first and revoke the departing admin in a second submission. For any other denial, correct the named input and resubmit — the digest-bound key makes the retry safe. See When something fails for the shared blocked-state model.
After bootstrap, this page and fa organization membership set are the only membership
surfaces: install-time configuration (AIRLIFT_AUTHORIZATION_JSON) is never consulted
again once any governed row exists for the organization, not even as a fallback.
Failure recovery
- Zero-active-admin prevention. A batch that would demote or revoke the last
active admin fails whole. Grant
adminto another natural person first, then revoke the departing admin in a second mutation with a fresh idempotency prefix. - Bootstrap failure. A missing or divergent bootstrap directory marks only that
organization unavailable (
bootstrap_conflictfor divergent input); healthy tenants continue serving. Correct the rows and restart under the same digest-bound attempt. - Revocation propagation. Protected reads refresh the registry and tenant at request time; mutations refresh once for admission and again immediately before effect. Cross-replica revocation therefore denies the next request without waiting for polling.
Honest support boundary
Organization membership governs Airlift application roles only. It never grants or
revokes Databricks workspace entitlements, App CAN_USE permission, Unity Catalog
privileges, or Runway/Experiments authority — those remain in their owning systems.
Membership rows are install-time seed input until adoption; after adoption, the
projected registry is the only grant source and this page, the action catalog, and the
repository contract tests are the authoritative contract.