Privileged Access Provisioning
Table of contents
A regulated application: a principal’s request for elevated access to a protected resource is gated by a mandatory multi-party approval chain; only after all required approvals commit does the composition allocate a time-limited, scoped Capability token to the requesting principal. Session validity is checked before every access exercise. The full arc — request, approvals, provisioning, access exercise, expiry, and revocation — is auditable from records alone, with attribution at every decision point.
Intent
Privileged access — access to financial controls, patient records, production credentials, source-of-truth databases, cryptographic key material — differs from ordinary access in two ways. First, the grant itself must be authorized by more than one party: a single administrator who can self-approve elevated access to any resource is a control failure under every regulated framework from SOX to HIPAA to PCI DSS. Second, the access must be time-limited and scoped: indefinite standing access to privileged resources is the most common finding in security audits, because standing access that outlives the business need is indistinguishable from residual access that was never intended.
The constituent atoms address neither property alone. Multi-Party Approval enforces the approval gate but does not produce the access artifact. Capability produces the time-limited scoped access token but knows nothing about whether an approval chain cleared. Session validates the authenticated channel but does not gate any downstream action. Credential authenticates the principal but does not know what they are requesting access to. The composition is the layer that wires these concerns into a single, enforceable arc: no Capability is issued without an Approved chain; no access exercise succeeds without an active Session; the full arc is recorded in one tamper-evident Audit Trail.
The load-bearing design decision is that privileged access is modeled as a Capability token rather than a Permissions grant. Permissions grants are persistent until revoked and identity-keyed — they say “this actor may perform this action, indefinitely.” Capability tokens are time-bounded, scoped, and bearer-keyed — they say “whoever holds this token may exercise this specific access once (or N times), until this date.” Privileged access is temporary authorization, not standing access. The bearer-key property of Capability is a feature here: the access token can be handed off to an automated system or a break-glass process without requiring the executing agent to carry the principal’s identity. The Capability’s audit record names the allocator (the composition, acting on behalf of the approved request) and the requestor (via the request record); it intentionally does not name the redeemer. This is the audit-subject asymmetry the composition must defend: the approval chain names every approver; the access exercise names only the session under which it occurred, not the bearer who presented the token.
This composition is the library’s worked example of approval-gated provisioning — the pattern that recurs in privileged access management (PAM), break-glass access, time-boxed administrative escalation, and regulated change-control access in financial, healthcare, and government systems.
Summary
Privileged Access Provisioning is a regulated composition that enforces the full lifecycle of a privileged access request: initiation under a named requestor, mandatory multi-party approval, Capability-token provisioning on approval, session-validated access exercise, and explicit revocation or natural expiry. It composes five constituents: Multi-Party Approval (the approval-gate substrate), Credential (authentication of the requesting principal and the approvers), Session (the time-limited authenticated channel checked before every access exercise), Capability (the provisioned access token), and Audit Trail (the regulated-audit substrate shared across all five stages of the arc).
The composition’s central structural commitment is approval-gates-provisioning: no Capability token exists for a privileged access request without a corresponding Approved chain record in Multi-Party Approval. The check is not advisory — the composition’s action wiring makes it structurally impossible to allocate a Capability for a request whose chain has not reached Approved. An auditor can verify this from the records alone by confirming that every Capability token in the provisioned-access store has a corresponding Approved chain record; a Capability without an Approved chain is evidence of a bypass.
The second structural commitment is session-gated exercise: exercise_access validates the caller’s session_token against Session’s validity surface before redeeming the Capability. A Session that has expired or been revoked — including cascading invalidation triggered by Credential revocation under the Login composition — blocks the access exercise before the Capability is even presented. The session check is not advisory; it is the first operation of exercise_access, and a non-valid result from Session.validate returns rejected(session-invalid) without touching the Capability.
The third commitment is single Audit Trail across all five stages: approval chain events (from Multi-Party Approval), provisioning events (Capability allocation, attribution pairing), and access-exercise events are all recorded in the same Audit Trail instance, producing one tamper-evident chain from which the full arc is reconstructable. An auditor reading the Audit Trail can trace any access exercise backward to the Capability, to the provisioning event, to the Approved chain record, to the individual approver attestations, to the original request. There is no gap in the chain and no second store to correlate.
The resulting arc has seven states: Pending (request submitted, approval chain in progress), Approved (chain cleared, provisioning cascade firing — internal transient state, not externally stable; transitions to Provisioned or ProvisioningFailed atomically within the same action), Provisioned (Capability token active), Denied (approval chain rejected), Withdrawn (request withdrawn before approval), Revoked (Capability revoked after provisioning), and ProvisioningFailed (chain reached Approved but Capability.allocate failed at provisioning time — terminal; requires a new request_access call to retry). Expiry of the Capability is reflected in the Capability store rather than as a separate request state; a request in Provisioned state with an Expired Capability record is fully readable from the two stores together.
Composes
-
Multi-Party Approval — the approval-gate substrate. The composition maintains one Multi-Party Approval instance whose chain-store
subject_refis therequest_idand whosescopeis theaccess_scopeof the request. Every privileged access request maps one-to-one to a Multi-Party Approval chain. Chain-level actions (initiate_chain,approve_step,reject_step,withdraw_chain) are exposed through this composition’s surface, which adds session-validation and Audit Trail recording around the passthrough. Multi-Party Approval’s own Audit Trail substrate is the same Audit Trail instance this composition uses for provisioning and access-exercise events — one Audit Trail instance for the full arc. -
Credential — the authentication surface for the requesting principal and for the approvers. The composition calls
Credential.verifyto confirm the requestor’s credential is Active before a request is accepted, and again before each approver step is routed (ensuring the approver has not been revoked between chain initiation and decision). Credential is not independently managed by this composition — it is queried read-only. The composition does not callregister,rotate, orrevokeon any Credential; those are Login’s (C13) or the identity-management surface’s concern. -
Session — the time-limited authenticated channel. The composition calls
Session.validate(session_token)as the first step ofexercise_access. If the session is notvalid— expired, revoked, or not known — the exercise is rejected before the Capability is presented. Session is not independently managed by this composition; it is queried read-only. Sessions are issued by the Login composition (C13). The cascade from Credential revocation through Session invalidation through blockedexercise_accesscalls is the composition’s cascading-revocation emergent invariant. -
Permissions — the authorization gate for caller-initiated actions. The composition calls
Permissions.permittedinrequest_access(scope:requests:initiate), inwithdraw_request(scope:requests:withdraw, for actors other than the requestor), and inrevoke_access(scope:requests:revoke). Permissions is queried read-only; the composition does not callgrantorrevokeon any permission. The authorization policy — which principals hold which request scopes — is owned by the deployment’s Permissions store. -
Capability — the provisioned access token. The composition calls
Capability.allocateonce, on the event that the Multi-Party Approval chain reachesApproved, producing the bearer token delivered to the requestor.max_redemptionsis deployment-configurable (default 1 for single-exercise privileged access; higher for break-glass or time-boxed access with multiple permitted uses).Capability.redeemis called insideexercise_accessafter Session validation passes.Capability.revokeis called byrevoke_access. The redeemer’s identity is intentionally not recorded by the Capability atom; the session under whichexercise_accesswas called is the closest the audit record comes to a redeemer identity. -
Audit Trail — the regulated-audit substrate. One Audit Trail instance is maintained across all five stages of the arc. Multi-Party Approval’s approval-chain events, the provisioning event (
access_provisioned), and each access-exercise event (access_exercised) and revocation event (access_revoked) are all recorded in this instance. The instance is configured with the host’s regulatory retention policy at deployment. Event Log, Actor Identity, Retention Window, and Tamper Evidence are reached transitively through Audit Trail; the composition does not maintain separate instances of those atoms.
Composition logic
Application state
The composition owns emergent state that wires the constituent atoms into one queryable access-request surface:
-
request_store— the set of access request records. Each record carriesrequest_id,requestor_ref,resource_ref,access_scope,justification,requested_at,expires_at(computed fromttlat request time),state(PendingApproved Provisioned Denied Withdrawn Revoked ProvisioningFailed), and denial_reason(nullable; set when state transitions toDeniedorProvisioningFailed).request_id,requestor_ref,resource_ref,access_scope,justification,requested_at, andexpires_atare immutable once written.stateis the only mutable field; it advances forward only.ProvisioningFailedis a terminal state: the approval chain cleared but the Capability allocation failed at provisioning time. A request inProvisioningFailedrequires a newrequest_accesscall to retry; there is no in-place re-provisioning action. -
request_to_chain— map fromrequest_idto thechain_idin Multi-Party Approval. Set atrequest_accesstime and immutable. Every request maps to exactly one chain. -
request_to_capability— map fromrequest_idto thecapability_tokenin Capability. Set at provisioning time (the transition fromApprovedtoProvisioned) and immutable. Only requests inProvisioned,Revoked, or (implicitly)Expiredstate have an entry in this map. -
capability_to_request— reverse map fromcapability_tokentorequest_id. Maintained as the strict inverse ofrequest_to_capability; every atomic write torequest_to_capabilitywrites the corresponding entry tocapability_to_requestin the same transaction. Used byexercise_accessstep 2 to recover therequest_idfor a presentedcapability_tokenwithout a full scan. Likerequest_to_capability, entries are immutable once written and never deleted by the composition. -
session_access_log— append-only record of eachexercise_accesscall that reached the Capability redeem step. Each entry carriesrequest_id,session_token,capability_token,exercised_at, andresult(redeemedcapability-invalid(reason)). Populated regardless of whether the Capability.redeem call succeeds or fails (if Session.validate succeeds and the call reaches the Capability step). Never modified after write.
Configuration
-
default_ttl— the Capability token’s time-to-live if the requestor does not supply attl. Deployment-configurable. Must be positive. Absent default isinvalid-request. -
max_redemptions_default— themax_redemptionsvalue passed toCapability.allocatewhen not overridden by the requestor. Defaults to 1 (single-exercise). Deployments permitting multi-use privileged access tokens configure higher values; the maximum permitted value is itself deployment-configurable. -
credential_check_on_request— whetherCredential.verifyis called against the requestor’s credential before accepting the request. Defaults totrue. Deployments that manage requestor credential verification upstream may disable; disabling does not change the session-validation requirement atexercise_access. -
approver_set_minimum— passed through to Multi-Party Approval’s configuration. The composition enforces that privileged access requires genuine multi-party approval by defaulting this to 2. A deployment that wants single-approver privileged access explicitly sets this to 1; the audit record reflects the single-approver configuration. -
audit_trail_retention_policy— the policy reference passed to Audit Trail at eachrecord_actioncall. Typicallysox_7_year,hipaa_6_year, orpci_dss_1_yeardepending on the regulated domain. Deployment-configurable. -
application_actor_refandapplication_credential— the composition’s service identity, used to attribute system-originated events (provisioning, cascade steps) in the Audit Trail. Same discipline as Multi-Party Approval’sapplication_actor_ref. The deployment provisions and rotates this credential; its compromise surface and forgery defense follow the same reasoning as documented in Multi-Party Approval’s Configuration section.
Action wiring
request_access(requestor_ref, resource_ref, access_scope, justification, approver_set, quorum_rule, ttl?, credential?) → request_id | rejected(invalid-request | permission-denied | credential-invalid | storage-failure)
- Validate:
requestor_ref,resource_ref,access_scope,justificationmust be non-null and non-empty;ttlmust be positive if supplied. Any violation:invalid-request. - Call
Permissions.permitted(requestor_ref, requests:initiate)→ ifdenied, returnpermission-denied. - If
credential_check_on_requestis enabled: callCredential.verify(credential, requestor_ref)→ if notverified, returncredential-invalid. - Allocate a fresh
request_id. Record the request inrequest_storewithstate = Pending,requested_at = now,expires_at = now + ttl(or default). - Call
MultiPartyApproval.initiate_chain(actor_ref=requestor_ref, subject_ref=request_id, scope=access_scope, approver_set, quorum_rule)→chain_id | rejected(...). On rejection, roll back therequest_storewrite and surfaceinvalid-requestorstorage-failureas appropriate. - Record
request_to_chain[request_id] = chain_id. - Call
AuditTrail.record_action(action_ref=access_requested, actor_ref=requestor_ref, credential, data={request_id, resource_ref, access_scope, justification, approver_set, quorum_rule, expires_at}, retention_policy). - If any write after step 4 fails, roll back and return
storage-failure. - Return
request_id.
approve_step(actor_ref, request_id, step_id, reason?, credential) → approved | rejected(invalid-request | not-known | not-pending | unauthorized | credential-invalid | recording-failure)
- Look up
chain_id = request_to_chain[request_id]. If not found,not-known. - Call
Credential.verify(credential, actor_ref)→ if notverified, returncredential-invalid. The approver’s credential must be Active at decision time. - Call
MultiPartyApproval.approve_step(actor_ref, chain_id, step_id, reason?)→ propagates rejection taxonomy unchanged. Onrecording-failure, surface unchanged. - Call
AuditTrail.record_action(action_ref=approval_step_decided, actor_ref, credential, data={request_id, step_id, decision=approved, reason}). - Chain state evaluation: query
MultiPartyApproval.read_chain(chain_id). If chainstateis nowApprovedandrequest_store[request_id].stateisPending(provisioning has not yet started): setrequest_store[request_id].state = Approved(internal transient — this marks the cascade as in-progress; see Summary), then fire the provisioning cascade (see below). Ifrequest_store[request_id].stateis alreadyApproved,Provisioned, orProvisioningFailed, the cascade is already in progress or has already run — skip it. This guard prevents re-fire under Multi-Party Approval’s trailing-decision semantics, where a late approval on an already-Approvedchain would otherwise trigger a second Capability allocation for the same request. If chainstateisRejectedandrequest_store[request_id].stateisPendingorApproved(i.e., not already terminal): transitionrequest_store[request_id].statetoDenied, setdenial_reason, and callAuditTrail.record_action(action_ref=access_denied, actor_ref=application_actor_ref, ...). If request is alreadyDenied, skip — a prior call already recorded the denial. - Return
approved.
reject_step(actor_ref, request_id, step_id, reason, credential) → rejected_outcome | rejected(invalid-request | not-known | not-pending | unauthorized | credential-invalid | recording-failure)
Mirrors approve_step steps 1–4 with decision=rejected. At step 5: if chain state is Rejected, transition request to Denied. reason is required (Approval Step Invariant 6 enforces it at the atom level).
withdraw_request(actor_ref, request_id, reason, credential) → withdrawn | rejected(invalid-request | not-known | not-pending | permission-denied | credential-invalid | recording-failure)
- Look up
request_id. If not found,not-known. Ifstateis notPending,not-pending. - Call
Credential.verify(credential, actor_ref)→ if notverified, returncredential-invalid. - If
actor_ref != request.requestor_ref: callPermissions.permitted(actor_ref, requests:withdraw)→ ifdenied, returnpermission-denied. The requestor is always permitted to withdraw their own request without an explicitrequests:withdrawgrant; any other actor requires one. - Call
MultiPartyApproval.withdraw_chain(actor_ref, chain_id=request_to_chain[request_id], reason)→withdrawn | rejected(not-known | not-pending | unauthorized | recording-failure). If the chain is already terminal (not-pending) — a race between this call and a concurrentapprove_steporreject_stepresolving the chain — returnnot-pendingto the caller; the request state remainsPendinginrequest_storeand will require a follow-up read to discover the chain’s actual terminal state and reconcile. The composition does not attempt to auto-reconcile this race; the auditor can reconstruct the race from the Audit Trail event timestamps. Ifwithdraw_chainreturnsunauthorized, the actor is not authorized to withdraw the chain at the Multi-Party Approval level (e.g., the chain requires the original requestor to withdraw and a different actor attempted it without arequests:withdrawgrant that Multi-Party Approval also honors); surface aspermission-denied. For any other rejection fromwithdraw_chain, surface asrecording-failure. - Transition
request_store[request_id].statetoWithdrawn. - Call
AuditTrail.record_action(action_ref=access_request_withdrawn, actor_ref, credential, data={request_id, reason}). - Return
withdrawn.
exercise_access(session_token, capability_token) → exercised | rejected(session-invalid(reason) | capability-invalid(reason) | not-known)
- Call
Session.validate(session_token)→valid | invalid(expired | revoked | not-known). If notvalid, returnrejected(session-invalid(reason)). The session check is the first operation; a non-validresult returns immediately without touching the Capability. - Look up
request_idviacapability_to_request[capability_token]. If the token is not in the map,not-known— the token was never issued by this composition’s provisioning cascade. - Call
Capability.redeem(capability_token)→redeemed | invalid(exhausted | expired | revoked | not-known). IfCapability.redeemreturnsnot-knownfor a token that is incapability_to_request, this is a storage inconsistency (the composition’s map has the token but the Capability atom does not). Append tosession_access_log:{request_id, session_token, capability_token, exercised_at=now, result=capability-invalid(not-known)}. Then callAuditTrail.record_action(action_ref=access_exercise_inconsistency, actor_ref=application_actor_ref, data={capability_token, request_id, detail="capability_to_request entry exists but Capability.redeem returned not-known"})and surfacerejected(capability-invalid(not-known))to the caller. Do not continue to step 4. - Append to
session_access_log(for all non-inconsistency outcomes of step 3):{request_id, session_token, capability_token, exercised_at=now, result}whereresultisredeemedon success orcapability-invalid(reason)on anyinvalid(...)response other than the inconsistency case handled in step 3. The log records everyexercise_accesscall that reached the Capability step — including failed attempts — because a presented-but-exhausted, presented-but-expired, or presented-but-revoked token is itself an auditable event. - If step 3 returned
invalid(reason), callAuditTrail.record_action(action_ref=access_exercise_failed, actor_ref=application_actor_ref, data={request_id, capability_token, session_token, reason})and returnrejected(capability-invalid(reason)). - Call
AuditTrail.record_action(action_ref=access_exercised, actor_ref=application_actor_ref, data={request_id, capability_token, session_token, exercised_at}). - Return
exercised.
Note: the Audit Trail entry records session_token but not the session’s principal_ref — the composition does not look up the session’s owner. The session_token is sufficient for an auditor who can cross-reference the Session store to recover the principal. This preserves the architecture’s layering: the composition does not reach inside the Session atom to extract fields it was not given.
revoke_access(actor_ref, request_id, reason, credential) → revoked | rejected(invalid-request | not-known | not-provisioned | credential-invalid | permission-denied | recording-failure)
- Look up
request_id. If not found,not-known. Ifstateis notProvisioned,not-provisioned. - Call
Credential.verify(credential, actor_ref)→ if notverified, returncredential-invalid. - Call
Permissions.permitted(actor_ref, requests:revoke)→ ifdenied, returnpermission-denied. - Call
Capability.revoke(capability_token=request_to_capability[request_id], revoked_by_ref=actor_ref, reason)→revoked | already-terminal(reason). Ifalready-terminal, the Capability is already inaccessible (expired, exhausted, or previously revoked by another path); continue to step 5. The access is blocked regardless of the terminal mode, and the state transition toRevokedand the Audit Trail entry serve the audit record. IfCapability.revokereturns any other rejection (e.g.,not-known), surface asrecording-failure. - Transition
request_store[request_id].statetoRevoked. - Call
AuditTrail.record_action(action_ref=access_revoked, actor_ref, credential, data={request_id, reason}). - Return
revoked.
Provisioning cascade
The provisioning cascade fires when MultiPartyApproval.read_chain returns state = Approved after an approve_step call. It is internal to the composition:
- Call
Capability.allocate(allocated_by_ref=application_actor_ref, scope=request.access_scope, resource_ref=request.resource_ref, max_redemptions=max_redemptions_default, ttl=request.expires_at - now)→capability_token | rejected(...). On rejection: transitionrequest_store[request_id].statetoProvisioningFailed, setdenial_reasonto the rejection reason, callAuditTrail.record_action(action_ref=access_provisioning_failed, actor_ref=application_actor_ref, data={request_id, reason}), and surfacerecording-failureto theapprove_stepcaller.ProvisioningFailedis a terminal state for the request; no in-place re-provisioning action exists. The requestor must callrequest_accessagain to open a new request and initiation chain. - Record
request_to_capability[request_id] = capability_token. - Transition
request_store[request_id].statefromApprovedtoProvisioned. - Call
AuditTrail.record_action(action_ref=access_provisioned, actor_ref=application_actor_ref, data={request_id, capability_token, requestor_ref=request.requestor_ref, resource_ref, access_scope}). - Deliver
capability_tokento the requestor via the deployment’s notification or return channel.
The provisioning cascade is the composition’s most critical operation. Invariant 1 (approval-gates-provisioning) depends on this cascade being the only path through which a capability_token enters request_to_capability. Any implementation path that writes to request_to_capability without going through the cascade violates Invariant 1.
Scope vocabulary
The composition defines these scopes for its Permissions instance:
| Scope | Permits |
|---|---|
requests:initiate | Call request_access |
requests:withdraw | Withdraw any access request (requestors may always withdraw their own) |
requests:revoke | Call revoke_access on any provisioned request |
requests:read | Query request records and their associated chain, capability, and audit events |
Behavior
-
Approval-gates-provisioning is structurally enforced, not advisory. The action wiring makes it impossible to allocate a Capability for a request via the composition’s surface without the Multi-Party Approval chain for that request reaching
Approved. The provisioning cascade is the only path from chain-approval to capability-allocation; there is noprovision_accessaction a caller can invoke directly. An implementation that provides a direct provisioning path outside the cascade violates Invariant 1. -
Session validity is the first check at access exercise, not an afterthought.
exercise_accesscallsSession.validatebefore touching the Capability. A revoked or expired session blocks the exercise regardless of whether the Capability is still valid. This is the composition’s enforcement of the cascading-revocation invariant: Credential revocation → Session invalidation (via Login/C13) →exercise_accessreturnssession-invalid→ privileged access blocked without any direct action on the Capability or the request record. -
The Capability’s bearer-key property is preserved, not subverted. The composition does not add an identity check at redemption time.
exercise_accesstakes asession_token(to verify the authenticated channel) and acapability_token(to present to the Capability atom). It records both in the access log and the Audit Trail. The Audit Trail entry names thesession_token— traceable to a principal by an auditor querying the Session store — but does not name the principal directly in the access-exercise record. The audit-subject asymmetry is intentional and documented: the approval chain names every approver; the access exercise names the authenticated channel, not the bearer’s identity at the moment of presentation. -
One Audit Trail covers the full arc. Every event from
access_requestedthrough approval decisions throughaccess_provisionedthrough eachaccess_exercisedthroughaccess_revokedis recorded in the same Audit Trail instance. The temporal sequence of these events in the Event Log is the structural form of the full arc. No second store is required to reconstruct the arc; no correlation step is needed. -
Approver authorization is enforced by the
approver_set, not by a Permissions grant.approve_stepandreject_stepdo not callPermissions.permittedon the deciding actor. Approver authorization is encoded in theapprover_setdeclared at chain initiation and enforced at the Multi-Party Approval level — the atom rejects any step decision from an actor not in theapprover_setfor that step. Adding aPermissions.permitted(actor_ref, requests:approve)check at this composition’s layer would double-enforce an already-enforced rule via a different mechanism, creating two failure modes for the same condition. The composition’s authorization concern for step decisions is limited to credential validity (Credential.verify) and approver-set membership (Multi-Party Approval’s enforcement). This is the composition’s deliberate design, not an oversight. -
Credential revocation blocks both new requests and existing approvals. If an approver’s Credential is revoked between chain initiation and their decision, step 2 of
approve_step(Credential.verify) returnsnot-verifiedand the step is rejected withcredential-invalid. The approval chain remains in-flight; the revoked approver’s step remains Pending. The chain can be withdrawn and re-initiated with a replacement approver. This is the correct behavior: a decision from a revoked actor is not valid evidence of approval.
Invariants
Invariant 1 — Approval-gates-provisioning. Every Capability token in request_to_capability has a corresponding Multi-Party Approval chain record in Approved state. A capability_token whose request_id maps to a chain that is not Approved is evidence of a provisioning bypass — a critical control failure. The provisioning cascade is the only write path to request_to_capability through the composition’s surface.
Invariant 2 — Request-capability bijection. Each request_id maps to at most one capability_token across the lifetime of the system. A second Capability is never allocated for a request that has already been provisioned; re-access requires a new request with a new approval chain. This preserves the audit property that each access token traces back to exactly one documented approval decision.
Invariant 3 — Session-gated exercise. Every access_exercised event in the Audit Trail was preceded in the same session by a valid result from Session.validate. No capability_token is redeemed through this composition’s surface without a prior session-validity confirmation in the same exercise_access call.
Invariant 4 — Cascading-revocation chain. Revoking the requestor’s Credential invalidates all Sessions derived from it (Login/C13 cascade). Any subsequent exercise_access call under an invalidated session returns session-invalid before the Capability is presented. The chain spans three composition layers (Credential → Session → exercise_access gate) and is not expressible at any single constituent atom layer.
Invariant 5 — Audit-arc completeness. Every access request record in request_store has at least one corresponding Audit Trail event with action_ref = access_requested. Every provisioned request has at least one access_provisioned event. Every access exercise recorded in session_access_log with result = redeemed has a corresponding access_exercised Audit Trail entry, and conversely, every access_exercised Audit Trail entry has a corresponding session_access_log entry. The bidirectional relationship holds because the action wiring writes the session_access_log entry (step 4) before the Audit Trail entry (step 6) in the success path — a Audit Trail entry without a log entry is evidence that step 4 was bypassed. An auditor can reconstruct the full arc — from first request to last access — from the Audit Trail alone.
Invariant 6 — Approver-credential completeness. Every approval decision (approve_step, reject_step) has a Credential.verify check against the approving actor’s credential immediately before the step is recorded. No approval decision is attributed to an actor whose Credential was not Active at the time of decision. A decision attributed to a revoked or expired credential is evidence of a verification bypass.
Invariant 7 — Denial completeness. Every request that transitions to Denied has a corresponding access_denied Audit Trail event naming the chain’s final state and the denial_reason. An auditor can determine from the records alone which requests were denied, by which quorum failure, and when.
Invariant 8 — Immutable request identity. Once a request record is created, request_id, requestor_ref, resource_ref, access_scope, justification, requested_at, and expires_at never change. The state field advances forward only; no terminal state returns to Pending.
Invariant 9 — Single Audit Trail instance. The same Audit Trail instance receives events from Multi-Party Approval (approval chain events), from the provisioning cascade (access_provisioned), and from exercise_access and revoke_access. A deployment that routes these event classes to separate Audit Trail instances violates this invariant and fragments the arc — an auditor reading one instance will see an incomplete record.
Invariant 10 — Session-access-log durability. Once written, entries in session_access_log are never modified or deleted. The log records every exercise_access call that reached the Capability step — including calls where Capability.redeem returned invalid — and is the operational-audit surface for access-exercise frequency, failed-presentation detection, session correlation, and capability exhaustion analysis. A log entry with result = capability-invalid(revoked) is evidence that someone presented a revoked token under a valid session; that signal is auditable from the log alone.
Examples
Happy path — privileged database access under two-approver gate
An engineer requests read access to a production database for incident investigation:
request_access(requestor_ref: eng_u42, resource_ref: db::prod::incidents, access_scope: "read", justification: "INC-2891 investigation — prod log correlation", approver_set: [mgr_u10, sec_u03], quorum_rule: all-of-2, ttl: 3600) → request_id: req_p7h3k2
Both approvers receive Assignment in-tray notifications. The security lead approves first:
approve_step(actor_ref: sec_u03, request_id: req_p7h3k2, step_id: step_s1, reason: "INC-2891 confirmed active", credential: cred_s03)
The chain is still Pending (one of two). The manager approves:
approve_step(actor_ref: mgr_u10, request_id: req_p7h3k2, step_id: step_s2, reason: "authorized", credential: cred_m10)
The chain reaches Approved. The provisioning cascade fires: Capability.allocate(scope: "read", resource_ref: db::prod::incidents, max_redemptions: 1, ttl: 3600) → capability_token: cap_9f2d1e. The request transitions to Provisioned. The engineer receives cap_9f2d1e.
The engineer authenticates (via Login, out of scope here) and acquires session_token: sess_a4b7c1. They exercise the access:
exercise_access(session_token: sess_a4b7c1, capability_token: cap_9f2d1e) → exercised
Session validates as valid. Capability redeems as redeemed (counter: 0 → exhausted, terminal). Access log entry written. Audit Trail records access_exercised.
Rejection path — first approver rejects; quorum unachievable
A request under all-of-2 quorum receives a rejection from the first approver:
reject_step(actor_ref: sec_u03, request_id: req_q5m8n1, step_id: step_s1, reason: "requestor does not have business need for this scope", credential: cred_s03)
Under all-of-2, one rejection makes quorum unachievable. Multi-Party Approval transitions the chain to Rejected. The composition detects this at step 5 of reject_step and transitions the request to Denied, recording denial_reason. The Audit Trail records access_denied. No Capability is ever allocated. Any subsequent exercise_access attempt returns not-known (the capability_token was never issued; the request_to_capability map has no entry for this request_id).
Rejection path — session invalid at exercise time
An engineer was provisioned access (Capability token issued after approval). Before they can exercise it, an administrator revokes their Credential (employment terminated):
Credential.revoke(credential_id: cred_e42, revoked_by_ref: hr_admin, reason: "employment-terminated")
The Login composition detects this and invalidates all Sessions derived from cred_e42, including sess_a4b7c1. The Session record transitions to Revoked.
The engineer (or an attacker with their token) attempts:
exercise_access(session_token: sess_a4b7c1, capability_token: cap_9f2d1e) → rejected(session-invalid(revoked))
Session.validate(sess_a4b7c1) returns invalid(revoked). The Capability is never presented. The Capability token cap_9f2d1e remains in Allocated state — unconsumed and revocable. The access log records no entry for this attempt (the session check failed before reaching the Capability step).
Regulated adversarial scenarios
Three scenarios the composition must survive in regulated contexts:
Regulator audit — SOX §404 privileged access control. An external auditor asks “can you prove that no one accessed the production financial database with elevated privileges without documented, multi-party authorization?” The auditor queries the Audit Trail for all access_exercised events with resource_ref matching the financial database. For each event, they trace backward: access_exercised → access_provisioned (same request_id) → Multi-Party Approval chain (same chain_id) → individual approval_step_decided events with approver references and credentials. Invariant 5 (audit-arc completeness) guarantees the chain is in the Audit Trail. Invariant 1 (approval-gates-provisioning) guarantees that every access_exercised event traces to an Approved chain. The auditor verifies both invariants from the records alone: count the access_exercised events; confirm each has a corresponding access_provisioned; confirm each access_provisioned has a corresponding chain in Approved state with the required number of decision records. No gap and no single-approver grants appear. SOX §404 is satisfied from the records alone.
Disputed access — former employee denies using privileged token. A security investigation reveals that the production credentials database was queried at 2026-11-14T03:22:00Z. A former employee claims they did not access it. The investigator queries session_access_log for access_exercised entries with resource_ref = db::prod::credentials and exercised_at in the window. The log returns one entry: {request_id: req_r9s4t1, session_token: sess_x7y2z9, capability_token: cap_3a1b2c, exercised_at: 2026-11-14T03:22:11Z}. The investigator queries the Session store for sess_x7y2z9: the session was issued to principal_ref: user_u88 (the former employee’s identity) at 03:15Z on the same date and expired at 04:15Z. The approval chain for req_r9s4t1 shows the former employee was the requestor_ref and both approvers approved on 2026-11-14T02:58Z. Invariant 3 (session-gated exercise) guarantees the session was valid at the moment of exercise; Invariant 5 (audit-arc completeness) guarantees the access record is in the Audit Trail. The former employee’s bearer of cap_3a1b2c operated under a session tied to their identity. The investigation has a structural record.
HIPAA break-glass access trace — clinical PHI access audit. A compliance officer at a hospital must demonstrate to an OCR auditor that a nurse’s emergency break-glass access to a patient’s record on 2026-09-12 was properly authorized and documented. The officer queries the Audit Trail for access_exercised events with resource_ref matching the patient record on that date. One event is returned: {request_id: req_h8k3n2, session_token: sess_c6d9f1, capability_token: cap_7b2e4a, exercised_at: 2026-09-12T02:44Z}. The officer traces backward: access_provisioned event for req_h8k3n2 is present, pointing to a Multi-Party Approval chain with quorum_rule: all-of-2 (charge nurse + attending physician). Both approval decisions are in the Audit Trail with verified approver credentials. The nurse’s session (sess_c6d9f1) was Active at 02:44Z. access_requested event for req_h8k3n2 shows the justification field: “INC-4418 cardiac event — emergency PHI access required.” The full arc — request, approval attestations, provisioning, and access exercise — is present in the Audit Trail. Invariant 5 (audit-arc completeness) and Invariant 6 (approver-credential completeness) provide the structural guarantee. HIPAA §164.312(b) access audit requirement is satisfied from the records alone. The storage-inconsistency event access_exercise_inconsistency does not appear for this token, confirming no storage anomaly at exercise time.
Breach investigation — approval gate bypassed? A security team discovers that access to a high-security vault was exercised (access_exercised appears in the Audit Trail at 2026-12-01T18:44Z). They want to confirm the approval gate was not bypassed. They query request_to_capability for the capability_token in the audit entry, recovering request_id: req_v2w5x8. They query request_store[req_v2w5x8]: state is Provisioned, chain_id is present. They query Multi-Party Approval for chain_id: state is Approved, two decision records present, both with verified approver credentials. Invariant 1 is satisfied. They also check: is there any capability_token in the Capability store that is not in request_to_capability? Any such token would be evidence of out-of-band allocation. The Capability store lists all allocated tokens; cross-referencing against request_to_capability is the structural bypass-detection check. If all tokens are accounted for, the approval gate was not bypassed. Invariant 1 makes this check deterministic.
Edge cases and explicit non-goals
-
Approval chain composition is Multi-Party Approval’s concern. This composition surfaces the chain-initiation and step-decision actions as pass-throughs. The quorum rules (
all-of-N,M-of-N,one-of-N), the trailing-decision behavior, the partial-failure recovery paths, and the cascading withdrawal logic are all owned by Multi-Party Approval. See its specification for those details. -
Capability expiry is lazy. When a
Provisionedrequest’s Capability TTL passes without exercise, the Capability transitions toExpiredat the nextexercise_accesscall orCapability.revokecall that detects the expiry (lazy-expiry path per the Capability atom). The request record’sstateremainsProvisionedin therequest_store; the Capability record carriesExpired. An auditor reading both records sees the full picture. A background scheduler may eagerly callCapability.expireper the Capability atom’sexpireaction; if it does, the request record remainsProvisionedand the Capability record transitions toExpiredbefore anyexercise_accessattempt. In either case, a subsequentexercise_accessreturnsrejected(capability-invalid(expired)). -
Re-access after expiry or revocation requires a new request. There is no
reneworre-provisionaction. If access is needed after the Capability expires or is revoked, the requestor callsrequest_accessagain with a fresh justification, which initiates a new approval chain. The old request record and its chain remain in the store as immutable history. Invariant 2 (request-capability bijection) guarantees no second Capability is issued against the oldrequest_id. -
ProvisioningFailedis a terminal state requiring a new request. If the provisioning cascade fires butCapability.allocatereturns a rejection (resource exhaustion, allocation service failure, TTL already elapsed), the request transitions toProvisioningFailed. The approval chain record remains inApprovedstate — the approvers’ decisions are valid and durable. However, the chain cannot be re-used:ProvisioningFailedis terminal for the request record. The requestor must callrequest_accessagain with a fresh justification, which initiates a new chain requiring the same (or a newly configured) approval set. TheProvisioningFailedrecord and the correspondingaccess_provisioning_failedAudit Trail event remain as permanent history. Notifying the requestor ofProvisioningFailedis a deployment obligation — the composition fires the cascade internally after anapprove_stepcall; the requestor is not present at that call and receives no automatic notification. A deployment must wire theaccess_provisioning_failedAudit Trail event to a notification or inbox mechanism so the requestor knows their request failed to provision and can retry. Operational runbooks for deployments whereCapability.allocatehas meaningful failure rates should instrument onaccess_provisioning_failedevents and alert; silentProvisioningFailedaccumulation indicates a capacity or configuration problem. -
Credential revocation of the requestor does not auto-revoke the Capability. Revoking the requestor’s Credential invalidates their Sessions, which blocks
exercise_accessvia the session check. But the Capability token itself remainsAllocateduntil its TTL expires or an administrator callsrevoke_access. A deployment that requires immediate Capability revocation on Credential revocation must implement a listener that callsrevoke_accesswhen Credential revocation events appear in the Audit Trail. This composition does not implement that listener; it is a composing-system obligation. The composition provides therevoke_accessaction precisely to support this pattern. -
Who receives the Capability token is a delivery concern. The composition allocates the Capability token and records it in
request_to_capability. Delivering it to the requestor — via notification, response payload, or a secure channel — is the deployment’s responsibility. The Capability atom’sallocateaction returns the token to the composition; the composition records it and surfaces it in the provisioning cascade’s step 5. The delivery mechanism (push notification, pull from a secure store, email link) is out of scope. -
This composition does not implement Login. Session issuance (Credential verification → Session.issue) is C13 (Login). This composition assumes sessions have already been established by Login and validates them at access-exercise time. A deployment that has not wired Login must satisfy the session-validation precondition through an equivalent mechanism.
-
Authorization policy — who may request access to what — is a deployment configuration. The composition checks Permissions for
requests:initiatebefore accepting a request, but it does not define the policy for which principals may request access to which resources. That policy is encoded in the Permissions store’s grant records and is owned by the deployment’s authorization surface. This composition enforces that the policy is checked; it does not define the policy. -
Multi-use Capability tokens.
max_redemptionsdefaults to 1 (single-exercise), but the composition supportsmax_redemptions > 1for break-glass or time-boxed access scenarios. Whenmax_redemptions > 1, thesession_access_logrecords each exercise separately; the Capability’sremaining_redemptionscounter decrements on eachredeemcall; the request transitions to effectively-exhausted when the counter reaches 0 (Capability state:Redeemed), but therequest_storestate remainsProvisioneduntil explicit revocation or natural expiry. An auditor reading both stores recovers the full usage picture.
Composition notes
-
Login (C13) — the upstream composition that issues Sessions. This composition depends on Sessions being issued by Login (Credential.verify → Session.issue → session_token returned to principal). Without Login or an equivalent session-issuance surface,
exercise_accessalways returnssession-invalid(not-known). Login is a peer composition, not a constituent; the two share the same Session and Credential atoms but own distinct composition-level surfaces. -
Session-Gated Authorization (C14) — a peer composition that gates permission checks on session validity. Privileged Access Provisioning gates Capability redemption on session validity; Session-Gated Authorization gates Permissions.permitted checks. The two are structurally parallel: both call
Session.validatebefore a downstream action, neither managing Session issuance. A deployment that wires both ensures that session validity is checked consistently at every protected surface — Capability redemption and Permissions evaluation alike. -
External Onboarding (C16) — in regulated deployments where privileged users are external contractors or auditors rather than internal employees, External Onboarding (Invitation → Party Identity → Credential registration) is the upstream surface that establishes the requestor’s Credential. A
request_accesscall with an unregisteredrequestor_reffails the Credential.verify check at step 2. External Onboarding establishes the Credential that this composition verifies. -
Capability-Backed Sharing (C15) — a peer composition that uses Capability for resource disclosure rather than privileged access. The two compositions share the Capability atom but wire it to different upstream gates: C15 gates on Selective Disclosure policy; this composition gates on Multi-Party Approval. The structural similarity — approve first, allocate Capability second, record in Audit Trail third — is the pattern the library names as approval-gated capability provisioning.
-
Tamper Evidence — in regulated deployments where the approval chain records and the Capability provisioning record are used as legal evidence (court proceedings, regulatory enforcement), composing with Tamper Evidence directly on the
request_storeandrequest_to_capabilitymaps provides cryptographic proof that no provisioning record was retroactively inserted or modified. The Audit Trail already applies Tamper Evidence to the event log; the composition’s emergent state maps are an additional surface for those deployments that require it.
Standards references
-
SOX §404 (Management Assessment of Internal Controls) — privileged access to financial systems requires documented, multi-party authorization. This composition’s approval-gates-provisioning invariant and its Audit Trail arc are the structural implementation of the §404 access-control evidence requirement. Every
access_exercisedevent traces to a documented, approved request with named approvers. -
HIPAA §164.312(a)(1) (Access Control) — covered entities must implement technical policies and procedures for electronic information systems that allow access only to authorized users. The composition’s multi-party approval gate is the authorization policy; the Credential.verify checks at both request and approval time ensure the actors in the arc are authenticated; the Session.validate check ensures the principal is currently authenticated at exercise time.
-
PCI DSS Requirements 7 and 8 (Restrict Access and Identify Users) — Requirement 7 mandates that access to system components and cardholder data is restricted to only those individuals whose job requires such access. Requirement 8 mandates that all users are assigned a unique ID before access is allowed. This composition enforces both: the approval chain documents the business need; the
requestor_refand the Session record establish the unique user identity. -
NIST SP 800-53 AC-2 (Account Management) and AC-6 (Least Privilege) — AC-2 requires that privileged user accounts are authorized by senior officials and reviewed regularly. AC-6 requires that privileged access is limited to the minimum required. This composition models the authorization record (AC-2) and the time-limited, scoped Capability (AC-6’s minimum-necessary principle expressed as a token with explicit TTL and scope).
-
NIST SP 800-53 AC-17 (Remote Access) — in deployments where privileged access is exercised remotely, the session-gated exercise check enforces that remote access is only permitted under an authenticated session. The Audit Trail records the session token for every remote access exercise, satisfying the remote-access audit requirement.
-
ISO/IEC 27001 §A.9.2.3 (Management of Privileged Access Rights) — the standard requires that the allocation and use of privileged access rights is controlled and restricted. This composition directly implements that control: allocation is gated by approval; use is gated by session validity; both are recorded in a tamper-evident log.
Generation acceptance
A derived implementation of Privileged Access Provisioning is acceptable — in the regulator-acceptance sense — when an external auditor, given the request_store, request_to_capability, session_access_log, Multi-Party Approval chain records, Capability records, Session records, and the shared Audit Trail, can do all of the following without recourse to source code, runbooks, or developer narration:
-
Confirm approval-gates-provisioning for every provisioned request. For every entry in
request_to_capability, confirm that a Multi-Party Approval chain record exists withsubject_ref = request_idandstate = Approved, and that the chain contains the required number ofapproval_step_decidedrecords inApprovedstate consistent with the declared quorum rule. Acapability_tokenwithout a correspondingApprovedchain is evidence of a provisioning bypass. Invariant 1 is the structural guarantee. -
Confirm session-gated exercise for every access exercise. For every entry in
session_access_logwithresult = redeemed, confirm that thesession_tokenfield references a Session record that was inActivestate (i.e.,now < expires_atand not revoked) at the recordedexercised_attimestamp. A redeemed access exercise under an expired or revoked session is evidence of a session-gate bypass. Invariant 3 is the structural guarantee. -
Confirm approver-credential validity at decision time. For every
approval_step_decidedAudit Trail entry, confirm that the approving actor’s Credential record showsActivestate at the recorded decision timestamp (i.e.,registered_at ≤ decision_atand either noexpires_atset orexpires_at > decision_atand not revoked before that time). A decision by an actor whose Credential was not Active at decision time is evidence of a verification bypass. Invariant 6 is the structural guarantee. -
Confirm denial completeness. For every request in
request_storewithstate = Denied, confirm that the Audit Trail contains anaccess_deniedevent for thatrequest_idand that the corresponding Multi-Party Approval chain record showsstate = Rejected. No denied request should be undocumented. Invariant 7 is the structural guarantee. -
Confirm inverse-map consistency. Every entry in
request_to_capabilityhas a corresponding entry incapability_to_requestwith the values swapped (i.e.,capability_to_request[request_to_capability[request_id]] = request_idfor allrequest_idwith a provisioning entry). Any asymmetry between the two maps is evidence of a non-atomic write — a storage-layer failure during the provisioning cascade. If such asymmetry is found, the affectedcapability_tokenmay have been allocated but is not recoverable to a request record via the normal lookup path; this requires manual reconciliation against the Capability atom’s store and the Audit Trail’saccess_provisionedevents. -
Reconstruct the full arc for any request. Given a
request_id, the following arc should be recoverable from the records alone without developer narration: who requested, against which resource and scope, when, with which justification; who the required approvers were and what each decided (with timestamps and credential references); when the Capability was provisioned; when and under which session each access exercise occurred; whether and when the access was revoked. This reconstruction requires no data beyond the stores named in this section.
Status
grounded on Final Critique 4 — three-pass baseline (Rounds 1–3) plus Final Critique (Round 4) complete. Thirteen findings across Rounds 1–4, all resolved. Round 5 (touch-triggered re-pass, 2026-05-23): one foundational finding open (R5-F1 — request_to_chain declared in vars but not initialized in Init, TLA+ model unrunnable via CLI). See Lineage notes for full finding-and-resolution record.
Lineage notes
Final Critique (Round 4) — Super Torvalds adversarial. Four findings, all resolved. Two foundational:
-
FC-F1 — exercise_access inconsistency branch exited before session_access_log write. Step 3’s early-exit path for the
not-knownstorage inconsistency skipped step 4 entirely, leaving thesession_access_logunwritten for a case that reached the Capability step. Fixed: step 3 now writes the log entry inline (withresult=capability-invalid(not-known)) before returning, and step 4 clarifies it applies to all non-inconsistency outcomes. -
FC-F2 —
Pending → Approvedtransition never written; approve_step guard checkingstate == Approvedwas permanently false on first fire. The cascade was dead code: the guardrequest_store[request_id].state is still Approvedcould never match because nothing set the state toApprovedbefore the check. Fixed: approve_step step 5 now explicitly setsstate = Approvedas the transient write before firing the cascade; the guard now checksstate == Pending(cascade not yet started) rather thanstate == Approved. The idempotency check for re-fire coversstate == Approved | Provisioned | ProvisioningFailed. -
FC-F3 — approve_step Denied transition not idempotency-guarded. A trailing
approve_stepon a chain already inRejectedstate would write a secondaccess_deniedAudit Trail event if the request was alreadyDenied. Fixed: the Denied transition now checksrequest_store state is Pending or Approvedbefore writing; if alreadyDenied, skips. -
FC-F4 — withdraw_request signature declared
unauthorizedbut body converted it torecording-failure. Fixed: step 4 now surfaceswithdraw_chain’sunauthorizedaspermission-denied(semantically correct);unauthorizedremoved from signature;permission-deniedadded.
Round 3, Pass 1 — GRID structural. All nine nodes clean. unauthorized in approve_step/reject_step signatures confirmed correct as Multi-Party Approval passthrough, not a composition-originated rejection; no gap. No findings.
Round 3, Pass 2 — EOS conceptual independence. session_access_log confirmed as composition-level operational surface of Event Log, not an extraction candidate. Session token (not principal_ref) in access-exercise records confirmed correct layering. No extractions.
Round 3, Pass 3 — Linus adversarial. Three findings:
- R3-F1 — Map sync not in Generation acceptance.
capability_to_requestandrequest_to_capabilitymust be atomically consistent; a non-atomic write during the provisioning cascade leaves a dangling token. Fixed: Generation acceptance extended with an inverse-map consistency check naming the reconciliation path via Audit Trailaccess_provisionedevents. - R3-F2 — withdraw_request step 4 has no rejection handling. Race between withdraw and a concurrent chain-terminal action:
withdraw_chainreturnsnot-pending. Fixed: step 4 now handles all rejection cases fromwithdraw_chain—not-pendingsurfaced to caller with race-documentation note; other rejections surface asrecording-failure. - R3-F3 / R3-F4 — access_exercise_inconsistency event unnamed in scenarios; HIPAA not exercised. Fixed: fourth adversarial scenario added (HIPAA break-glass clinical PHI trace);
access_exercise_inconsistencyevent absence is noted as a positive signal in that scenario.
Round 2, Pass 1 — GRID structural. Eight nodes clean. One gap: no feedback path documented for requestor notification on ProvisioningFailed — the cascade fires internally after approve_step; the requestor is absent. Fixed: added deployment-obligation note to the ProvisioningFailed edge case.
Round 2, Pass 2 — EOS conceptual independence. Five constituents confirmed freestanding. session_access_log correctly identified as the composition’s operational surface of Event Log, not an extraction candidate. Scope vocabulary correctly composition-level. No extractions.
Round 2, Pass 3 — Linus adversarial. Five findings:
- R2-F1 — ProvisioningFailed notification obligation not named. Fixed: Edge cases ProvisioningFailed bullet now explicitly names requestor-notification as a deployment obligation.
- R2-F2 — No Permissions.permitted on approve_step/reject_step — looked like oversight. Fixed: Behavior section now explicitly documents that approver authorization is enforced by
approver_setat Multi-Party Approval’s surface; a Permissions.permitted check would double-enforce via a different mechanism. Design documented, not changed. - R2-F3 — exercise_access inconsistency case unhandled.
capability_to_requesthas the token butCapability.redeemreturnsnot-known— storage inconsistency with no named handling path. Fixed: step 3 now names this case explicitly, routes it to a dedicatedaccess_exercise_inconsistencyAudit Trail event, and returnsrejected(capability-invalid(not-known)). - R2-F4 — Invariant 5 one-directional. “Every log entry with result=redeemed has an Audit Trail entry” stated; converse not stated. Fixed: Invariant 5 now states the bidirectional relationship with the ordering rationale (log written before Audit Trail in step wiring).
- R2-F5 — revoke_access rejection taxonomy missing permission-denied. Round 1 added explicit
Permissions.permittedcall to step 3 but did not update the signature. Fixed:permission-deniedadded to rejection vocabulary.
Round 1, Pass 1 — GRID structural. All nine MUSE nodes resolved. Feedback loop (Notification Fanout) confirmed out-of-scope and named as deployment obligation. Scope vocabulary table present. Configuration section present. No missing GRID nodes.
Round 1, Pass 2 — EOS conceptual independence. Five constituents confirmed freestanding. Session-gated exercise is an emergent property not expressible at any constituent atom. Approval-gates-provisioning is an emergent invariant across Multi-Party Approval and Capability. No over-absorption: the composition does not manage Credential rotation, Session issuance, or Capability expiry scheduling — all named as Login/deployment obligations.
Round 1, Pass 3 — Linus adversarial. Five findings:
-
F1 — revoke_access already-terminal gap. Step 4 called
Capability.revokewithout handling thealready-terminalresponse (Capability expired, exhausted, or previously revoked). Fixed: step 4 now continues pastalready-terminalto the state transition and Audit Trail entry. The revocation record is preserved regardless of the Capability’s prior terminal mode. -
F2 — revoke_access implicit permission check. Step 3 used informal prose (“actor must hold requests:revoke scope”) without an explicit
Permissions.permittedcall and nocredential-invalidannotation on step 2. Fixed: step 2 now explicitly returnscredential-invalidon non-verified; step 3 callsPermissions.permitted(actor_ref, requests:revoke)explicitly. -
F3 — ProvisioningFailed missing from state enum. The provisioning cascade referenced a “quarantined sub-state” with no named entry in the
request_storestate enum. Fixed:ProvisioningFailedadded to the enum with its terminal semantics and new-request resolution path defined. -
F4 — trailing-decision re-fire.
approve_stepstep 5 fired the provisioning cascade on any chain reachingApproved, including trailing decisions on an already-Approvedchain (Multi-Party Approval trailing-decision semantics). Fixed: added idempotency guard checkingrequest_store[request_id].statebefore cascade fires — skips if alreadyProvisionedorProvisioningFailed. -
F5 — undefined inverse map.
exercise_accessstep 2 referenced “inverse lookup onrequest_to_capability” without defining the inverse map. Fixed:capability_to_requestadded to Application state as the explicitly maintained strict inverse, updated atomically withrequest_to_capability.
Pre-test fixes (applied before Round 1 from GPT peer review): exercise_access logging gap (log moved to record both success and failure results; access_exercise_failed Audit Trail event added); missing Permissions.permitted call in request_access step 2; Approved state clarified as internal transient in Summary.
Conventions inherited. This composition composes regulated atoms (Credential, Session, Capability) and a regulated composition (Multi-Party Approval, itself composing Audit Trail). It inherits the Regulated adversarial scenarios and Generation acceptance conventions from PRESSURE_TESTING.md as a regulated composition.
Structural decisions made in draft.
-
Capability over Permissions for the provisioned access token. The access token is a time-limited, scoped Capability rather than a Permissions grant. Rationale: privileged access should be temporary authorization, not standing access. Permissions grants are indefinite until revoked; Capability tokens carry an explicit TTL and a redemption counter. The time-bound property is the PAM (Privileged Access Management) industry’s core discipline for break-glass and just-in-time access. The composing-system concern is scoping the Capability correctly at allocation time — the composition passes
access_scopeandresource_reffrom the request record toCapability.allocate. -
Single Audit Trail instance across all five stages. Multi-Party Approval’s approval-chain events and this composition’s provisioning and access-exercise events are all recorded in the same Audit Trail instance. This is a deliberate departure from treating Multi-Party Approval’s Audit Trail as internal state: the regulated audit question is about the full arc, not the approval chain in isolation. A deployment that routes approval events and access events to separate Audit Trail instances cannot answer the arc-reconstruction check in Generation acceptance without correlating two stores — which the records-alone bar disallows.
-
Session token (not principal identity) in the access-exercise audit record.
access_exercisedrecordssession_tokenrather than the session’sprincipal_ref. Rationale: the composition does not look inside the Session atom to extract fields it was not given; thesession_tokenis sufficient for an auditor who can cross-reference the Session store. This preserves the layering discipline: each atom owns its own field surface. -
Credential.verify on every approver step. The approver’s Credential is verified immediately before each step decision. This ensures that approval decisions are only recorded against Active credentials. A decision from a recently-revoked approver is not accepted. The cost is one Credential.verify call per step decision; the benefit is that every recorded decision in the Audit Trail is backed by a verified Credential at decision time.
Formal verification pass — TLA+ behavioral model.
Model: compositions/privilegedAccessProvisioning.tla (twin file). Python bounded model checker: compositions/privileged_access_provisioning_check.py. BFS over all reachable states within scope (RequestIDs={“r1”,”r2”}, ApproverIDs={“a1”,”a2”,”a3”}, CapTokens={“cap1”,”cap2”}, QuorumSize=2).
841 states explored. All safety invariants held across every reachable state — no counterexample found:
ApprovalGatesProvisioning— every token inrequest_to_capcorresponds to a chain inApprovedstate. No provisioning bypass reachable.MapInverseConsistency—request_to_capabilityandcapability_to_requestare strict inverses in all reachable states. No asymmetry introduced by any action interleaving.NoPendingRequestHasToken— no Pending request ever holds a capability token.TokensAllocatedOnlyForProvisioned— every allocated token has an owner inProvisionedorRevokedstate.ExhaustedSubsetAllocated— every exhausted token was allocated.
The trailing-decision re-fire guard (F4, Round 1) was the primary interleaving concern. The BFS confirmed that under concurrent approve_step calls racing on the same chain, the cascade fires exactly once — the idempotency guard on request_store[request_id].state correctly blocks re-fire in all explored interleavings.
No spec findings from the formal pass.
Formal model re-verification — Python BFS re-run + TLC attempt (precipitating touch for Round 5). Re-run 2026-05-23 after the cross-cutting rename of all four .tla files in compositions/ from kebab-case to lower-camelCase to satisfy SANY’s filename↔module-name rule — recorded in DISCOVERIES.md §2026-05-23. The rename constituted an effective touch under PRESSURE_TESTING.md §Touch triggers re-pass and triggered Round 5. Two verification paths were exercised:
Python BFS re-run. compositions/privileged_access_provisioning_check.py re-run at scope RequestIDs={"r1","r2"}, ApproverIDs={"a1","a2","a3"}, CapTokens={"cap1","cap2"}, QuorumSize=2. Result: 841 states explored, all safety invariants hold. Confirmed identical to the original formal verification pass result.
TLC CLI attempt. compositions/privilegedAccessProvisioning.cfg created (INVARIANT list: TypeOK, ApprovalGatesProvisioning, MapInverseConsistency, CascadeFiresAtMostOnce, TokensAllocatedOnlyForProvisioned, NoPendingRequestHasToken, ExhaustedSubsetAllocated; ACTION_CONSTRAINT: TerminalAbsorbingAction; CONSTANTS at the same scope as the Python BFS). TLC 2.19 failed on the initial state: “current state is not a legal state.” Root cause: request_to_chain is declared in the vars tuple (line 74, 90 of privilegedAccessProvisioning.tla) but is not initialized in Init and is not referenced in any action or invariant. The TLA+ semantics of Spec == Init /\ [][Next]_vars require every variable in vars to be assigned a value in Init; the omission makes the initial state undefined in TLC’s evaluation. The Python checker works because it does not model request_to_chain (the checker’s state representation covers only the variables that appear in the TLA+ actions). This is an implementation-discovered finding — logged below per CLAUDE.md §Implementation-discovered findings.
Pass 1 — Structural completeness (GRID), Round 5 (touch-triggered re-pass, 2026-05-23). Complete. No new GRID findings. The .cfg addition (new file) and the Python BFS re-run are touch events, not spec body changes.
Pass 2 — Conceptual independence (EOS), Round 5. Complete. No new EOS findings. The TLA+ artifact and the Python checker do not introduce new concepts requiring extraction.
Pass 3 — Adversarial scrutiny (Linus mode), Round 5. One finding.
- R5-F1 —
request_to_chaindeclared invarsbut never initialized inInitand never used — TLA+ model unrunnable via CLI (foundational → open).request_to_chainappears in theVARIABLESsection and in thevarstuple but has no corresponding assignment inInitand no reference in any action (ApproveStep,RejectStep,WithdrawRequest,ExerciseAccess,RevokeAccess,ProvisioningFailed), no role in any invariant, and no mention in TypeOK. The TLA+Spec == Init /\ [][Next]_varssemantics require every variable invarsto receive a value inInit; TLC 2.19 reports “current state is not a legal state” and halts before evaluating any invariant. The Python BFS checker abstracts overrequest_to_chainand runs clean at 841 states, confirming the English spec and the action-level model are correct; the TLA+ formal model artifact has a dead-variable deficiency that prevents CLI-driven TLC verification. PerCLAUDE.md§Implementation-discovered findings, this is a contradiction inside the formal model artifact (not a preference):varsclaims membership butInitdoes not provide the variable’s initial value. The spec and the Python checker are unaffected; the TLA+.tlafile requires a corrective review pass to either initializerequest_to_chaininInitwith the natural value[r \in RequestIDs |-> r](matching the comment “here: same as request_id for simplicity”) or remove it fromvarsand theVARIABLESdeclaration if it was left in by oversight. The resolution path is a future review round; the formal model is not edited mid-build. Open.
Round 5 does not close clean. Foundational finding: one open (R5-F1). Privileged Access Provisioning remains at grounded on Final Critique 4 pending resolution of R5-F1.