Skip to main content

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

  1. Validate trust material and policy bundle shape.
  2. Verify signatures with the configured bundle-signing public key.
  3. Review the diff for route, issuer, key, source, freshness, and signer-class changes.
  4. Preserve unrelated issuer/key/source entries unless an explicit removal is intended.
  5. Update only the mounted bundle objects.
  6. 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

StepExpected
Unknown client key before publishdeny
Publish reviewed, signed trust material and policynew client accepted, no image rebuild
Signed business request after publishallow
Duplicate replaydeny (jti_replay)
Tampered pathdeny (request_binding_mismatch)
Wrong audiencedeny
Wrong routedeny
Removed source after refreshdeny

Freshness during refresh

  • realtime routes require a current bundle view and fail closed when the mounted file view is stale or unknown.
  • bounded routes allow a known bundle age up to max_staleness_seconds, then fail closed.
  • offline-ok routes explicitly allow stale/offline bundle use for lower-risk cases.

Example deployment · → Capability → example map