Bundle refresh & key lifecycle
Bundle refresh is the runtime counterpart of Add a client without redeploying: a running adapter picks up new signed trust material and signed policy without an image rebuild, upstream change, or adapter code change.
The two paths, separated on purpose
The deployment path owns runtime resources. The refresh path owns bundle material and the refresh signal that makes the adapter read new mounted files. Keeping them separate avoids accidental infrastructure changes during a trust update.
What a refresh should do
- Validate trust material and policy bundle shape.
- Verify signatures with the configured bundle-signing public key.
- Review the diff for route, issuer, key, source, freshness, and signer-class changes.
- Preserve unrelated issuer/key/source entries unless an explicit removal is intended.
- Update only the mounted bundle objects.
- Trigger an adapter refresh or rollout so the projected files are read again.
It should not change the adapter image, containers, volumes, replicas, Service, upstream, networking, broker configuration, or cloud infrastructure.
Add a client
trustplane bundle merge-source appends a client public key to trust material and a matching
source rule to the selected route:
trustplane bundle merge-source \
--trust-material trust-material.json \
--policy-bundle trustplane.bundle.json \
--out-trust-material trust-material.merged.json \
--out-policy-bundle trustplane.bundle.merged.json \
--source-type oidc_jwks \
--issuer https://issuer.example.com/external-jwks \
--trust-domain example.external \
--kid example-client-2 \
--public-key "$CLIENT_PUBLIC_KEY_B64URL" \
--route-id example.orders.read \
--subject-exact external:jwks:example-client-2 \
--required-key-binding software
Then sign the merged outputs before using them with adapter signature enforcement:
trustplane bundle sign \
--trust-material trust-material.merged.json \
--out trust-material.merged.signed.json \
--signer-issuer local://bundle-signer \
--kid bundle-signing-key-1 \
--private-key "$BUNDLE_SIGNING_PRIVATE_KEY_B64URL"
trustplane bundle sign \
--policy-bundle trustplane.bundle.merged.json \
--out trustplane.bundle.merged.signed.json \
--signer-issuer local://bundle-signer \
--kid bundle-signing-key-1 \
--private-key "$BUNDLE_SIGNING_PRIVATE_KEY_B64URL"
Remove or revoke a local source
trustplane bundle remove-source removes a trusted key and matching route source selector while
preserving unrelated trust material and route policy. Because removal is destructive, it requires
explicit confirmation:
trustplane bundle remove-source \
--trust-material trust-material.json \
--policy-bundle trustplane.bundle.json \
--out-trust-material trust-material.removed.json \
--out-policy-bundle trustplane.bundle.removed.json \
--issuer https://issuer.example.com/external-jwks \
--trust-domain example.external \
--kid example-client-2 \
--route-id example.orders.read \
--subject-exact external:jwks:example-client-2 \
--confirm-remove \
--revoke
--revoke appends local revocation metadata for the removed key/source. OSS Auth denial comes
from refreshing the adapter with the replacement signed bundle. This does not add a hosted
revocation workflow, database, approval system, or managed distribution service.
Smoke matrix
| Step | Expected |
|---|---|
| Unknown client key before publish | deny |
| Publish reviewed, signed trust material and policy | new client accepted, no image rebuild |
| Signed business request after publish | allow |
| Duplicate replay | deny (jti_replay) |
| Tampered path | deny (request_binding_mismatch) |
| Wrong audience | deny |
| Wrong route | deny |
| Removed source after refresh | deny |
Freshness during refresh
realtimeroutes require a current bundle view and fail closed when the mounted file view is stale or unknown.boundedroutes allow a known bundle age up tomax_staleness_seconds, then fail closed.offline-okroutes explicitly allow stale/offline bundle use for lower-risk cases.