Skip to main content

Authorization engine

Strataplane 1.0 embeds Cedar in the server for permission decisions. Its public authorization model is structured role bindings, ownership and selectors. Application-owned Cedar policies evaluate that model; Postgres holds its authoritative inputs.

The permissions per API group live in architecture.md §7, the secret delivery rule in §11, and approval requirements in spec-format.md §6. This document specifies the engine integration and its verification.

1. Scope​

Cedar runs in process, with no authorization service or runtime network dependency (Rust API, authorization semantics). It takes the permission decision; domain logic computes its inputs:

ConcernPermission decisionDomain computation
Management accessActor may perform an action within a binding's scopeIdentity, membership, ownership, selector matching
Token restrictionsThe granting binding's role is allowed by this credentialToken authentication, expiry and revocation
Secret deliveryAgent may receive this secret under the delivery ruleReferenced versions, grant match, request certificate verification
ReviewsHuman may approve this requirement, subject to author separationRequired teams, digest binding, counting valid approvals
Change submission and mergeActor may change every affected resourceCandidate tree, validation, impact and concurrency checks
Rollouts and pinsActor may perform the requested operation on its targetsCohorts, pins, guardrails and state transitions

Cedar returns a permission decision. Approval requirements, rendering and rollout state remain typed domain logic. Database tenant isolation remains independent of the engine.

2. Integration boundary​

sp-authz owns the Cedar schema, policy templates, action definitions, binding projection and decision wrapper. It accepts typed facts and returns Allow, Deny or EvaluationError. It performs no database or network I/O, and no other code calls Cedar's authorizer, so the deny-on-error rule cannot be bypassed. The domain layer constructs compound checks; sp-store loads their facts and executes authorized query plans within the caller's transaction.

authenticated request + tenant
-> transaction: load actor, credential restrictions, bindings and targets
-> typed authorization plan
-> entity projection + Cedar -> permission decision
-> parameterized SQL filter -> authorized lists and aggregates
-> domain validation and transition
-> write + audit + commit

The internal entry points are:

Entry pointContract
authorize(actor, action, resource, facts)One decision with internal policy/binding identifiers
authorize_all(actor, action, resources, facts)Every resource must be allowed; errors reject the whole operation
authorized_filter(actor, action, resource_kind, facts)Typed query predicate for list/count/group operations

These are domain entry points used by handlers, workers and agent modules. HTTP middleware alone cannot authorize a change set or rollout because its affected resources are computed after routing.

3. Entities, actions and scoped bindings​

3.1 Entity projection​

EntitySource and use
UserUser plus active tenant membership; parents are the tenant's current teams
ServiceAccountUser of kind service with its tenant membership; binding subject directly, never through a team
TeamTenant team, identified by immutable id
AgentAuthenticated agent instance; separate from human/service management principals
TenantResolved tenant identity
ScopeVirtual entity per role binding, identified by the binding id
Domain resourceHost, agent-management resource, spec object, secret, review requirement, chat or other protected object
Platform principal/resourceSeparate types and policy set for platform operations

Tenant entity identifiers include tenant id and immutable object id. User projection identifies the tenant membership, while audit attribution retains the underlying user id. Names, hostnames and team slugs are display or lookup values, not permission identities. The assistant uses its user's principal.

The loader supplies the complete entity slice required by the selected application policies, including parent groups and scopes. Schema validation does not replace checks that those referenced entities exist and belong to the resolved tenant. Only the server constructs entities and request context.

3.2 Preserve the role-scope association​

Each role_binding produces a template-linked permit with:

  • principal slot: its user, team or service account;
  • resource slot: the binding's virtual Scope entity;
  • action set: the operations explicitly granted by its role;
  • condition: that exact role is allowed by the request credential.

A resource is a member of the binding's scope precisely when the structured scope matches it. Scope memberships are computed for the request, not stored as a host-by-binding table. The binding id is retained as the linked policy id for diagnostics.

Each role has one template, generated from the action catalog: its action set is every action the role grants, and its condition names the role. The operator template for host actions:

permit (
principal in ?principal,
action in [Action::"host.read", Action::"host.operate"],
resource in ?resource
)
when { context.allowedRoles.contains("operator") };

