Identity and keys
Every Covenant install owns a single ed25519 keypair. The same key signs capability grants, signs Solana settlement transactions, and fronts the daemon's issuer field on audit events and memory records. There is no second key system.
Persistence
The keypair is stored as the raw 32-byte seed at $COVENANT_HOME/identity/local.key. The daemon writes the file with mode 0600 (owner read/write, no group, no other). On startup the daemon refuses to load a key file with broader permissions; tighten it with chmod 0600 if you ever see this error.
The matching public key is derivable from the seed and is attached to every AgentId the daemon emits as the issuer.
The AgentId shape
AgentId {
display: "user@local", // human-readable label
pubkey: [u8; 32] // raw ed25519 pubkey
}The display half is set on first daemon start (default user@<hostname>). The pubkey half is derived from the seed and is the cryptographically meaningful identifier — display strings can collide; pubkeys cannot.
Signing helpers
The covenant-identity crate exposes:
LocalIdentity::generate(display)— fresh ed25519 keypair plus display string.LocalIdentity::load_or_create(path, display)— loads from disk if present, else generates and persists.LocalIdentity::sign(&self, message)— signs arbitrary bytes.verify_with_pubkey(pubkey, message, signature)— read-side verification; returnsResult<(), SignatureError>.verifying_key_from_bytes(pubkey)— converts a 32-byte pubkey to aned25519_dalek::VerifyingKey.
The signing helpers are deliberately small: they do not prescribe a canonical encoding for the message. The capability layer (covenant-permissions) supplies its own encoder and feeds the signing helpers the resulting bytes.
Same key, two roles
The same ed25519 keypair is used to:
- sign capability tokens (
SignedCapability); - sign Solana settlement transactions when the daemon flushes receipts on-chain;
- appear as the
issueron audit events and theowneron memory records.
Reusing the key across roles keeps the operator's mental model small. The cost is that compromise of the key compromises all three; the benefit is that there is only one thing to back up, rotate, and protect.
Rotation
Rotation is deliberate and disruptive. Re-issuing the keypair invalidates every signed capability written under the old key — verifying a token after rotation will fail because the expected granter pubkey no longer matches the daemon's live key. Plan for the re-grant when you rotate.
The procedure:
- Stop the daemon.
- Move the existing key file aside (or delete it once you are sure no other state references it).
- Start the daemon. It will generate a fresh key on first run.
- Re-grant every capability your agents need. The audit log will record both the rotation (implicitly, via the absence of the old issuer in subsequent events) and the new grants.
- Update any external systems that bound to the old pubkey (Solana program authority records, third-party integrations).
Keys at scale
Today Covenant runs one keypair per machine. A future shape will allow multiple subordinate keys for delegated agents (each subordinate signed by the root), so an agent compromise does not require a full root rotation. Until then, treat the single keypair the way you would treat your shell's SSH key.
Related
- Capability tokens — everything that depends on the signing helpers.
- Settlement — the on-chain side that signs with the same key.
- Security model — the file-permissions, threat-model context.