Authority Revocation Bundle
Audience: Authority operators, Offline Kit / air-gap mirror maintainers, integrators consuming revocations. Scope: The on-disk revocation bundle format, its deterministic formatting and signing rules, and the CLI/API surface for exporting and verifying it.
The Authority service exports revocation information as an offline-friendly JSON document plus a detached JWS signature. Operators can mirror the bundle alongside Concelier exports so that air-gapped scanners receive the latest token, subject, and client revocations during the same sync pass.
File layout
| Artefact | Description |
|---|---|
revocation-bundle.json | Canonical JSON document describing revoked entities. Validates against etc/authority/revocation_bundle.schema.json. |
revocation-bundle.json.jws | Detached JWS signature covering the exact UTF-8 bytes of revocation-bundle.json. |
revocation-bundle.json.sha256 | SHA-256 digest used by mirror automation. The CLI auth revoke export writes it in prefixed sha256:<hex> form (lower-case hex). Optional but recommended. |
All hashes and signatures are generated after applying the deterministic formatting rules below.
Deterministic formatting rules
- JSON is serialised with UTF-8 encoding and 2-space indentation. Object keys are emitted in a fixed, explicitly declared order (
RevocationBundleModel/RevocationEntryModelannotate every property with[JsonPropertyOrder]), not lexicographically. Only free-form maps — the top-levelmetadataobject and each entry’smetadata— are key-sorted, because they are serialised from aSortedDictionary. - Arrays are sorted by deterministic keys:
- Top-level
revocationssorted by (category,id,revokedAt). - Nested arrays (
scopes) sorted ascending, unique enforced.
- Top-level
- Numeric values (
sequence) are emitted without leading zeros. - Timestamps use UTC ISO-8601 format with
Zsuffix.
Consumers MUST treat the combination of schemaVersion and sequence as a monotonic feed. Bundles with older sequence values are ignored unless bundleId differs and issuedAt is newer (supporting replay detection).
Revocation entry categories
The schema (revocation_bundle.schema.json) enumerates four categories. Two are emitted by the current builder; the other two are reserved by the schema for forward-looking use but are not produced by any Authority code path today.
| Category | Status | Description | Required fields (per schema) |
|---|---|---|---|
token | Emitted (RevocationBundleBuilder.BuildTokenEntries, from revoked token documents) | A single OAuth token (access, refresh, device, authorization code). | tokenType, clientId (plus id, revokedAt); optional subjectId |
client | Emitted (StandardClientProvisioningStore / LdapClientProvisioningStore write manual client revocations) | Entire OAuth client registration is revoked. | clientId (plus id, revokedAt) |
subject | Schema-reserved (no current emitter) | All credentials issued to a subject (user/service account). | subjectId (plus id, revokedAt) |
key | Schema-reserved (no current emitter; the schema declares no key-specific then clause) | Signing/encryption key material revoked. | id, revokedAt |
reason is a machine-friendly code constrained to the schema pattern ^[a-z0-9_.-]{1,64}$ (lower-cased on export); observed values include compromised, rotation, and lifecycle. When the source revocation has no reason recorded, the builder emits unspecified. reasonDescription may include a short operator note (max 256 chars).
Detached JWS workflow
- Serialise
revocation-bundle.jsonusing the deterministic rules. - Compute the lower-case hex SHA-256 digest of the canonical bytes; write it to
revocation-bundle.json.sha256(the CLI prefixes it assha256:<hex>). - Sign using ES256 (default, or
Signing.Algorithmwhen overridden) with the configured Authority signing key (Signing.ActiveKeyId). The detached JWS header carries the following claims (serialised compactly by the signer; shown expanded here for readability):{ "alg": "ES256", "kid": "{signingKeyId}", "provider": "{providerName}", "typ": "application/vnd.stellaops.revocation-bundle+jws", "b64": false, "crit": ["b64"] } - Persist the detached signature payload to
revocation-bundle.json.jws(per RFC 7797).
Verification steps:
- Validate
revocation-bundle.jsonagainst the schema. - Re-compute SHA-256 and compare with
.sha256(if present). - Resolve the signing key from the Authority JWKS endpoint (
/jwks, advertised in/.well-known/openid-configuration) or the offline key bundle, preferring the provider declared in the JWS header (providerfalls back todefault). - Verify the detached JWS using the resolved provider. The CLI mirrors Authority resolution, so builds compiled with
StellaOpsCryptoSodium=trueautomatically use the libsodium provider when advertised; otherwise verification downgrades to the managed fallback.
CLI verification workflow
Use the bundled CLI command before distributing a bundle:
stella auth revoke verify \
--bundle artifacts/revocation-bundle.json \
--signature artifacts/revocation-bundle.json.jws \
--key etc/authority/signing/authority-public.pem \
--verbose
The verifier performs three checks:
- Prints the computed digest in
sha256:<hex>format. Compare it with the exported.sha256artefact. - Parses the detached JWS header, confirms it advertises
b64: false, captures theproviderandkidhints, and reads the signing algorithm from thealgheader (defaulting to ES256 when absent). The CLI verifies with the algorithm carried in the header; it does not have the Authority’s runtime configuration to compare against. - Registers the supplied PEM key with the crypto provider registry and validates the signature (falling back to the managed provider when the hinted provider is unavailable).
A zero exit code means the bundle is ready for mirroring/import. Non-zero codes signal missing arguments, malformed JWS payloads, or signature mismatches; regenerate or re-sign the bundle before distribution.
Example
The repository contains an example bundle demonstrating a mixed export of token, subject, and client revocations. Use it as a reference for integration tests and tooling. Note that the subject entry is illustrative: it is valid against the schema, but no current Authority code path emits subject revocations (see Revocation entry categories).
Operations Quick Reference
stella auth revoke exportemits a canonical JSON bundle,.sha256digest, and detached JWS signature in one command. Use--outputto write into your mirror staging directory.stella auth revoke verifyvalidates a bundle against an offline PEM key (--bundle,--signature, and--keyare all required), honours theprovidermetadata embedded in the signature, and reports the computed digest before distribution.GET /internal/revocations/exportprovides the same payload for orchestrators that already talk to the bootstrap API. The bundle is returned base64-encoded inside a JSON envelope (bundle.data) alongside thesignature(withalgorithm,keyId,provider,value) anddigest(sha256). Like all/internal/*routes it is guarded by the bootstrap API key (BootstrapApiKeyFilter) and only mounted whenBootstrap.Enabledis set — it is not scope-gated.GET /api/v1/authority/revocation/statusreturns read-only bundle metadata for gateway/operator verification fan-out:entryCount,bundleDigest,lastIssuedAt, andlastSigningKid. It requiresauthority.revocation.readand never returns token identifiers.POST /internal/signing/rotate(also bootstrap API-key gated) rotates JWKS material without downtime; always export a fresh bundle afterward so downstream mirrors receive signatures from the newkid.- Offline Kit automation should mirror
revocation-bundle.json*alongside Concelier exports so agents ingest revocations during the same sync pass.
