Console Admin RBAC Architecture
Audience: engineers and operators working with the Authority-backed Console admin surface, and anyone reasoning about Stella Ops scopes and role bundles. Source of truth: the canonical scope catalog is
StellaOpsScopes.cs; the seeded role bundles areConsoleAdminEndpointExtensions.GetDefaultRoles(). Where this doc and the code disagree, the code wins — several rows below are explicitly marked proposed vs seeded.
1. Purpose
- Provide a unified, Authority-backed admin surface for tenants, users, roles, clients, tokens, and audit.
- Expose the same capabilities to UI and CLI while preserving offline-first operation.
- Normalize scope and role bundles, including Scanner producer roles, for consistent RBAC across modules.
- Align tenant assignment and selection behavior with ADR-002 — multi-tenant same-API-key selection.
2. Scope
- Authority admin APIs and data model used by the Console Admin workspace.
- Role and scope taxonomy, including scanner roles.
- Audit, fresh-auth, and offline export/import workflow.
- UI integration contract (routes, scopes, and API paths).
Non-goals:
- Replacing external IdP user lifecycle workflows (SAML/OIDC remains primary for enterprise identity).
- Exposing privileged mTLS-only admin endpoints directly to the browser.
3. Core Architecture
3.1 Authority admin tiers
- /console/admin: the implemented admin surface. Every endpoint requires the
ui.adminscope (enforced at the route-group level viaRequireStellaOpsScopes(StellaOpsScopes.UiAdmin)), plus the per-resourceauthority:*scope on each operation. Mutating operations additionally callRequireFreshAuth(). A tenant header filter resolves the active tenant. Verified insrc/Authority/StellaOps.Authority/StellaOps.Authority/Console/Admin/ConsoleAdminEndpointExtensions.cs. - /api/admin: a legacy alias group that mirrors the user-management endpoints (
/api/admin/users) with the sameui.admin+authority:users.*scope requirements and tenant/fresh-auth filters. Same data model and audit log as/console/admin.
NOTE (doc-vs-code): an earlier draft described a separate
/admintier gated by mTLS plus anauthority.adminscope. Neither exists in source: there is noauthority.adminscope constant inStellaOpsScopes.cs, and the Console admin endpoints are not bound to an mTLS-only route group. Authority does support mutual-TLS sender-constrained tokens generally (AuthorityClientCertificateValidator), but the Console admin tiers above authorize on bearer scopes, not client certificates. The/api/adminalias is the only second tier, and it uses the same UI scopes — not mTLS.
3.2 Entities and ownership
Authority remains the source of truth for:
- Tenant: id, display name, status, isolation mode, default roles.
- Installation: installation id, tenant binding, bootstrap metadata.
- Role: id, display name, scopes[], audiences[], flags (interactive-only, requires fresh-auth).
- User: subject, status, display name, tenant assignments, roles per tenant.
- Client: client id, grant types, auth method, allowed scopes, audiences, tenant hint.
- Token record: access/refresh/device metadata, revocation status.
- Audit events: immutable admin and auth events.
3.3 Fresh-auth
High-risk mutating operations are wired to a fresh-auth gate via RequireFreshAuth() in code:
- Tenant create / update / suspend / resume (and operator-compliance mutations)
- Token revocation (bulk or admin)
- Role create / edit
- Client create / update / secret rotation
- User create / update / enable / disable
- Branding apply (PUT)
Authority enforces these operations against the authenticated principal’s auth_time claim. The claim must be a Unix timestamp no more than five minutes old; timestamps more than one minute in the future are also rejected. Missing, invalid, stale, or excessively future authentication returns HTTP 401 with code fresh_auth_required. Read-only admin routes do not apply this filter, so an older session may inspect state but must reauthenticate before changing it. The general Console admin and process-global identity-provider admin surfaces use the same five-minute/one-minute contract.
4. Scope and Role Taxonomy
4.1 Console admin scopes
Admin scopes (Authority-managed, all defined in StellaOpsScopes.cs):
authority:tenants.read,authority:tenants.writeauthority:users.read,authority:users.writeauthority:roles.read,authority:roles.writeauthority:clients.read,authority:clients.writeauthority:tokens.read,authority:tokens.revokeauthority.audit.read— note the dot beforeaudit. The audit-read scope is the only Authority admin scope that uses dot separators (authority.audit.read, alongside the olderauthority.users.manage,authority.clients.manage,authority.revocation.read); all the tenant/user/role/client/token/branding scopes use the colon-prefixedauthority:<resource>.<verb>form. There is noauthority:audit.read(colon-form) scope.authority:branding.read,authority:branding.writeauthority:idp.read,authority:idp.writeui.admin(console access for admin views)
The console Identity Providers route requires ui.admin together with authority:idp.read. Its create, update, delete, enable/disable, test, and apply controls require authority:idp.write and use the same fresh-authentication contract as the Authority mutation endpoints. Legacy platform.idp.read and platform.idp.admin claims do not authorize this Authority-owned surface.
4.2 Scanner scope and role bundles
Scanner scopes and role bundles (all five scopes exist in StellaOpsScopes.cs; the four scanner roles are seeded built-ins in GetDefaultRoles()):
- Scopes:
scanner:read,scanner:scan,scanner:export,scanner.signed-sbom-material.write,scanner:write - Role bundles (scopes here match the code exactly):
role/scanner-viewer->scanner:readrole/scanner-operator->scanner:read,scanner:scan,scanner:exportrole/scanner-signed-sbom-writer->scanner.signed-sbom-material.writerole/scanner-admin->scanner:read,scanner:scan,scanner:export,scanner:write
Doc-vs-code (catalog table below): §4.3 augments these scanner bundles with
findings:readandaoc:verify. The seededGetDefaultRoles()catalog does not include those extra scopes on the scanner roles — the built-ins carry only thescanner:*scopes listed here. The §4.3 entries withfindings:read/aoc:verifyare proposed, not the seeded reality.
Compatibility:
- The signed-SBOM material endpoint uses the dedicated
scanner.signed-sbom-material.writescope, which is preserved separately fromscanner:write.
4.3 Module role bundle catalog
Role bundles are grouped by module and map to existing Authority scopes unless noted.
Seeded vs. proposed (doc-vs-code, important). The only role bundles actually seeded as built-ins by Authority today are the ten returned by
ConsoleAdminEndpointExtensions.GetDefaultRoles():role/console-viewer,role/console-admin,role/executive-assurance-viewer,role/release-operations-steward,role/security-risk-manager,role/service-release-contributor,role/scanner-viewer,role/scanner-operator,role/scanner-signed-sbom-writer,role/scanner-admin. Every other row in the module table below (Policy, Concelier, Excititor, Notify, Scheduler, Orchestrator, Graph, Vuln Explorer, Export Center, Advisory AI, Signals, Evidence Locker, Observability, Issuer Directory, Task Packs, Airgap, Exceptions, Attestor, Signer, SBOM, Release, Zastava, and theoperator/auditorOperator Verify rows) is a proposed/reference bundle: the underlying scopes generally exist inStellaOpsScopes.cs, but the role itself is not seeded as a built-in. Operators can recreate any of these as custom roles viaPOST /console/admin/roles(which accepts any catalog scope and auto-creates the permission rows). The role IDs marked(new)in the Scheduler rows refer to a no-longer-accurate “scope is new” annotation — see the Missing-scopes note in this section. Where a proposed row’s scopes differ from a seeded built-in (notably the Scanner rows, which the table augments withfindings:read/aoc:verifythat the seeded scanner roles do not carry), treat the seededGetDefaultRoles()definition as authoritative.
Service-contributor deployment is a scoped release-control case, not an admin case. Use role/service-release-contributor for users who can update or request updates for their own microservice images in allowed environments. That role must be paired with ABAC constraints such as serviceIds, repositoryIds, imageNamespaces, environmentIds, and maxEnvironmentType; do not grant broad release:publish, release:bypass, ui.admin, orch:operate, or policy:approve just to enable self-service updates. Authority also narrows interactive console authorization-code grants for role/service-release-contributor and legacy role/release-developer principals to the self-service allow-list, so a broad UI client request does not mint admin/publisher/approval scopes for a contributor.
Console administrators manage those attributes from Identity & Access > Users. Selecting role/service-release-contributor opens fields for service IDs, repository IDs, image namespaces, environment IDs, maximum environment type, and directUpdateAllowed. The Authority admin API persists these values under user metadata attributes, returns them in user summaries, and the standard/LDAP/AD claim enrichers can project them into issued tokens. The UI blocks save until the administrator explicitly confirms any combination of service-release contribution with broad admin, operator, bypass, publisher, or approver roles. Built-in roles returned by GetDefaultRoles() are valid user-update assignments even when an installation has no matching tenant-local role-table row; their authoritative scope bundles remain the compiled catalog. Tenant-created roles still require a persisted role row before assignment.
The customer-facing default bundles are professional functions rather than job titles:
| Customer function | Built-in role | Scope boundary |
|---|---|---|
| Executive assurance | role/executive-assurance-viewer | Read release, policy audit, findings, evidence, graph, exports, authority audit, tenant timeline, platform context, and (BAZ-3) attestor evidence-transparency / proof-chain via attest:read. |
| Release operations | role/release-operations-steward | Operate release orchestration and scans, and read the tenant timeline, without release bypass or platform admin. |
| Security risk | role/security-risk-manager | Read tenant registry image inventory plus the release and integration context used by visible dashboard, Promotions, image, custody, and audit surfaces; investigate findings and vulnerabilities; run policy simulations; read advisory/VEX context, evidence, and the tenant timeline; and (BAZ-3) read the remediation console surface via remediation:read. release:read, registry:read, and integration:read do not grant release mutation, registry administration, or integration write/operate actions, and remediation:read does not grant remediation submit/manage (admin-only). |
| Service delivery | role/service-release-contributor | Request or perform ABAC-limited service image updates and read the tenant timeline; no publish, bypass, approval, admin, or broad orchestration operate scopes. |
| Module | Role bundle | Scopes |
|---|---|---|
| Console | role/console-viewer | ui.read |
| Console | role/console-admin | ui.read, ui.admin, authority:tenants.read, authority:tenants.write, authority:users.read, authority:users.write, authority:roles.read, authority:roles.write, authority:clients.read, authority:clients.write, authority:tokens.read, authority:tokens.revoke, authority.audit.read, authority:branding.read, authority:branding.write, authority:idp.read, authority:idp.write |
| Console | role/console-superadmin (proposed; not seeded) | ui.read, ui.admin, authority:tenants.*, authority:users.*, authority:roles.*, authority:clients.*, authority:tokens.*, authority.audit.read, authority:branding.* — NB there is no distinct console-superadmin built-in; the seeded role/console-admin already carries the full set. Audit scope is dot-form authority.audit.read. |
| Operator Verify | operator | operator.verify.read, operator.verify.admin |
| Operator Verify | auditor | operator.verify.read |
| Scanner | role/scanner-viewer | scanner:read, findings:read, aoc:verify |
| Scanner | role/scanner-operator | scanner:read, scanner:scan, scanner:export, findings:read, aoc:verify |
| Scanner | role/scanner-signed-sbom-writer | scanner.signed-sbom-material.write |
| Scanner | role/scanner-admin | scanner:read, scanner:scan, scanner:export, scanner:write, findings:read, aoc:verify |
| Policy | role/policy-author | policy:read, policy:author, policy:simulate, findings:read |
| Policy | role/policy-reviewer | policy:read, policy:review, policy:simulate, findings:read |
| Policy | role/policy-approver | policy:read, policy:review, policy:approve, policy:simulate, findings:read |
| Policy | role/policy-operator | policy:read, policy:operate, policy:run, policy:activate, policy:publish, policy:promote, policy:simulate, findings:read |
| Policy | role/policy-auditor | policy:read, policy:audit, findings:read |
| Concelier | role/concelier-reader | advisory:read, aoc:verify |
| Concelier | role/concelier-ingest | advisory:ingest, advisory:read, aoc:verify |
| Concelier | role/concelier-operator | concelier.jobs.trigger, advisory:read, aoc:verify |
| Concelier | role/concelier-admin | concelier.jobs.trigger, concelier.merge, advisory:read, aoc:verify |
| Excititor | role/excititor-reader | vex:read, aoc:verify |
| Excititor | role/excititor-ingest | vex:ingest, vex:read |
| Notify | role/notify-viewer | notify.viewer |
| Notify | role/notify-operator | notify.viewer, notify.operator |
| Notify | role/notify-admin | notify.viewer, notify.operator, notify.admin |
| Scheduler | role/scheduler-viewer | scheduler:read |
| Scheduler | role/scheduler-operator | scheduler:read, scheduler:operate |
| Scheduler | role/scheduler-admin | scheduler:read, scheduler:operate, scheduler:admin |
| Orchestrator | role/orch-viewer | orch:read, findings:read |
| Orchestrator | role/orch-operator | orch:read, orch:operate, findings:read |
| Orchestrator | role/orch-admin | orch:read, orch:operate, orch:quota, orch:backfill, findings:read |
| Graph | role/graph-viewer | graph:read, graph:export |
| Graph | role/graph-operator | graph:read, graph:export, graph:simulate |
| Graph | role/graph-admin | graph:read, graph:export, graph:simulate, graph:write, graph:admin |
| Vuln Explorer | role/vuln-viewer | vuln:view, findings:read |
| Vuln Explorer | role/vuln-investigator | vuln:view, vuln:investigate, findings:read |
| Vuln Explorer | role/vuln-operator | vuln:view, vuln:investigate, vuln:operate, findings:read |
| Vuln Explorer | role/vuln-auditor | vuln:view, vuln:audit, findings:read |
| Export Center | role/export-viewer | export.viewer |
| Export Center | role/export-operator | export.viewer, export.operator |
| Export Center | role/export-admin | export.viewer, export.operator, export.admin |
| Advisory AI | role/advisory-ai-viewer | advisory-ai:view, aoc:verify |
| Advisory AI | role/advisory-ai-operator | advisory-ai:view, advisory-ai:operate, aoc:verify |
| Advisory AI | role/advisory-ai-admin | advisory-ai:view, advisory-ai:operate, advisory-ai:admin, aoc:verify |
| Signals | role/signals-viewer | signals:read, aoc:verify |
| Signals | role/signals-uploader | signals:read, signals:write, aoc:verify |
| Signals | role/signals-admin | signals:read, signals:write, signals:admin, aoc:verify |
| Evidence Locker | role/evidence-reader | evidence:read |
| Evidence Locker | role/evidence-creator | evidence:read, evidence:create |
| Evidence Locker | role/evidence-legal | evidence:read, evidence:hold |
| Observability | role/observability-viewer | obs:read, timeline:read, attest:read |
| Observability | role/observability-investigator | obs:read, timeline:read, timeline:write, evidence:read, evidence:create, attest:read |
| Observability | role/observability-incident-commander | obs:read, obs:incident, timeline:read, timeline:write, evidence:read, evidence:create, attest:read |
| Issuer Directory | role/issuer-directory-viewer | issuer-directory:read |
| Issuer Directory | role/issuer-directory-operator | issuer-directory:read, issuer-directory:write |
| Issuer Directory | role/issuer-directory-admin | issuer-directory:read, issuer-directory:write, issuer-directory:admin |
| Task Packs | role/packs-viewer | packs.read |
| Task Packs | role/packs-operator | packs.read, packs.run |
| Task Packs | role/packs-publisher | packs.read, packs.write |
| Task Packs | role/packs-approver | packs.read, packs.approve |
| Airgap | role/airgap-viewer | airgap:status:read |
| Airgap | role/airgap-operator | airgap:status:read, airgap:import |
| Airgap | role/airgap-admin | airgap:status:read, airgap:import, airgap:seal |
| Exceptions | role/exceptions-viewer | exceptions:read |
| Exceptions | role/exceptions-approver | exceptions:read, exceptions:approve |
| Exceptions | role/exceptions-editor | exceptions:read, exceptions:write |
| Attestor | role/attestor-viewer | attest:read, aoc:verify |
| Attestor | role/attestor-operator | attest:read, attest:create, aoc:verify |
| Attestor | role/attestor-admin | attest:read, attest:create, attest:admin, aoc:verify |
| Signer | role/signer-viewer | signer:read, aoc:verify |
| Signer | role/signer-operator | signer:read, signer:sign, aoc:verify |
| Signer | role/signer-admin | signer:read, signer:sign, signer:rotate, signer:admin, aoc:verify |
| SBOM | role/sbom-viewer | sbom:read, aoc:verify |
| SBOM | role/sbom-creator | sbom:read, sbom:write, aoc:verify |
| SBOM | role/sbom-attestor | sbom:read, sbom:write, sbom:attest, attest:create, aoc:verify |
| Release | role/release-viewer | release:read, policy:read, findings:read |
| Release | role/service-release-contributor | ui.read, ui.preferences.read, ui.preferences.write, release:read, release:write, orch:read, scheduler:read, scanner:read, scanner:scan, sbom:read, policy:read, findings:read, evidence:read, vuln:view, timeline:read, platform.context.read |
| Release | role/release-developer | Legacy alias only; migrate users to role/service-release-contributor. |
| Release | role/release-manager | release:read, release:write, policy:read, findings:read |
| Release | role/release-publisher | release:read, release:write, release:publish, policy:read, findings:read |
| Release | role/release-admin | release:read, release:write, release:publish, release:bypass, policy:read, findings:read |
| Zastava | role/zastava-viewer | zastava:read |
| Zastava | role/zastava-operator | zastava:read, zastava:trigger |
| Zastava | role/zastava-admin | zastava:read, zastava:trigger, zastava:admin |
Scope availability (reconciled against StellaOpsScopes.cs):
This subsection previously tracked scopes that “must be added to Authority”. As of this reconciliation, every scope referenced in the §4.3 catalog is already defined as a const in src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOpsScopes.cs, so the catalog rows describe enforceable scopes — what remains aspirational is the seeding of most module role bundles (see the seeded-vs-proposed note at the top of §4.3), not the scopes themselves.
Confirmed present in the catalog (was previously listed as “missing/new”):
- Scanner:
scanner:read,scanner:scan,scanner:export,scanner.signed-sbom-material.write,scanner:write. The dedicated signed-SBOM material writer scope is kept distinct fromscanner:write. - Scheduler:
scheduler:read,scheduler:operate,scheduler:admin(no longer “not yet defined” — they exist). - Authority admin:
authority:tenants.read|write,authority:users.read|write,authority:roles.read|write,authority:clients.read|write,authority:tokens.read|revoke,authority:branding.read|write,authority:idp.read|write, and the dot-formauthority.audit.read. - UI admin:
ui.admin. - Attestor:
attest:read,attest:create,attest:admin. - Signer:
signer:read,signer:sign,signer:rotate,signer:admin. - SBOM:
sbom:read,sbom:write,sbom:attest. - Release:
release:read,release:write,release:publish,release:bypass. - Zastava:
zastava:read,zastava:trigger,zastava:admin. - Graph:
graph:admin(alongsidegraph:read|write|export|simulate). - Exceptions:
exceptions:read,exceptions:approve, andexceptions:writeall exist (the catalog also definesexceptions:request).
Caveat (gateway enforcement vs. scope existence): a scope being defined in
StellaOpsScopes.csonly means an issued OpTok may carry it (token issuance filters requested scopes againstauthority.clients.allowed_scopes; theauthority.permissionsRBAC catalog is not consulted at issuance — see the class remarks inStellaOpsScopes.cs). It does not by itself guarantee that every downstream service enforces the scope on its endpoints; verify per-service authorization policies against the owning module when that guarantee matters.
5. Console Admin API Surface
5.1 Tenants
GET /console/admin/tenantsPOST /console/admin/tenantsPATCH /console/admin/tenants/{tenantId}POST /console/admin/tenants/{tenantId}/suspendPOST /console/admin/tenants/{tenantId}/resumeGET /console/admin/tenants/{tenantId}/operator-compliancePUT /console/admin/tenants/{tenantId}/operator-compliancePOST /console/admin/tenants/{tenantId}/operator-compliance/approversDELETE /console/admin/tenants/{tenantId}/operator-compliance/approvers/{approverId}
Scopes: authority:tenants.read (read/list/operator-compliance read), authority:tenants.write (create/update/suspend/resume and all operator-compliance mutations).
Suspension has an additional invariant beyond scope and fresh-auth checks: the normalized target tenant must differ from the tenant resolved by TenantHeaderFilter for the authenticated request. Attempting to suspend the active context returns sealed HTTP 409 application/problem+json with code active_tenant_suspend_forbidden and performs no repository update, shared-tenant propagation, or success audit. An operator must switch to a different tenant context before suspending the former tenant.
5.2 Users
GET /console/admin/users(tenant resolved from the tenant header /TenantHeaderFilter, not atenantIdquery param)POST /console/admin/users(local users only)PATCH /console/admin/users/{userId}POST /console/admin/users/{userId}/disablePOST /console/admin/users/{userId}/enable
Legacy alias (same handlers, same scopes/filters): GET /api/admin/users and POST /api/admin/users. The 201 Location header reflects whichever prefix was called (/api/admin/users/{id} vs /console/admin/users/{id}).
Scopes: authority:users.read (list), authority:users.write (create/update/disable/enable).
POST /console/admin/users requires a non-blank password for local users. Missing or blank passwords return HTTP 400 with error=password_required and code=PASSWORD_REQUIRED so QA and clients can assert the exact contract. The created account is hashed with Argon2id and a authority.admin.users.password.set audit row is emitted alongside authority.admin.users.create. DELETE of the last enabled user in a tenant is rejected with error=last_admin_user (HTTP 400) to avoid lockout.
5.3 Roles and scopes
GET /console/admin/rolesPOST /console/admin/rolesPATCH /console/admin/roles/{roleId}POST /console/admin/roles/{roleId}/preview-impact
Scopes: authority:roles.read|write
5.4 Clients
GET /console/admin/clientsPOST /console/admin/clientsPATCH /console/admin/clients/{clientId}POST /console/admin/clients/{clientId}/rotate
Scopes: authority:clients.read|write
5.5 Tokens and audit
GET /console/admin/tokens(tenant from the tenant header)POST /console/admin/tokens/revokeGET /console/admin/audit(tenant from the tenant header)
Scopes: authority:tokens.read (list), authority:tokens.revoke (revoke), authority.audit.read (audit list — dot-form scope, not authority:audit.read).
Both token endpoints were stubs until 2026-07-21 (the list always returned [] + "implementation pending", the revoke echoed the request count without revoking — Fable audit #12). They now run against the real IAuthorityTokenStore (authority.oidc_tokens, the same store issuance persists into and TokenValidationHandlers consults):
- List returns the newest
access_token/refresh_tokenrows for the resolved tenant (authorization/device codes are excluded as one-shot handshake artifacts), withlimit(default 200, max 1000 via?limit=) and an exacttruncatedflag (computed by over-fetching one row). Display status is derived honestly:revokedfrom the persisted status,expiredfromexpires_at, elseactive; unknown persisted statuses pass through raw. - Revoke accepts
{ tokenIds?[], subject?, clientId?, reason? }— at least one selector required (400 otherwise); selectors combine additively. Subject/client match sets are resolved server-side against the full store (subject cap 1000 newest; client paged 200×25). The response{ revokedCount, alreadyRevokedCount, notFoundCount, message }counts REALvalid → revokedtransitions — never a request echo. Rows outside the caller’s resolved tenant count as not-found. Revoked tokens are rejected at/tokenand/introspectimmediately and flow into revocation bundles; access tokens already issued to JWT-validating gateways expire on their own schedule.
Deprecation:
GET /console/admin/auditcarries aSunsetheader (2027-10-19) and advertises a successor link to the unified Timeline audit store at/api/v1/audit/events?modules=authority(DeprecatedForTimeline(...)inConsoleAdminEndpointExtensions). New integrations should read audit from Timeline rather than this endpoint.
5.6 Branding
GET /console/branding?tenantId=...(public, no auth — runtime theme loading)GET /console/admin/branding(admin read, with edit metadata)PUT /console/admin/branding(admin update; fresh-auth-wired)POST /console/admin/branding/preview(admin preview before apply)
Scopes: authority:branding.read (admin GET / preview), authority:branding.write (PUT). Source: ConsoleBrandingEndpointExtensions.
6. Audit and Observability
- Every admin mutation emits
authority.admin.*events with tenant, actor, and trace id. - Audit export provides deterministic ordering and ISO-8601 timestamps.
- Token revocations emit revocation bundle update markers for downstream caches.
7. Offline-first Administration
NOT IMPLEMENTED / roadmap. This section describes a planned signed-bundle import workflow. As of this reconciliation there is no
/admin/bundles/applyendpoint (or any/admin/bundles*route) in the Authority source — searchingsrc/**returns no match. The Console admin surface (/console/admin/*,/api/admin/users) operates against a live Authority instance over bearer scopes; there is no offline change-manifest apply path in code yet. Keep the design below as the intended direction, not as shipped behavior.
- (Planned) Admin changes can be exported as signed bundles for air-gapped import.
- (Planned) The Console produces a change manifest; Authority applies it via a privileged import endpoint. The earlier
/admin/bundles/apply(mTLS) path name is not present in source and should be treated as a placeholder until the endpoint is implemented. - (Planned) UI labels changes as pending when Authority is offline.
8. UI Integration Contract
- Admin workspace routes live under
/console/admin/*. - Admin UI uses
/console/adminAPIs with DPoP; no mTLS endpoints are called by the browser. ui.adminplus specificauthority:*scopes are required to render and mutate data.
9. References
- Authority module architecture
- UI / Console module architecture
- Console operator guide (UI_GUIDE)
- Web gateway tenant RBAC contract
- Console Branding architecture — the branding endpoints and
authority:branding.*scopes referenced in §5.6. - Multi-tenant service impact ledger
- Multi-tenant flow sequences
- Multi-tenant rollout and compatibility
- Multi-tenant acceptance matrix
- ADR-002 — multi-tenant same-API-key selection
