Native library reference. Extended CLI examples use
jacs-compat. This source reference can include unreleased changes; see the package guide for installation and MCP profiles for available tools.
Key Rotation
Key rotation is the process of replacing an agent's cryptographic keys while preserving the ability to verify documents signed with previous keys. JACS implements version-aware key management to support secure key lifecycle operations.
Why Key Rotation Matters
Key Compromise Recovery
When a private key is compromised, the agent must be able to:
- Generate new keys and continue operating
- Revoke trust in the compromised key
- Maintain verifiability of documents signed before the compromise
Cryptographic Agility
Cryptographic algorithms evolve. Key rotation enables:
- Migration from older algorithms to newer ones
- Transition to post-quantum cryptography when needed
- Algorithm upgrades without breaking existing signatures
Migrating Ed25519 Agents to pq2025
Native rotation without --algorithm preserves the current algorithm. Upgrade
an Ed25519 identity explicitly:
jacs-compat agent rotate-keys --algorithm pq2025
- An Ed25519 identity can replace its key with Ed25519 or explicitly upgrade
to
pq2025; the transition proof is signed by its previous key. - A
pq2025identity cannot downgrade to Ed25519. - Until it rotates, an Ed25519 agent keeps signing normally with Ed25519.
- Documents signed by the old Ed25519 key remain verifiable after migration (see Cross-Algorithm Verification in the Algorithm Selection Guide).
- Rotation supersedes the ES256 compatibility key binding: the binding
is signed by the native root, so after rotation ecosystem exports fail
with a "re-issue" error until
jacs-compat agent issue-compat-bindingruns under the new native root. - Rotation applies to the native root only — rotating the ES256 compatibility key itself is out of scope in P2.
Compliance Requirements
Many security standards require periodic key rotation:
- PCI-DSS mandates regular key changes
- SOC 2 requires key management policies
- NIST guidelines recommend rotation schedules
Agent Versioning
JACS uses a versioned identity model where each key rotation creates a new agent version.
Version Format
Agent identifiers follow the format: {agent_id}:{version_uuid}
- jacsId: The stable agent identity (UUID v4) - never changes
- jacsVersion: Current version UUID - changes on each update
- jacsPreviousVersion: Links to the prior version
- jacsOriginalVersion: The first version ever created
{
"jacsId": "550e8400-e29b-41d4-a716-446655440000",
"jacsVersion": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"jacsPreviousVersion": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"jacsOriginalVersion": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
}
Version Chain
Each version forms a linked chain back to the original:
Original (v1) <-- Previous (v2) <-- Current (v3)
| | |
key-A key-B key-C
This chain provides an audit trail of all key changes and allows verification of any version.
Version-Aware Verification
The critical insight enabling key rotation is that signatures contain both the agent ID and the version that created them.
Signature Structure
{
"jacsSignature": {
"agentID": "550e8400-e29b-41d4-a716-446655440000",
"agentVersion": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"publicKeyHash": "sha256-of-public-key-A",
"signingAlgorithm": "ring-Ed25519",
"signature": "base64-encoded-signature",
"date": "2024-01-15T10:00:00Z"
}
}
Key Resolution Process
When verifying a signature:
- Extract
agentVersionandpublicKeyHashfrom the signature - Look up the public key that was active for that version
- Verify the signature using that historical key
#![allow(unused)] fn main() { // Pseudocode for version-aware verification fn verify_signature(doc: &Document) -> Result<()> { let sig = &doc.jacs_signature; // Find the key that was active for this version let public_key = resolve_key_for_version( &sig.agent_id, &sig.agent_version, &sig.public_key_hash, )?; // Verify with the historical key verify_with_key(&doc, &sig, &public_key) } }
Key Lookup Priority
Native external-document verification uses the configured JACS_KEY_RESOLUTION
sources (default local,registry). An opted-in dns source fetches the exact
signed version's key over HTTPS and pins it to the raw-key DNS fingerprint, so
old documents verify through DNS only while their fingerprint and exact-version
key endpoint stay published.
Key Rotation Process
Step-by-Step Rotation
- Generate new key pair with the desired algorithm
- Create new agent version with updated key information
- Sign new version with old key (transition signature)
- Update DNS records to include new key fingerprint
- Store old public key for future verifications
CLI Commands
# Rotate keys with default algorithm
jacs-compat agent rotate-keys
# Rotate to post-quantum algorithm
jacs-compat agent rotate-keys --algorithm pq2025
# List key history (active and archived keys)
jacs-compat agent keys-list
# Repair config after a crash during rotation
jacs-compat agent repair
Transition Signature
During key rotation, JACS produces a cryptographic transition proof that binds the old key to the new key. This proof is embedded in the agent document as jacsKeyRotationProof:
{
"jacsKeyRotationProof": {
"transitionMessage": "JACS_KEY_ROTATION:{agent_id}:{old_key_hash}:{new_key_hash}:{timestamp}",
"signature": "base64-encoded-signature-with-old-key",
"signingAlgorithm": "ring-Ed25519",
"oldPublicKeyHash": "sha256-of-old-key",
"newPublicKeyHash": "sha256-of-new-key",
"timestamp": "2026-04-07T10:00:00Z"
}
}
The transition message is signed with the old private key before it is archived. This proves:
- The rotation was authorized by the holder of the previous key
- The old and new keys are cryptographically linked
- An attacker cannot forge a rotation without the old private key
Agent::verify_transition_proof() validates the detached old-key signature.
Registries and directories must use
Agent::verify_transition_proof_for_rotation(proof, expected_agent_id, old_key, new_key)
instead. That stricter API also binds the proof to the expected stable identity
and exact candidate new key, preventing proof transplantation.
Crash Recovery
JACS uses a write-ahead journal to recover from crashes during key rotation. Before rotation begins, a journal file is written to {key_directory}/.jacs_rotation_journal.json. The journal tracks the rotation stage:
started- Rotation initiatedkeys_rotated- New keys generated, old keys archivedagent_saved- New agent version saved to diskconfig_signed- Config re-signed with new key (journal deleted on success)
If the process crashes mid-rotation, the next agent load detects the journal and automatically repairs the config by re-signing it with the current key. No manual intervention is required for the common case.
For manual recovery: jacs-compat agent repair
Cross-Algorithm Rotation
An Ed25519 root can explicitly upgrade to pq2025. Rotation without an
override preserves the current algorithm; PQ-to-Ed25519 downgrade is rejected:
# Rotate from Ed25519 to post-quantum
jacs-compat agent rotate-keys --algorithm pq2025
#![allow(unused)] fn main() { // Rust API let result = advanced::rotate(&agent, Some("pq2025"))?; }
# Python
result = client.rotate_keys(algorithm="pq2025")
// Node.js
const result = await client.rotateKeys({ algorithm: 'pq2025' });
After cross-algorithm rotation, the config file's jacs_agent_key_algorithm field is updated atomically. Documents signed before the rotation remain verifiable using the archived old key.
Version transition
Rotation retains jacsId, assigns a new jacsVersion and jacsVersionDate,
links jacsPreviousVersion, and embeds jacsKeyRotationProof. A document signed
before rotation still identifies its original agent version and key hash.
Retain that independently trusted public key to verify the older document;
there is no on-document jacsKeyHistory revocation registry.
Trust Store with Version History
Retain independently trusted public keys for each exact agent_id:version.
UUID versions are identifiers, not ordered ranges; do not infer key validity
from UUID comparison.
DNS and key versions
Agent-specific native lookup accepts overlapping fingerprints for one agent and records for multiple agents. Each record uses the actual format:
v=jacs; jacs_agent_id=<agent-uuid>; alg=SHA-256; enc=hex; jac_public_key_hash=<raw-key-sha256>; uri=https://keys.example.com/jacs/v1/agents/<agent-uuid>/keys/<version-uuid>
A cross-origin URI requires an explicitly configured JACS_KEYS_BASE_URL.
Publish distinct raw-key fingerprints and exact-version endpoints during an
overlap. Conflicting URIs for the same agent and fingerprint are rejected.
jacs-compat agent dns generates the configured identity's record; it does not
publish changes or provide an --all-keys export. The DNS record does not
implement ver, valid_until or revocation semantics. Keep historical keys
available and enforce revocation in application policy. There is no universal
rotation interval of twice the TTL: account for caches and retained documents.
See DNS trust anchoring and the
key discovery guide
for signed-reference compatibility and verifier boundaries.
Security Considerations
Key Revocation
When a key is compromised:
- Revoke the key in the relying application's trust policy
- Update DNS to remove discovery records for the compromised key; DNS has no revocation-status field
- Reject revoked keys in verification policy; cryptographic signature validity alone does not enforce revocation
- Notify trusted peers if possible
Secure Deletion
After rotation:
- Old private keys should be securely deleted
- Only public keys are retained for verification
- Key metadata must be protected from modification
Best Practices
Rotation Schedule
- Regular rotation: Quarterly or annually for compliance
- Algorithm upgrade: When transitioning to stronger cryptography
- Incident response: Immediately after suspected compromise
Pre-Rotation Checklist
- Backup current agent state
- Verify all systems can handle new key format
- Plan DNS propagation time
- Notify dependent systems of upcoming change
Post-Rotation Checklist
- Verify new key is active
- Confirm old documents still verify
- Update DNS records
- Securely delete old private key
- Test signing with new key
-
Re-issue the compatibility key binding (
jacs-compat agent issue-compat-binding) if the agent has an ES256 compatibility key — ecosystem exports fail until the new native root signs a fresh binding
See Also
- Security Model - Overall security architecture
- Cryptographic Algorithms - Algorithm details
- DNS Verification - DNS-based identity verification