?principal and ?resource are the two supported Cedar template slots; linking supplies entity identities, not interpolated policy text. See policy templates.

An operator binding for team A and a viewer binding for team B remain two independent grants. Neither roles nor scopes are flattened into independent sets. Sessions allow all role names through the credential restriction; tokens use their allowlist, or all names when the allowlist is absent. An empty allowlist grants nothing. Membership and bindings still determine which roles the actor actually holds.

3.3 Scope domains and selectors​

Tenant scopes cover resources in the resolved tenant. Owner scopes use the resource's authoritative owner: host ownership for agents, spec ownership for spec objects and journey evidence, and the required team for a review requirement. Selector scopes apply to hosts and host-backed agent resources. They do not confer edit rights on spec files. Compound operations check files and impacted hosts separately.

Other resources use their domain access rule from architecture §7, such as the owner of a chat. Unsupported role/scope/resource combinations confer no permission. Tenant administration actions require tenant scope. Tenant-wide data (tenant.read, and rollout.write, the right to start and act on rollouts and pins at all) is in every scope, so a binding of a role that grants it confers it whatever its scope; the targets of a rollout or pin are still authorized host by host.

The existing structured selector AST remains the authoritative language. Its in-memory evaluator produces scope membership; its SQL compiler produces the corresponding predicate. Both use the same missing-label, numeric and glob semantics (spec-format.md §2), and match host labels only. Binding selectors may not reference owner; an owner scope expresses team ownership by team id.

The SQL compiler guards numeric casts with CASE, because Postgres does not keep the order of AND; it compiles globs to anchored regular expressions without newline-sensitive flags and compares character ranges by code point (COLLATE "C"); matchLabels compiles to containment, so it can use the GIN index on host.labels.

Selectors are not translated to Cedar string expressions. In particular, Cedar's like wildcard is *, whereas Strataplane selectors also support ? and character classes. See Cedar operators.

4. Policy package and evaluation​

One application-owned action catalog defines domain action ids, principal and resource types, granting roles, applicable scope domains and supported context conditions. It generates the Cedar action schema, role templates and the metadata used by authorized query plans. Endpoint coverage checks require every protected operation to name its action or compound check. Before the handler runs, one middleware refuses a tenant request whose actor has no binding granting the operation's action anywhere (for a compound check: tenant.read), the plan of §5 being empty; the handler then decides on the resources. A filtered list stays a list of nothing. Role permissions follow architecture §7; roles have no implicit ordering.

The policy package also contains mandatory restrictions for tenant consistency, credential restrictions and operation-specific requirements. Tenant and platform packages are separate: platform administration does not make a user a tenant administrator. Agent-delivery actions are distinct from management actions, so management roles cannot grant secret delivery.

The package is embedded and versioned with the server. CI and startup run strict schema validation; an invalid package prevents readiness. Requests, context and entities are schema checked before authorization. A failure to load required facts rejects the request.

Cedar normally ignores an individual policy that errors. The wrapper returns EvaluationError if any evaluated policy reports an error, even if the Cedar decision is Allow. Only an error-free Allow authorizes an operation. See Cedar evaluation semantics.

Policy selection links only the bindings whose subject includes the actor, and the projection computes scope membership only for those bindings. Cedar evaluates every linked policy for each decision, so fleet-wide checks need this selection. Mandatory restrictions for the action are always included. The entity loader and policy selection are tested together: omitting a restriction must never be an optimization.

5. Lists, pagination and fleet-wide checks​

The structured authorization plan is shared by Cedar projection and SQL filtering. The SQL path compiles the supported application rule model; it does not attempt to translate arbitrary Cedar programs. Changes to a role template or restriction require a corresponding plan representation and equivalence tests.

For an inventory query, the plan restricts to the tenant and combines the scopes of bindings whose subjects include the actor, whose role grants the action and whose role survives the token allowlist. Owner and selector predicates retain their binding association. An empty union is false.

Filtering happens before sorting, pagination, counts and grouping. Cursor requests recompute permissions from current facts; a cursor grants no access. The filter and returned rows use one database snapshot. Bounded result pages are also checked through Cedar before serialization. A disagreement is an evaluation error for the entire response, not a reason to silently remove a row from the page. Aggregate equivalence is covered by database tests because aggregate responses do not enumerate their source rows.

