The keystore module provides functions for generating, loading, and using Ed25519 keypairs. These keypairs are used for agent authentication via challenge-response during AgentDoor registration.
Overview
AgentDoor uses Ed25519 public-key cryptography for agent authentication:
- Keypairs are generated using the tweetnacl library
- Private keys are stored securely with 0o600 permissions (owner read/write only)
- Public keys are shared with services during registration
- Signatures prove the agent owns the private key corresponding to a public key
Functions
generateKeypair()
Generate a fresh Ed25519 keypair.
import { generateKeypair } from "@agentdoor/sdk";
const keypair = generateKeypair();
console.log("Public key:", keypair.publicKeyBase64);
A new keypair object with both raw Uint8Array and base64-encoded representations.
saveKeypair()
Save a keypair to disk as JSON. Creates parent directories if they don’t exist.
import { generateKeypair, saveKeypair } from "@agentdoor/sdk";
const keypair = generateKeypair();
saveKeypair(keypair, "~/.agentdoor/keys.json");
The file path where the keypair will be stored. Supports ~ for home directory expansion. The file will be created with 0o600 permissions (owner read/write only).
loadKeypair()
Load a keypair from disk. Returns null if the file doesn’t exist. Throws if the file exists but is malformed.
import { loadKeypair } from "@agentdoor/sdk";
const keypair = loadKeypair("~/.agentdoor/keys.json");
if (keypair) {
console.log("Loaded keypair:", keypair.publicKeyBase64);
} else {
console.log("No keypair found at path");
}
The file path to load the keypair from. Supports ~ for home directory expansion.
The loaded keypair, or null if the file doesn’t exist.
Throws:
- Error if the file exists but contains invalid JSON
- Error if the file is missing required fields (
publicKey, secretKey)
- Error if the key lengths are incorrect (public key must be 32 bytes, secret key must be 64 bytes)
loadOrCreateKeypair()
Load an existing keypair from disk, or generate and save a new one if none exists. This is the recommended way to manage keypairs in most applications.
import { loadOrCreateKeypair } from "@agentdoor/sdk";
const keypair = loadOrCreateKeypair("~/.agentdoor/keys.json");
console.log("Agent public key:", keypair.publicKeyBase64);
The file path for the keypair. Defaults to ~/.agentdoor/keys.json.
The loaded or newly generated keypair.
signMessage()
Sign an arbitrary message with the agent’s secret key. Returns the detached signature as a base64 string.
import { loadOrCreateKeypair, signMessage } from "@agentdoor/sdk";
const keypair = loadOrCreateKeypair();
const message = "agentdoor:challenge:abc123";
const signature = signMessage(message, keypair.secretKey);
console.log("Signature:", signature);
The message to sign. Can be any string.
The Ed25519 secret key (64 bytes) to sign with.
Base64-encoded detached Ed25519 signature (64 bytes encoded).
verifySignature()
Verify a detached Ed25519 signature against a message and public key.
import { verifySignature, decodeBase64 } from "@agentdoor/sdk";
const message = "agentdoor:challenge:abc123";
const signatureBase64 = "...";
const publicKeyBase64 = "...";
const publicKey = decodeBase64(publicKeyBase64);
const isValid = verifySignature(message, signatureBase64, publicKey);
console.log("Signature valid:", isValid);
The original message that was signed.
Base64-encoded signature to verify.
The Ed25519 public key (32 bytes) to verify against.
true if the signature is valid, false otherwise.
Type Definitions
Keypair
In-memory keypair with raw Uint8Arrays alongside base64 representations.
interface Keypair {
publicKey: Uint8Array; // 32-byte Ed25519 public key
secretKey: Uint8Array; // 64-byte Ed25519 secret key
publicKeyBase64: string; // Base64-encoded public key
secretKeyBase64: string; // Base64-encoded secret key
}
StoredKeypair
Serialized keypair format stored on disk.
interface StoredKeypair {
publicKey: string; // Base64-encoded 32-byte public key
secretKey: string; // Base64-encoded 64-byte secret key
createdAt: string; // ISO-8601 timestamp
}
Constants
DEFAULT_KEY_PATH
Default keypair file path: ~/.agentdoor/keys.json
import { DEFAULT_KEY_PATH } from "@agentdoor/sdk";
console.log("Default key path:", DEFAULT_KEY_PATH);
// Output: ~/.agentdoor/keys.json
Security Considerations
File Permissions
Keypair files are automatically created with restrictive permissions:
- Unix/Linux/macOS: 0o600 (owner read/write only)
- Private keys should never be shared or committed to version control
- The
.agentdoor directory should be added to .gitignore
Key Storage
For production deployments, consider:
- Using environment variables or secret management systems
- Encrypting keypair files at rest
- Rotating keys periodically
- Using hardware security modules (HSMs) for high-security scenarios
Challenge-Response Flow
During registration, AgentDoor uses challenge-response authentication:
- Agent sends its public key to the service
- Service generates a random nonce and challenge message
- Agent signs the challenge with its private key
- Service verifies the signature using the public key
- If valid, the service issues credentials
This proves the agent controls the private key without ever transmitting it.
Examples
Generate and Save a Keypair
import { generateKeypair, saveKeypair } from "@agentdoor/sdk";
const keypair = generateKeypair();
saveKeypair(keypair, "~/.agentdoor/keys.json");
console.log("Generated keypair:");
console.log(" Public key:", keypair.publicKeyBase64);
console.log(" Saved to: ~/.agentdoor/keys.json");
Load or Create Keypair (Recommended)
import { loadOrCreateKeypair } from "@agentdoor/sdk";
// This will load existing keypair or generate a new one
const keypair = loadOrCreateKeypair("~/.agentdoor/keys.json");
console.log("Agent identity:", keypair.publicKeyBase64);
Sign a Message
import { loadOrCreateKeypair, signMessage } from "@agentdoor/sdk";
const keypair = loadOrCreateKeypair();
// Sign a challenge during registration
const challenge = "agentdoor:challenge:1234567890:abc123";
const signature = signMessage(challenge, keypair.secretKey);
console.log("Challenge:", challenge);
console.log("Signature:", signature);
Verify a Signature
import {
generateKeypair,
signMessage,
verifySignature
} from "@agentdoor/sdk";
const keypair = generateKeypair();
const message = "Hello, AgentDoor!";
// Sign the message
const signature = signMessage(message, keypair.secretKey);
// Verify the signature
const isValid = verifySignature(message, signature, keypair.publicKey);
console.log("Signature valid:", isValid); // true
// Try verifying with wrong message
const isInvalid = verifySignature("Different message", signature, keypair.publicKey);
console.log("Wrong message valid:", isInvalid); // false
Custom Keypair Location
import { loadOrCreateKeypair } from "@agentdoor/sdk";
import * as path from "path";
// Use a custom location for keypairs
const customPath = path.join(process.cwd(), ".keys", "agent.json");
const keypair = loadOrCreateKeypair(customPath);
console.log("Loaded keypair from:", customPath);
Handle Loading Errors
import { loadKeypair } from "@agentdoor/sdk";
try {
const keypair = loadKeypair("~/.agentdoor/keys.json");
if (keypair) {
console.log("Loaded existing keypair");
} else {
console.log("No keypair found - need to generate one");
}
} catch (error) {
console.error("Failed to load keypair:", error.message);
// Handle corrupted or invalid keypair file
}
Integration with AgentDoor
The AgentDoor class uses keystore functions internally:
import { AgentDoor } from "@agentdoor/sdk";
// AgentDoor automatically calls loadOrCreateKeypair internally
const agent = new AgentDoor({
keyPath: "~/.agentdoor/keys.json"
});
// Access the public key
console.log("Agent public key:", agent.publicKey);
// Connect to a service (keypair is used for challenge-response)
const session = await agent.connect("https://api.example.com");