Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt

Use this file to discover all available pages before exploring further.

Rotation and versioning

Every crypto key in KnoxCall is versioned. New encryptions always use the latest active version; old ciphertext keeps decrypting against whatever version originally produced it. Rotation moves the active pointer forward without breaking anything that’s already encrypted.

The version lifecycle

v1 active   ──rotate──▶  v1 retired  +  v2 active   ──rotate──▶  v2 retired + v3 active  ──destroyVersion──▶  v1 destroyed
Each version is in one of three states:
  • active — used for new encryptions. Exactly one per key at any time.
  • retired — kept around so old ciphertext under that version still decrypts. As many as you’ve rotated.
  • destroyed — cryptographic erasure. The wrapped key bytes are wiped and the row stays as an audit marker. Ciphertext under a destroyed version is permanently unreadable.

Rotating a key

curl -X POST https://api.knoxcall.com/admin/crypto/keys/customer-pii/rotate \
  -H "Authorization: Bearer $KC_ADMIN_JWT" \
  -H "X-Tenant-ID: $TENANT_ID"
# → { "new_version": 2 }
What happens, atomically inside a single transaction:
  1. A new 256-bit key is generated and wrapped with the tenant master key.
  2. The new version row is inserted as active.
  3. The previous active version is flipped to retired.
  4. The key’s current_version pointer is updated.
encrypt calls from this point forward emit knoxcall:v2:... ciphertext. decrypt calls handle either v1 or v2 ciphertext seamlessly.

When to rotate

ReasonHow often
Routine hygiene (compliance)Every 90 days
After a personnel change with key accessImmediately
After a suspected compromiseImmediately + rewrap + destroy old version
Pre-emptive before destroying the active versionAlways (you can’t destroy active)
Rotating is cheap. Decrypting from old versions is also cheap. The only meaningful “cost” of holding many retired versions is audit-trail clutter.

Rewrap: migrate ciphertext to a new version

Rotating doesn’t re-encrypt existing data. Old ciphertext keeps decrypting against retired versions. To migrate it:
curl -X POST https://api.knoxcall.com/admin/crypto/keys/customer-pii/rewrap \
  -H "Authorization: Bearer $KC_ADMIN_JWT" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{ "ciphertext": "knoxcall:v1:G2x..." }'
# → { "ciphertext": "knoxcall:v2:K9p...", "key_version": 2 }
rewrap decrypts the old ciphertext with the retired version, re-encrypts with the active version, and returns the new ciphertext — the plaintext never crosses the wire. The operation runs entirely on KnoxCall’s side. The typical migration loop:
for each row in your_table where ciphertext_version < current_version:
  new_ct = POST /admin/crypto/keys/<name>/rewrap { ciphertext: row.ct }
  UPDATE your_table SET ct = new_ct WHERE id = row.id
After every row is rewrapped, you can safely destroy the old version.

Destroying a version (cryptographic erasure)

curl -X DELETE https://api.knoxcall.com/admin/crypto/keys/customer-pii/versions/1 \
  -H "Authorization: Bearer $KC_ADMIN_JWT" \
  -H "X-Tenant-ID: $TENANT_ID"
What this does:
  1. Refuses if deletion_allowed = false on the parent key (the prod safety latch).
  2. Refuses if the version is active — rotate first.
  3. Wipes the wrapped key bytes from the row, flips status to destroyed, records who and when.
Any ciphertext that named this version in its prefix is gone forever. There’s no off-site backup. This is the feature, not a bug — it’s the right-to-be-forgotten primitive.

deletion_allowed

By default, new keys ship with deletion_allowed = false on production tenants. Flipping it to true is a deliberate, audit-logged action that says “I’m about to destroy a version on purpose.” Pattern we recommend:
  1. Rewrap all data to the new active version.
  2. Confirm in a query that no row in your DB still has ciphertext at the version you’re about to destroy.
  3. Flip deletion_allowed = true.
  4. DELETE /versions/<old>.
  5. Flip deletion_allowed = false again.

What rotation doesn’t do

  • It doesn’t break existing decryption — old ciphertext keeps working until you destroy the old version.
  • It doesn’t re-encrypt existing data — that’s rewrap.
  • It doesn’t change the ciphertext format — same prefix, just a higher version number.
  • It doesn’t propagate to bundled-mode agents instantly — they pick up the new version on their next session-bundle refresh.

Key cache behaviour

Unwrapped key versions are cached in memory for 5 minutes after first use. Rotation invalidates the previous active entry; destroy invalidates the destroyed entry. You don’t need to worry about the cache day-to-day — it’s an internal performance optimisation, not something you manage.

Audit log markers

Audit actionMeans
transit.key.createA new key was minted (version 1 active)
transit.key.rotateNew version became active
transit.encrypt / transit.decryptSingle op (records key version, not plaintext)
transit.rewrapCiphertext was rewrapped from one version to another
transit.key.destroy_versionVersion was cryptographically erased

See also