Changes and rollouts require authorization of the entire affected set; for a rollout action that is every agent the rollout can reach, assigned or in a later stage. Checks run in bounded batches through Cedar or, for impact sets beyond a few thousand agents, an equivalent SQL anti-join for unauthorized targets, under a consistent snapshot. They never check only the first page or sample. Authorization is applied to the actual candidate, including its before and after ownership/targeting where those change, so a mutation cannot grant itself permission. The domain layer retains responsibility for revalidating the candidate at its state transition.

The implementation uses stable Cedar APIs. Production list filtering does not depend on the experimental partial/batched evaluation features listed in the Rust API.

6. Workflows, secret delivery and freshness​

Review requirements are computed by the approval workflow. Each required team is an independently authorized review target; any requested acting_teams must be eligible. Author separation is an explicit restriction when four eyes applies. Service accounts are ineligible for human approval actions. A permission decision does not establish that a review's digest is current or that all required teams have approved; the workflow checks both.

For secret delivery, the agent module supplies the authenticated instance, the eligible secret/version references, grant match, current quarantine/class status and assurance of this request. Cedar evaluates the delivery rule from architecture §11. Neither stored assurance nor a client's reported labels can substitute for verified request facts. Secret values are never policy inputs, and the delivery decision is taken before decryption and audited with the delivery.

The grant and minAssurance come from the Secret at the latest revision (architecture.md §11).

Facts used for a decision come from one transaction snapshot. Mutations authorize and write in the same transaction, with their domain locks or optimistic version checks. Concurrent requests follow database transaction ordering; a completed revocation is reflected in newly started authorization transactions. Authorization decisions and mutable entity memberships are not cached across requests in 1.0. Immutable parsed policies may be cached. Token lookup caches do not replace checks of current credential validity.

Long-running operations retain the originating actor and credential restriction, then recheck current access before a privileged transition. Workers never substitute a tenant-wide administrator for that actor. Long-polls and SSE refresh authorization before sending protected data; they do not retain an opening request's permission indefinitely. Tenant lifecycle rules remain those in schema.md.

7. Storage and API contract​

Bindings, memberships, owners, labels and credential restrictions remain the authoritative rows in schema.md. Linked Cedar policies and scope entities are deterministic projections. This integration adds no policy store, authorization graph table or durable scope-membership table.

The management API continues to expose structured bindings and existing domain operations. It carries no client-supplied Cedar entities or trusted authorization context. Error behavior is specified in openapi.yaml; detailed policy and binding identifiers remain in internal diagnostics. Mutating audit records carry the policy package version and the determining binding ids (audit_event), without recording secret values or the full entity slice.

User and service-account principals are distinct in Cedar. Both are app_user rows, so authorship and attribution reference either, and the API names them as ActorRef.

8. Verification​

CI runs these correctness cases:

  • Operator on team A plus viewer on team B cannot operate on B; direct service-account and team grants retain the same scope behavior.
  • Missing role, empty token allowlist and a narrowed token deny the corresponding action, including when another role permits related actions.
  • Cross-tenant resource, group, scope and token inputs cannot grant access; platform-admin status alone grants no tenant permissions.
  • Membership removal, binding removal, token revocation, ownership changes and label changes take effect on the next authorization snapshot.
  • Four-eyes self-approval and ineligible acting_teams fail; changed digests and insufficient approvals fail in the workflow even with permission.
  • Low request assurance, absent artifact reference, failed grant, quarantine and class mode each prevent secret delivery as required by its contract.
  • Missing entity data, invalid context and an error in a restriction never allow the operation; diagnostics contain no secret values.
  • Property tests compare SQL and in-memory selectors for all operators, missing keys, glob escaping and numeric edge cases.
  • Database tests compare SQL-visible sets, counts and groups with exhaustive Cedar decisions, including overlapping bindings, pagination and changing permissions. An unauthorized target beyond the first page rejects a compound write.
  • New protected endpoints and policy package changes fail CI when their action mapping or SQL equivalence coverage is missing.

Load tests use the fleet sizes in operations.md §8, with overlapping owner/selector grants and large impacted sets, alongside the agent polling and heartbeat load. No operation loads the whole fleet to authorize a bounded inventory page.