A platform key-management standard for services on CoreWeave that need AWS-managed key material for two purposes: encrypting tenant data at rest, and signing/verifying provenance records. Two key classes share one credential-and-delivery path. The encryption half is proven in advaita-product-drive; the signing half uses wanderland-core-pki and is exercised by foxfire and wisp. Services with either need adopt this rather than reinventing it.
Two key classes over one delivery path:
Wanderland::Activities::PKI, provisioned from SSM at boot and used to sign in-process. Verification resolves the signer's key from the record itself.Both are reached from a CoreWeave pod by the same projected-ServiceAccount-token web-identity role; both sit under one per-env CMK and one deletion-protection guardrail. Private material lives as an SSM SecureString encrypted under the CMK; public material is published in the clear, so verification never depends on KMS being alive.
kms:GenerateDataKey, or a local CSPRNG in the dev fallback) and use it directly as the S3 SSE-C customer key. The plaintext key is never persisted; S3 manages the per-object IV.EncryptionContext={tenantId}. The wrapped key + a key reference + the tenant id are stored on the object row.kms:Decrypt the wrapped key under the same EncryptionContext, then replay the plaintext key as the SSE-C key on the GET. A context mismatch fails Decrypt — the CMK fences one tenant's wrapped keys from another's.Wanderland::Activities::PKI holds keys as storage-native append-only records at :pki:keys:{name}, with generate / sign / verify / demote over a storage registry, RSA or EC P-256. Keys are ordinary crossings, not a separate ring.sig (over its own fields, including its identity) and trace (the predecessor's sig). Verification resolves each crossing's own identity's public key, and the seal boundary emits a roll-up root commitment. So every signed record already names the key that signed it — per-signer resolution is the model.!SsmLookup resolver (with_decryption, from wanderland-aws-pack) populates :pki: at boot. On CoreWeave the resolver authenticates with the same web-identity role the encryption path uses. Public halves are published in the clear (plain SSM String / config), so verification does not depend on KMS or the CMK.The requirement: rotating a signing key must never make old chains unverifiable. Old public keys are retained forever; superseded private keys are destroyed.
Activities::PKI.verify reads records.last at :pki:keys:{name} — only the newest key. generate is idempotent-skip; there is no rotate verb. So placing new material at a fixed name orphans every prior signature, and demote alone (verify-only deprecation of the same key) does not address it.verify walks the append-only key history at :pki:keys:{name} and accepts a signature if any historical public_pem verifies it. The history is already written by generate/demote; verification stops at the newest record only because read_key returns records.last.rotate verb appends fresh material at an existing name, leaving prior records in place. sign uses records.last (the new key); old chains verify against the retained old public keys.demote additionally scrubs private_pem, keeping public_pem — public halves immortal, superseded private halves gone.:pki: → new records sign under it while every prior chain still verifies. Destroy the superseded private half; never delete a published public half.Activities::PKI (a verified core module), so it is a tracked core change, not a per-service workaround.sts.amazonaws.com) → AssumeRoleWithWebIdentity → coreweave-<service>-kms-<env>. CoreWeave has no pod-identity webhook, so this is the one credential path both key classes use. The wanderland-aws-pack SsmLookup resolver, with its credential-refresh handling, is the client.ensure_kms step at environment bootstrap creates the per-env CMK alias and role; it extends to provision the signing-key SSM parameters (private SecureString + public String) and their guardrails. In Advaita Drive this lives in advaita-deploy/projects/<product>/scripts/setup.sh.ScheduleKeyDeletion / DisableKey) and deleting a published signing public key. Both are guarded.Required for any environment holding real encrypted or signed data, and a production go-live prerequisite:
kms:ScheduleKeyDeletion and kms:DisableKey for the CMK (7–30 day deletion window gives time to cancel), and on ssm:DeleteParameter for the published public-key path.kms:Decrypt grant intact — revoking it fails Decrypt until restored.Deleting a published signing public key destroys verifiability exactly as deleting the CMK destroys ciphertext — the same guardrail family, extended to the SSM public-key path. These live in the deploy repo (ensure_kms + key/parameter policy) and AWS, not in the application.
Encryption:
GenerateDataKey / Decrypt with EncryptionContext={tenantId} on every call.wrapped_data_key + key_ref + tenant_id on the object row, plus a sidecar recovery blob.Signing:
Activities::PKI and the merkle DAG rather than a bespoke key store or a single-identity verifier.!SsmLookup; publish the public half; hold no static k8s Secret for private signing material.Both:
Encryption-at-rest protects against object-store or credential compromise, not against the authenticated client. For browser-direct SSE-C the raw per-object key is handed to the client on every part PUT and GET, so it is not a control against a user reading their own tenant's data — the intended model. A service that must withhold plaintext from the client needs a server-side proxy or a different scheme.
Signing proves authorship and ordering, not confidentiality. In-cluster producers that post unsigned records (for example foxfire's /plan and /placement) sit outside the signed chain and rely on network trust; whether each such producer joins the chain is a per-producer decision now that the signing rails exist.
Activities::PKI.verify walks key history; add a rotate verb; demote scrubs private_pem. See wanderland-core-pki.!Env FOXFIRE_SEAL_KEY + auto-gen + a single service:foxfire identity onto the storage-backed :pki: path provisioned by !SsmLookup, using a history-walking verifier so rotation applies. See advaita-service-foxfire.SIGNING-KEY-MOUNT TODO), not an env-var Secret.ensure_kms to provision the signing-key SSM parameters and the public-key-path guardrails; one per-env CMK covers both key classes.!SsmLookup is wired for boot-time :pki: population in foxfire/wisp today — the aws-pack ships the resolver, but the services currently provision via !Env/!Fixture.