|
1 | | -# Invariants — Auto-synced from registry@5d14280 |
| 1 | +# Invariants — Auto-synced from registry@ea7486a |
2 | 2 | # DO NOT EDIT |
3 | 3 |
|
4 | 4 | # Platform Invariants |
@@ -241,3 +241,52 @@ about it. |
241 | 241 |
|
242 | 242 | *Source: ADR-029 (chief-architect draft, 2026-09-05), raised during the |
243 | 243 | Dictionary Games UX and progression review.* |
| 244 | + |
| 245 | +## INV-017 — A Key Vault operation proceeds only against a grant issued for that exact operation |
| 246 | + |
| 247 | +**Applies to:** the Key Vault as sole verifier, the Identity Node as sole |
| 248 | +issuer, and every client that reaches the Key Vault. |
| 249 | + |
| 250 | +Every Key Vault request **fails closed** unless it carries an Identity-issued |
| 251 | +token satisfying all of: |
| 252 | + |
| 253 | +- **Scalar `aud` of `key-vault`.** Not an array, not absent, not another class. |
| 254 | +- **Exact subject and path match.** The grant names what it acts on; a grant for |
| 255 | + one subject or path is refused for any other. |
| 256 | +- **Exact operation match.** The grant names one operation. A grant is never a |
| 257 | + general capability, and an operation not named in it is refused. |
| 258 | +- **Valid `iat`, `nbf` and `exp`.** |
| 259 | + |
| 260 | +**No other credential substitutes.** Session, offline, provisioning, machine and |
| 261 | +array-audience tokens are each refused, whatever else about them is valid. This |
| 262 | +is INV-015's cross-class rule applied at the Vault's door, stated separately |
| 263 | +because the Vault is where substitution would be most costly. |
| 264 | + |
| 265 | +Every **destructive** operation additionally requires: |
| 266 | + |
| 267 | +- **The expected manifest version**, bound in the grant, so a captured grant |
| 268 | + cannot be replayed against a manifest that has moved. |
| 269 | +- **A transaction ID**, bound in the grant. |
| 270 | +- **A grant lifetime of at most five minutes.** |
| 271 | +- **A single-use `jti`, consumed atomically before any mutation.** Consumed |
| 272 | + before, not after: a consume-after ordering makes the mutation the thing that |
| 273 | + races. |
| 274 | + |
| 275 | +**On any failure — replay, mismatch, expiry, or a missing claim — stored key |
| 276 | +material is unchanged.** No partial mutation, no best-effort completion. A |
| 277 | +refused request must be indistinguishable from one that never arrived, as far |
| 278 | +as stored key material is concerned. |
| 279 | + |
| 280 | +**Enforced by one shared contract test suite**, run against **both** Identity's |
| 281 | +grant issuance and the Key Vault's verification. Two suites that agree today are |
| 282 | +the failure mode this replaces: the contract is only real where a single set of |
| 283 | +cases binds issuer and verifier to the same answers. A repository that |
| 284 | +implements either side without running that suite has not satisfied this |
| 285 | +invariant. |
| 286 | + |
| 287 | +*Source: Owner ruling, 2026-09-05, following the removal of an invented |
| 288 | +`key-vault` grant before it shipped. Wire contract: |
| 289 | +`sparxstar-contracts-registry`, |
| 290 | +`Contracts/sparxstar-3iatlas-identity-node/`. Shared conformance vectors: |
| 291 | +`Contracts/sparxstar-3iatlas-identity-node/conformance/vectors.json` in that |
| 292 | +registry — the same path the assertion rationale cites.* |
0 commit comments