Resolve Concelier/Excititor merge conflicts

This commit is contained in:
root
2025-10-20 14:19:25 +03:00
2687 changed files with 212646 additions and 85913 deletions

View File

@@ -1,106 +1,106 @@
# Authority Threat Model (STRIDE)
> Prepared by Security Guild — 2025-10-12. Scope covers Authority host, Standard plug-in, CLI, bootstrap workflow, and offline revocation distribution.
## 1. Scope & Method
- Methodology: STRIDE applied to primary Authority surfaces (token issuance, bootstrap, revocation, operator tooling, plug-in extensibility).
- Assets in scope: identity credentials, OAuth tokens (access/refresh), bootstrap invites, revocation manifests, signing keys, audit telemetry.
- Out of scope: Third-party IdPs federated via OpenIddict (tracked separately in SEC6 backlog).
## 2. Assets & Entry Points
| Asset / Surface | Description | Primary Actors |
|-----------------|-------------|----------------|
| Token issuance APIs (`/token`, `/authorize`) | OAuth/OIDC endpoints mediated by OpenIddict | CLI, UI, automation agents |
| Bootstrap channel | Initial admin invite + bootstrap CLI workflow | Platform operators |
| Revocation bundle | Offline JSON + detached JWS consumed by agents | Feedser, Agents, Zastava |
| Plug-in manifests | Standard plug-in configuration and password policy overrides | Operators, DevOps |
| Signing keys | ES256 signing keys backing tokens and revocation manifests | Security Guild, HSM/KeyOps |
| Audit telemetry | Structured login/audit stream persisted to Mongo/observability stack | SOC, SecOps |
## 3. Trust Boundaries
| Boundary | Rationale | Controls |
|----------|-----------|----------|
| TB1 — Public network ↔️ Authority ingress | Internet/extranet exposure for `/token`, `/authorize`, `/bootstrap` | TLS 1.3, reverse proxy ACLs, rate limiting (SEC3.A / CORE8.RL) |
| TB2 — Authority host ↔️ Mongo storage | Credential store, revocation state, audit log persistence | Authenticated Mongo, network segmentation, deterministic serializers |
| TB3 — Authority host ↔️ Plug-in sandbox | Plug-ins may override password policy and bootstrap flows | Code signing, manifest validation, restart-time loading only |
| TB4 — Operator workstation ↔️ CLI | CLI holds bootstrap secrets and revocation bundles | OS keychain storage, MFA on workstations, offline kit checksum |
| TB5 — Authority ↔️ Downstream agents | Revocation bundle consumption, token validation | Mutual TLS (planned), detached JWS signatures, bundle freshness checks |
## 4. Data Flow Diagrams
### 4.1 Runtime token issuance
```mermaid
flowchart LR
subgraph Client Tier
CLI[StellaOps CLI]
UI[UI / Automation]
end
subgraph Perimeter
RP[Reverse Proxy / WAF]
end
subgraph Authority
AUTH[Authority Host]
PLGIN[Standard Plug-in]
STORE[(Mongo Credential Store)]
end
CLI -->|OAuth password / client creds| RP --> AUTH
UI -->|OAuth flows| RP
AUTH -->|PasswordHashOptions + Secrets| PLGIN
AUTH -->|Verify / Persist hashes| STORE
STORE -->|Rehash needed| AUTH
AUTH -->|Access / refresh token| RP --> Client Tier
```
### 4.2 Bootstrap & revocation
```mermaid
flowchart LR
subgraph Operator
OPS[Operator Workstation]
end
subgraph Authority
AUTH[Authority Host]
STORE[(Mongo)]
end
subgraph Distribution
OFFKIT[Offline Kit Bundle]
AGENT[Authorized Agent / Feedser]
end
OPS -->|Bootstrap CLI (`stellaops auth bootstrap`)| AUTH
AUTH -->|One-time invite + Argon2 hash| STORE
AUTH -->|Revocation export (`stellaops auth revoke export`)| OFFKIT
OFFKIT -->|Signed JSON + .jws| AGENT
AGENT -->|Revocation ACK / telemetry| AUTH
```
## 5. STRIDE Analysis
| Threat | STRIDE Vector | Surface | Risk (L×I) | Existing Controls | Gaps / Actions | Owner |
|--------|---------------|---------|------------|-------------------|----------------|-------|
| Spoofed revocation bundle | Spoofing | TB5 — Authority ↔️ Agents | Med×High | Detached JWS signature (planned), offline kit checksums | Finalise signing key registry & verification script (SEC4.B/SEC4.HOST); add bundle freshness requirement | Security Guild (follow-up: **SEC5.B**) |
| Parameter tampering on `/token` | Tampering | TB1 — Public ingress | Med×High | ASP.NET model validation, OpenIddict, rate limiter (CORE8.RL) | Tampered requests emit `authority.token.tamper` audit events (`request.tampered`, unexpected parameter names) correlating with `/token` outcomes (SEC5.C) | Security Guild + Authority Core (follow-up: **SEC5.C**) |
| Bootstrap invite replay | Repudiation | TB4 — Operator CLI ↔️ Authority | Low×High | One-time bootstrap tokens, Argon2id hashing on creation | Invites expire automatically and emit audit events on consumption/expiration (SEC5.D) | Security Guild |
| Token replay by stolen agent | Information Disclosure | TB5 | Med×High | Signed revocation bundles, device fingerprint heuristics, optional mTLS | Monitor revocation acknowledgement latency via Zastava and tune replay alerting thresholds | Security Guild + Zastava (follow-up: **SEC5.E**) |
| Privilege escalation via plug-in override | Elevation of Privilege | TB3 — Plug-in sandbox | Med×High | Signed plug-ins, restart-only loading, configuration validation | Add static analysis on manifest overrides + runtime warning when policy weaker than host | Security Guild + DevOps (follow-up: **SEC5.F**) |
| Offline bundle tampering | Tampering | Distribution | Low×High | SHA256 manifest, signed bundles (planned) | Add supply-chain attestation for Offline Kit, publish verification CLI in docs | Security Guild + Ops (follow-up: **SEC5.G**) |
| Failure to log denied tokens | Repudiation | TB2 — Authority ↔️ Mongo | Med×Med | Serilog structured events (partial), Mongo persistence path (planned) | Finalise audit schema (SEC2.A) and ensure `/token` denies include subject/client/IP fields | Security Guild + Authority Core (follow-up: **SEC5.H**) |
Risk scoring uses qualitative scale (Low/Med/High) for likelihood × impact; mitigation priority follows High > Med > Low.
## 6. Follow-up Backlog Hooks
| Backlog ID | Linked Threat | Summary | Target Owners |
|------------|---------------|---------|---------------|
| SEC5.B | Spoofed revocation bundle | Complete libsodium/Core signing integration and ship revocation verification script. | Security Guild + Authority Core |
| SEC5.C | Parameter tampering on `/token` | Finalise audit contract (`SEC2.A`) and add request tamper logging. | Security Guild + Authority Core |
| SEC5.D | Bootstrap invite replay | Implement expiry enforcement + audit coverage for unused bootstrap invites. | Security Guild |
| SEC5.E | Token replay by stolen agent | Coordinate Zastava alerting with the new device fingerprint heuristics and surface stale revocation acknowledgements. | Security Guild + Zastava |
| SEC5.F | Plug-in override escalation | Static analysis of plug-in manifests; warn on weaker password policy overrides. | Security Guild + DevOps |
| SEC5.G | Offline bundle tampering | Extend Offline Kit build to include attested manifest + verification CLI sample. | Security Guild + Ops |
| SEC5.H | Failure to log denied tokens | Ensure audit persistence for all `/token` denials with correlation IDs. | Security Guild + Authority Core |
Update `src/StellaOps.Cryptography/TASKS.md` (Security Guild board) with the above backlog entries to satisfy SEC5.A exit criteria.
# Authority Threat Model (STRIDE)
> Prepared by Security Guild — 2025-10-12. Scope covers Authority host, Standard plug-in, CLI, bootstrap workflow, and offline revocation distribution.
## 1. Scope & Method
- Methodology: STRIDE applied to primary Authority surfaces (token issuance, bootstrap, revocation, operator tooling, plug-in extensibility).
- Assets in scope: identity credentials, OAuth tokens (access/refresh), bootstrap invites, revocation manifests, signing keys, audit telemetry.
- Out of scope: Third-party IdPs federated via OpenIddict (tracked separately in SEC6 backlog).
## 2. Assets & Entry Points
| Asset / Surface | Description | Primary Actors |
|-----------------|-------------|----------------|
| Token issuance APIs (`/token`, `/authorize`) | OAuth/OIDC endpoints mediated by OpenIddict | CLI, UI, automation agents |
| Bootstrap channel | Initial admin invite + bootstrap CLI workflow | Platform operators |
| Revocation bundle | Offline JSON + detached JWS consumed by agents | Concelier, Agents, Zastava |
| Plug-in manifests | Standard plug-in configuration and password policy overrides | Operators, DevOps |
| Signing keys | ES256 signing keys backing tokens and revocation manifests | Security Guild, HSM/KeyOps |
| Audit telemetry | Structured login/audit stream persisted to Mongo/observability stack | SOC, SecOps |
## 3. Trust Boundaries
| Boundary | Rationale | Controls |
|----------|-----------|----------|
| TB1 — Public network ↔️ Authority ingress | Internet/extranet exposure for `/token`, `/authorize`, `/bootstrap` | TLS 1.3, reverse proxy ACLs, rate limiting (SEC3.A / CORE8.RL) |
| TB2 — Authority host ↔️ Mongo storage | Credential store, revocation state, audit log persistence | Authenticated Mongo, network segmentation, deterministic serializers |
| TB3 — Authority host ↔️ Plug-in sandbox | Plug-ins may override password policy and bootstrap flows | Code signing, manifest validation, restart-time loading only |
| TB4 — Operator workstation ↔️ CLI | CLI holds bootstrap secrets and revocation bundles | OS keychain storage, MFA on workstations, offline kit checksum |
| TB5 — Authority ↔️ Downstream agents | Revocation bundle consumption, token validation | Mutual TLS (planned), detached JWS signatures, bundle freshness checks |
## 4. Data Flow Diagrams
### 4.1 Runtime token issuance
```mermaid
flowchart LR
subgraph Client Tier
CLI[StellaOps CLI]
UI[UI / Automation]
end
subgraph Perimeter
RP[Reverse Proxy / WAF]
end
subgraph Authority
AUTH[Authority Host]
PLGIN[Standard Plug-in]
STORE[(Mongo Credential Store)]
end
CLI -->|OAuth password / client creds| RP --> AUTH
UI -->|OAuth flows| RP
AUTH -->|PasswordHashOptions + Secrets| PLGIN
AUTH -->|Verify / Persist hashes| STORE
STORE -->|Rehash needed| AUTH
AUTH -->|Access / refresh token| RP --> Client Tier
```
### 4.2 Bootstrap & revocation
```mermaid
flowchart LR
subgraph Operator
OPS[Operator Workstation]
end
subgraph Authority
AUTH[Authority Host]
STORE[(Mongo)]
end
subgraph Distribution
OFFKIT[Offline Kit Bundle]
AGENT[Authorized Agent / Concelier]
end
OPS -->|Bootstrap CLI (`stellaops auth bootstrap`)| AUTH
AUTH -->|One-time invite + Argon2 hash| STORE
AUTH -->|Revocation export (`stellaops auth revoke export`)| OFFKIT
OFFKIT -->|Signed JSON + .jws| AGENT
AGENT -->|Revocation ACK / telemetry| AUTH
```
## 5. STRIDE Analysis
| Threat | STRIDE Vector | Surface | Risk (L×I) | Existing Controls | Gaps / Actions | Owner |
|--------|---------------|---------|------------|-------------------|----------------|-------|
| Spoofed revocation bundle | Spoofing | TB5 — Authority ↔️ Agents | Med×High | Detached JWS signature (planned), offline kit checksums | Finalise signing key registry & verification script (SEC4.B/SEC4.HOST); add bundle freshness requirement | Security Guild (follow-up: **SEC5.B**) |
| Parameter tampering on `/token` | Tampering | TB1 — Public ingress | Med×High | ASP.NET model validation, OpenIddict, rate limiter (CORE8.RL) | Tampered requests emit `authority.token.tamper` audit events (`request.tampered`, unexpected parameter names) correlating with `/token` outcomes (SEC5.C) | Security Guild + Authority Core (follow-up: **SEC5.C**) |
| Bootstrap invite replay | Repudiation | TB4 — Operator CLI ↔️ Authority | Low×High | One-time bootstrap tokens, Argon2id hashing on creation | Invites expire automatically and emit audit events on consumption/expiration (SEC5.D) | Security Guild |
| Token replay by stolen agent | Information Disclosure | TB5 | Med×High | Signed revocation bundles, device fingerprint heuristics, optional mTLS | Monitor revocation acknowledgement latency via Zastava and tune replay alerting thresholds | Security Guild + Zastava (follow-up: **SEC5.E**) |
| Privilege escalation via plug-in override | Elevation of Privilege | TB3 — Plug-in sandbox | Med×High | Signed plug-ins, restart-only loading, configuration validation | Add static analysis on manifest overrides + runtime warning when policy weaker than host | Security Guild + DevOps (follow-up: **SEC5.F**) |
| Offline bundle tampering | Tampering | Distribution | Low×High | SHA256 manifest, signed bundles (planned) | Add supply-chain attestation for Offline Kit, publish verification CLI in docs | Security Guild + Ops (follow-up: **SEC5.G**) |
| Failure to log denied tokens | Repudiation | TB2 — Authority ↔️ Mongo | Med×Med | Serilog structured events (partial), Mongo persistence path (planned) | Finalise audit schema (SEC2.A) and ensure `/token` denies include subject/client/IP fields | Security Guild + Authority Core (follow-up: **SEC5.H**) |
Risk scoring uses qualitative scale (Low/Med/High) for likelihood × impact; mitigation priority follows High > Med > Low.
## 6. Follow-up Backlog Hooks
| Backlog ID | Linked Threat | Summary | Target Owners |
|------------|---------------|---------|---------------|
| SEC5.B | Spoofed revocation bundle | Complete libsodium/Core signing integration and ship revocation verification script. | Security Guild + Authority Core |
| SEC5.C | Parameter tampering on `/token` | Finalise audit contract (`SEC2.A`) and add request tamper logging. | Security Guild + Authority Core |
| SEC5.D | Bootstrap invite replay | Implement expiry enforcement + audit coverage for unused bootstrap invites. | Security Guild |
| SEC5.E | Token replay by stolen agent | Coordinate Zastava alerting with the new device fingerprint heuristics and surface stale revocation acknowledgements. | Security Guild + Zastava |
| SEC5.F | Plug-in override escalation | Static analysis of plug-in manifests; warn on weaker password policy overrides. | Security Guild + DevOps |
| SEC5.G | Offline bundle tampering | Extend Offline Kit build to include attested manifest + verification CLI sample. | Security Guild + Ops |
| SEC5.H | Failure to log denied tokens | Ensure audit persistence for all `/token` denials with correlation IDs. | Security Guild + Authority Core |
Update `src/StellaOps.Cryptography/TASKS.md` (Security Guild board) with the above backlog entries to satisfy SEC5.A exit criteria.

