Authority token lifecycle

Updated Jul 27, 2026

Authority tokens are stateful security objects managed through zamburak-core. Lifecycle operations are:

Minting

Only host-trusted issuers may mint tokens. Each minted token encodes a subject, capability, scope, and expiry. Minting from untrusted issuers is rejected with AuthorityLifecycleError::UntrustedMinter.

use zamburak_core::{
    AuthorityToken, AuthorityIssuer, IssuerTrust, MintRequest,
    AuthorityTokenId, AuthoritySubject, AuthorityCapability,
    AuthorityScope, ScopeResource, TokenTimestamp,
};

let token = AuthorityToken::mint(MintRequest {
    token_id: AuthorityTokenId::try_from("tok-1")?,
    issuer: AuthorityIssuer::try_from("policy-host")?,
    issuer_trust: IssuerTrust::HostTrusted,
    subject: AuthoritySubject::try_from("assistant")?,
    capability: AuthorityCapability::try_from("EmailSendCap")?,
    scope: AuthorityScope::new(vec![
        ScopeResource::try_from("send_email")?,
    ])?,
    issued_at: TokenTimestamp::new(100),
    expires_at: TokenTimestamp::new(500),
})?;
# Ok::<(), zamburak_core::AuthorityLifecycleError>(())

Delegation

Delegated tokens must narrow both scope (strict subset) and lifetime (strict subset). Parent lineage is retained for audit. Delegation from revoked or expired parents is rejected before scope checks run. The delegation start time must also be on or after the parent issuance time.

use zamburak_core::{
    AuthorityToken, AuthorityIssuer, AuthorityTokenId, AuthoritySubject,
    AuthorityCapability, AuthorityScope, ScopeResource, IssuerTrust,
    MintRequest, DelegationRequest, RevocationIndex, TokenTimestamp,
};

// Mint a parent token with two scope resources.
let parent_token = AuthorityToken::mint(MintRequest {
    token_id: AuthorityTokenId::try_from("tok-parent")?,
    issuer: AuthorityIssuer::try_from("policy-host")?,
    issuer_trust: IssuerTrust::HostTrusted,
    subject: AuthoritySubject::try_from("assistant")?,
    capability: AuthorityCapability::try_from("EmailSendCap")?,
    scope: AuthorityScope::new(vec![
        ScopeResource::try_from("send_email")?,
        ScopeResource::try_from("draft_email")?,
    ])?,
    issued_at: TokenTimestamp::new(100),
    expires_at: TokenTimestamp::new(500),
})?;

// Delegate with strictly narrowed scope and lifetime.
let revocation_index = RevocationIndex::default();
let child = AuthorityToken::delegate(
    &parent_token,
    DelegationRequest {
        token_id: AuthorityTokenId::try_from("tok-child")?,
        delegated_by: AuthorityIssuer::try_from("policy-host")?,
        subject: AuthoritySubject::try_from("assistant")?,
        scope: AuthorityScope::new(vec![
            ScopeResource::try_from("send_email")?,
        ])?,
        delegated_at: TokenTimestamp::new(200),
        expires_at: TokenTimestamp::new(400),
    },
    &revocation_index,
)?;
# Ok::<(), zamburak_core::AuthorityLifecycleError>(())

Revocation

The host manages a RevocationIndex. Revoked tokens are stripped at policy-evaluation boundaries.

let mut revocation_index = RevocationIndex::default();
revocation_index.revoke(token.token_id().clone());

Policy boundary validation

PolicyEngine::validate_authority_tokens partitions tokens into effective and invalid sets at a given evaluation time. Revoked, expired, and pre-issuance tokens (evaluation time before issued_at) are stripped from the effective set.

let validation = engine.validate_authority_tokens(
    &tokens,
    &revocation_index,
    TokenTimestamp::new(now),
);
let effective = validation.effective_tokens();
let invalid = validation.invalid_tokens();

Snapshot restore

revalidate_tokens_on_restore applies the same validation as policy-boundary checks. On restore, any previously valid tokens that have since been revoked or expired are conservatively stripped.

Error handling

All lifecycle operations return Result<_, AuthorityLifecycleError>:

  • EmptyField — a required text field was empty,
  • InvalidTokenLifetime — issued_at is not before expires_at,
  • UntrustedMinter — issuer trust level is not HostTrusted,
  • DelegationScopeNotStrictSubset — delegated scope is not a proper subset,
  • DelegationLifetimeNotStrictSubset — delegated expiry is not before parent expiry,
  • InvalidParentToken — parent is revoked or expired at delegation time,
  • DelegationBeforeParentIssuance — delegation start is before parent issuance.

All timestamps are injected via TokenTimestamp to ensure deterministic evaluation without wall-clock dependencies.