FabricFabricAirlift
Reference

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 submissionId plus changes (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_person holds viewer, operator, approver, validator, or admin. A service_account holds only automation. 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. status is active or revoked.
  • Permission. The action requires airlift:membership:manage, granted to the admin role only. The automation role 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_conflict with HTTP 409.
  • Optimistic concurrency. Every row carries expectedRevision (absent for a new principal or the projected event:... revision for an update). A stale row rejects the whole batch as membership_conflict with 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 \
  --json

The 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:manage before 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 the automation role — 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 (active or revoked). Choosing Service account pins the role to automation; people hold viewer, operator, approver, validator, or admin. Principal strings with whitespace, control characters, or a secret:/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_set with 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 admin to 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 admin to 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_conflict for 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.

On this page