View File

@@ -1,56 +1,56 @@
{
"$schema": "../../etc/authority/revocation_bundle.schema.json",
"schemaVersion": "1.0.0",
"issuer": "https://auth.stella-ops.example",
"bundleId": "6f9d08bfa0c24a0a9f7f59e6c17d2f8e8bca2ef34215c3d3ba5a9a1f0fbe2d10",
"issuedAt": "2025-10-12T15:00:00Z",
"validFrom": "2025-10-12T15:00:00Z",
"sequence": 42,
"signingKeyId": "authority-signing-20251012",
"revocations": [
{
"id": "7ad4f3d2c21b461d9b3420e1151be9c4",
"category": "token",
"tokenType": "access_token",
"clientId": "feedser-cli",
"subjectId": "user:ops-admin",
"reason": "compromised",
"reasonDescription": "Access token reported by SOC automation run R-2045.",
"revokedAt": "2025-10-12T14:32:05Z",
"scopes": [
"feedser:export",
"feedser:jobs"
],
"fingerprint": "AD35E719C12204D7E7C92ED3F6DEBF0A44642D41AAF94233F9A47E183F4C5F18",
"metadata": {
"reportId": "R-2045",
"source": "soc-automation"
}
},
{
"id": "user:departed-vendor",
"category": "subject",
"subjectId": "user:departed-vendor",
"reason": "lifecycle",
"revokedAt": "2025-10-10T18:15:00Z",
"metadata": {
"ticket": "HR-8821"
}
},
{
"id": "ci-runner-legacy",
"category": "client",
"clientId": "ci-runner-legacy",
"reason": "rotation",
"revokedAt": "2025-10-09T11:00:00Z",
"expiresAt": "2025-11-01T00:00:00Z",
"metadata": {
"replacement": "ci-runner-2025"
}
}
],
"metadata": {
"generator": "stellaops-authority@1.4.0",
"jobId": "revocation-export-20251012T1500Z"
}
}
{
"$schema": "../../etc/authority/revocation_bundle.schema.json",
"schemaVersion": "1.0.0",
"issuer": "https://auth.stella-ops.example",
"bundleId": "6f9d08bfa0c24a0a9f7f59e6c17d2f8e8bca2ef34215c3d3ba5a9a1f0fbe2d10",
"issuedAt": "2025-10-12T15:00:00Z",
"validFrom": "2025-10-12T15:00:00Z",
"sequence": 42,
"signingKeyId": "authority-signing-20251012",
"revocations": [
{
"id": "7ad4f3d2c21b461d9b3420e1151be9c4",
"category": "token",
"tokenType": "access_token",
"clientId": "concelier-cli",
"subjectId": "user:ops-admin",
"reason": "compromised",
"reasonDescription": "Access token reported by SOC automation run R-2045.",
"revokedAt": "2025-10-12T14:32:05Z",
"scopes": [
"concelier:export",
"concelier:jobs"
],
"fingerprint": "AD35E719C12204D7E7C92ED3F6DEBF0A44642D41AAF94233F9A47E183F4C5F18",
"metadata": {
"reportId": "R-2045",
"source": "soc-automation"
}
},
{
"id": "user:departed-vendor",
"category": "subject",
"subjectId": "user:departed-vendor",
"reason": "lifecycle",
"revokedAt": "2025-10-10T18:15:00Z",
"metadata": {
"ticket": "HR-8821"
}
},
{
"id": "ci-runner-legacy",
"category": "client",
"clientId": "ci-runner-legacy",
"reason": "rotation",
"revokedAt": "2025-10-09T11:00:00Z",
"expiresAt": "2025-11-01T00:00:00Z",
"metadata": {
"replacement": "ci-runner-2025"
}
}
],
"metadata": {
"generator": "stellaops-authority@1.4.0",
"jobId": "revocation-export-20251012T1500Z"
}
}

View File

@@ -1,91 +1,91 @@
# Authority Revocation Bundle
The Authority service exports revocation information as an offline-friendly JSON document plus a detached JWS signature. Operators can mirror the bundle alongside Feedser exports to ensure air-gapped scanners receive the latest token, subject, and client revocations.
## File layout
| Artefact | Description |
| --- | --- |
| `revocation-bundle.json` | Canonical JSON document describing revoked entities. Validates against [`etc/authority/revocation_bundle.schema.json`](../../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` | Hex-encoded SHA-256 digest used by mirror automation (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, 2-space indentation, and lexicographically sorted object keys.
- Arrays are sorted by deterministic keys:
- Top-level `revocations` sorted by (`category`, `id`, `revokedAt`).
- Nested arrays (`scopes`) sorted ascending, unique enforced.
- Numeric values (`sequence`) are emitted without leading zeros.
- Timestamps use UTC ISO-8601 format with `Z` suffix.
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
| Category | Description | Required fields |
| --- | --- | --- |
| `token` | A single OAuth token (access, refresh, device, authorization code). | `tokenType`, `clientId`, `revokedAt`, optional `subjectId` |
| `subject` | All credentials issued to a subject (user/service account). | `subjectId`, `revokedAt` |
| `client` | Entire OAuth client registration is revoked. | `clientId`, `revokedAt` |
| `key` | Signing/encryption key material revoked. | `id`, `revokedAt` |
`reason` is a machine-friendly code (`compromised`, `rotation`, `policy`, `lifecycle`, etc). `reasonDescription` may include a short operator note.
## Detached JWS workflow
1. Serialise `revocation-bundle.json` using the deterministic rules.
2. Compute SHA-256 digest; write to `revocation-bundle.json.sha256`.
3. Sign using ES256 (default) with the configured Authority signing key. The JWS header uses:
```json
{
"alg": "ES256",
"kid": "{signingKeyId}",
"provider": "{providerName}",
"typ": "application/vnd.stellaops.revocation-bundle+jws",
"b64": false,
"crit": ["b64"]
}
```
4. Persist the detached signature payload to `revocation-bundle.json.jws` (per RFC 7797).
Verification steps:
1. Validate `revocation-bundle.json` against the schema.
2. Re-compute SHA-256 and compare with `.sha256` (if present).
3. Resolve the signing key from JWKS (`/.well-known/jwks.json`) or the offline key bundle, preferring the provider declared in the JWS header (`provider` falls back to `default`).
4. Verify the detached JWS using the resolved provider. The CLI mirrors Authority resolution, so builds compiled with `StellaOpsCryptoSodium=true` automatically 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:
```bash
stellaops 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:
1. Prints the computed digest in `sha256:<hex>` format. Compare it with the exported `.sha256` artefact.
2. Confirms the detached JWS header advertises `b64: false`, captures the provider hint, and that the algorithm matches the Authority configuration (ES256 unless overridden).
3. 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](revocation-bundle-example.json) demonstrating a mixed export of token, subject, and client revocations. Use it as a reference for integration tests and tooling.
## Operations Quick Reference
- `stella auth revoke export` emits a canonical JSON bundle, `.sha256` digest, and detached JWS signature in one command. Use `--output` to write into your mirror staging directory.
- `stella auth revoke verify` validates a bundle using cached JWKS or an offline PEM key, honours the `provider` metadata embedded in the signature, and reports digest mismatches before distribution.
- `POST /internal/revocations/export` provides the same payload for orchestrators that already talk to the bootstrap API.
- `POST /internal/signing/rotate` rotates JWKS material without downtime; always export a fresh bundle afterward so downstream mirrors receive signatures from the new `kid`.
- Offline Kit automation should mirror `revocation-bundle.json*` alongside Feedser exports so agents ingest revocations during the same sync pass.
# Authority Revocation Bundle
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 to ensure air-gapped scanners receive the latest token, subject, and client revocations.
## File layout
| Artefact | Description |
| --- | --- |
| `revocation-bundle.json` | Canonical JSON document describing revoked entities. Validates against [`etc/authority/revocation_bundle.schema.json`](../../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` | Hex-encoded SHA-256 digest used by mirror automation (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, 2-space indentation, and lexicographically sorted object keys.
- Arrays are sorted by deterministic keys:
- Top-level `revocations` sorted by (`category`, `id`, `revokedAt`).
- Nested arrays (`scopes`) sorted ascending, unique enforced.
- Numeric values (`sequence`) are emitted without leading zeros.
- Timestamps use UTC ISO-8601 format with `Z` suffix.
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
| Category | Description | Required fields |
| --- | --- | --- |
| `token` | A single OAuth token (access, refresh, device, authorization code). | `tokenType`, `clientId`, `revokedAt`, optional `subjectId` |
| `subject` | All credentials issued to a subject (user/service account). | `subjectId`, `revokedAt` |
| `client` | Entire OAuth client registration is revoked. | `clientId`, `revokedAt` |
| `key` | Signing/encryption key material revoked. | `id`, `revokedAt` |
`reason` is a machine-friendly code (`compromised`, `rotation`, `policy`, `lifecycle`, etc). `reasonDescription` may include a short operator note.
## Detached JWS workflow
1. Serialise `revocation-bundle.json` using the deterministic rules.
2. Compute SHA-256 digest; write to `revocation-bundle.json.sha256`.
3. Sign using ES256 (default) with the configured Authority signing key. The JWS header uses:
```json
{
"alg": "ES256",
"kid": "{signingKeyId}",
"provider": "{providerName}",
"typ": "application/vnd.stellaops.revocation-bundle+jws",
"b64": false,
"crit": ["b64"]
}
```
4. Persist the detached signature payload to `revocation-bundle.json.jws` (per RFC 7797).
Verification steps:
1. Validate `revocation-bundle.json` against the schema.
2. Re-compute SHA-256 and compare with `.sha256` (if present).
3. Resolve the signing key from JWKS (`/.well-known/jwks.json`) or the offline key bundle, preferring the provider declared in the JWS header (`provider` falls back to `default`).
4. Verify the detached JWS using the resolved provider. The CLI mirrors Authority resolution, so builds compiled with `StellaOpsCryptoSodium=true` automatically 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:
```bash
stellaops 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:
1. Prints the computed digest in `sha256:<hex>` format. Compare it with the exported `.sha256` artefact.
2. Confirms the detached JWS header advertises `b64: false`, captures the provider hint, and that the algorithm matches the Authority configuration (ES256 unless overridden).
3. 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](revocation-bundle-example.json) demonstrating a mixed export of token, subject, and client revocations. Use it as a reference for integration tests and tooling.
## Operations Quick Reference
- `stella auth revoke export` emits a canonical JSON bundle, `.sha256` digest, and detached JWS signature in one command. Use `--output` to write into your mirror staging directory.
- `stella auth revoke verify` validates a bundle using cached JWKS or an offline PEM key, honours the `provider` metadata embedded in the signature, and reports digest mismatches before distribution.
- `POST /internal/revocations/export` provides the same payload for orchestrators that already talk to the bootstrap API.
- `POST /internal/signing/rotate` rotates JWKS material without downtime; always export a fresh bundle afterward so downstream mirrors receive signatures from the new `kid`.
- Offline Kit automation should mirror `revocation-bundle.json*` alongside Concelier exports so agents ingest revocations during the same sync pass.