npm package: @reykzett/agentid
Description: Cryptographic identity for AI agents — verifiable reputation, delegation chains, and agent-to-agent trust
Author: Reyk Zepper
Latest version: 0.1.0

# AgentID

Cryptographic identity for AI agents — verifiable reputation, delegation chains, and agent-to-agent trust.

AI agents have no verifiable identity. When an agent claims "I am Claude, assistant of Company X" — nobody can verify that. There is no trust, no reputation, no accountability. AgentID solves this with cryptographic identities: like SSL certificates, but for agents.

## Features

- **Self-certifying identity** — `did:key` addressing, no registry needed
- **Ed25519 signatures** — sign and verify any agent action
- **Delegation chains** — Human -> Agent A -> Agent B, each link cryptographically signed
- **Scoped authority** — action-based permissions with mandatory expiry
- **Revocation** — revoke delegations before expiry, checked across all verify paths
- **Attestations** — verifiable claims about agent behavior and capabilities
- **Private key encapsulation** — keys never exposed via public API
- **Tiny footprint** — 3 runtime dependencies, ~25KB total

## Install

```bash
npm install agentid
```

Requires Node.js >= 18.

## Quick Start

### Create an Identity

```typescript
import { createIdentity } from "agentid";

const { identity, signer } = createIdentity({
  type: "agent",
  name: "DeployBot",
  provider: "acme-corp",
});

console.log(identity.id);
// did:key:z6Mkr1...
```

### Sign and Verify an Action

```typescript
import { createIdentity, signAction, verify } from "agentid";

const { signer } = createIdentity({ name: "DeployBot" });

const action = await signAction(signer, {
  action: "deploy",
  target: "production",
  version: "1.2.3",
});

const result = verify(action);
console.log(result.valid); // true

// Tamper with the payload
action.payload.version = "9.9.9";
const tampered = verify(action);
console.log(tampered.valid); // false
console.log(tampered.error?.code); // "INVALID_SIGNATURE"
```

### Delegate Authority

```typescript
import { createIdentity, createDelegation, verifyDelegation } from "agentid";

const human = createIdentity({ type: "human", name: "Alice" });
const agent = createIdentity({ name: "DeployBot", controller: human.identity.id });

const delegation = await createDelegation({
  issuer: human.signer,
  subject: agent.identity.id,
  scope: {
    actions: ["deploy-code", "sign-contract"],
    delegatable: false,
  },
  expiresIn: 3600, // 1 hour
});

const result = verifyDelegation(delegation);
console.log(result.valid); // true
```

### Verify a Delegation Chain

```typescript
import {
  createIdentity,
  createDelegation,
  signAction,
  verifyDelegationChain,
} from "agentid";

// Human -> Agent A -> Agent B
const human = createIdentity({ type: "human", name: "Alice" });
const agentA = createIdentity({ name: "Orchestrator" });
const agentB = createIdentity({ name: "DeployBot" });

// Human delegates to Agent A (re-delegatable)
const certA = await createDelegation({
  issuer: human.signer,
  subject: agentA.identity.id,
  scope: { actions: ["deploy-code"], delegatable: true },
  expiresIn: 3600,
});

// Agent A delegates to Agent B
const certB = await createDelegation({
  issuer: agentA.signer,
  subject: agentB.identity.id,
  scope: { actions: ["deploy-code"], delegatable: false },
  expiresIn: 1800,
  proofs: [certA.payload.id],
});

// Agent B signs an action
const action = await signAction(agentB.signer, {
  action: "deploy-code",
  target: "production",
}, { delegation: certB });

// Verify the full chain
const proofStore = new Map([[certA.payload.id, certA]]);

const result = await verifyDelegationChain({
  action,
  delegation: certB,
  expectedRoot: human.identity.id,
  resolveProof: (id) => Promise.resolve(proofStore.get(id) ?? null),
});

console.log(result.valid); // true
console.log(result.delegationValid); // true
```

### Attestations

```typescript
import { createIdentity, createAttestation, verifyAttestation } from "agentid";

const reviewer = createIdentity({ type: "human", name: "Alice" });
const agent = createIdentity({ name: "DeployBot" });

const attestation = await createAttestation(
  reviewer.signer,
  agent.identity.id,
  { type: "behavior_rating", rating: 5, context: "Flawless deployment" },
  { expiresIn: 86400 },
);

const result = verifyAttestation(attestation);
console.log(result.valid); // true
```

### Revoke a Delegation

```typescript
import {
  createIdentity,
  createDelegation,
  signAction,
  verify,
  createRevocation,
  MemoryRevocationRegistry,
} from "agentid";

const human = createIdentity({ type: "human", name: "Alice" });
const agent = createIdentity({ type: "agent", name: "DeployBot" });

const delegation = await createDelegation({
  issuer: human.signer,
  subject: agent.identity.id,
  scope: { actions: ["deploy-code"], delegatable: false },
  expiresIn: 3600,
});

// Agent acts under delegation — valid
const action = await signAction(agent.signer, { action: "deploy-code" }, { delegation });
const registry = new MemoryRevocationRegistry();
console.log((await verify(action, { revocation: registry })).valid); // true

// Human revokes the delegation
const revocation = await createRevocation(human.signer, delegation.payload.id, {
  reason: "Agent compromised",
});
await registry.revoke(revocation);

// Same action now fails
console.log((await verify(action, { revocation: registry })).valid); // false
console.log((await verify(action, { revocation: registry })).error?.code); // "REVOKED_DELEGATION"
```

### Export and Restore an Identity

```typescript
import { createIdentity, decryptSigner } from "agentid";

const { identity, signer } = createIdentity({ name: "DeployBot" });

// Export (encrypts private key with passphrase)
const bundle = await signer.export("my-secret-passphrase");

// Later: restore from encrypted bundle
const restored = await decryptSigner(bundle, identity, "my-secret-passphrase");
console.log(restored.identity.id === identity.id); // true
```

## How It Works

A step-by-step walkthrough of the core flow: identity → delegation → action → verification → revocation.

### The Scenario

You're building a system where a human (Alice) authorizes an AI agent (DeployBot) to deploy code. Later, the agent gets compromised and Alice needs to revoke its access immediately — before the delegation expires.

### Step 1: Create Identities

```typescript
import { createIdentity } from "agentid";

const alice = createIdentity({ type: "human", name: "Alice" });
const deployBot = createIdentity({
  type: "agent",
  name: "DeployBot",
  controller: alice.identity.id,
});

console.log(deployBot.identity.id);
// did:key:z6Mk...
```

Each identity gets an Ed25519 key pair and a self-certifying `did:key` address. The private key is captured in a closure inside the `signer` object — it can never be read directly, only used to sign.

### Step 2: Delegate Authority

```typescript
import { createDelegation } from "agentid";

const delegation = await createDelegation({
  issuer: alice.signer,
  subject: deployBot.identity.id,
  scope: {
    actions: ["deploy-code"],
    delegatable: false,
  },
  expiresIn: 3600, // 1 hour
});
```

Alice creates a signed delegation certificate: "I authorize DeployBot to perform `deploy-code` for 1 hour." The Ed25519 signature proves it came from Alice. The scope limits what the agent can do. `delegatable: false` prevents the agent from passing authority to another agent.

### Step 3: Agent Acts Under Delegation

```typescript
import { signAction } from "agentid";

const action = await signAction(
  deployBot.signer,
  { action: "deploy-code", target: "production", version: "2.1.0" },
  { delegation },
);
```

DeployBot signs the action with its own key. The delegation certificate is attached, so any verifier can see both who acted and who authorized it.

### Step 4: Verify

```typescript
import { verify } from "agentid";

const result = await verify(action);
console.log(result.valid);           // true
console.log(result.delegationValid); // true
```

`verify()` checks everything in order:
1. Is DeployBot's signature over the action valid?
2. Is the attached delegation signed by Alice?
3. Does the delegation signer match the claimed issuer? (spoofing protection)
4. Is DeployBot the delegation's subject?
5. Is the delegation still within its expiry window?
6. Is the delegation revoked? (if a `RevocationChecker` is provided)
7. Does the scope cover the required capability? (if `requiredCapability` is set)

If any check fails, you get `{ valid: false, error: { code: "...", message: "..." } }`. Verify never throws — invalid input is a normal result, not an exception.

### Step 5: Revoke

```typescript
import { createRevocation, MemoryRevocationRegistry } from "agentid";

const registry = new MemoryRevocationRegistry();

const revocation = await createRevocation(
  alice.signer,
  delegation.payload.id,
  { reason: "Agent exhibited unexpected behavior" },
);
await registry.revoke(revocation);

const result = await verify(action, { revocation: registry });
console.log(result.valid);       // false
console.log(result.error?.code); // "REVOKED_DELEGATION"
```

Alice creates a signed revocation — cryptographically proving she revoked it. From this point, every `verify()` call against this registry rejects actions based on that delegation. To restore access, create a new delegation.

### Step 6: Delegation Chains (optional)

For multi-agent setups: Alice → Orchestrator → DeployBot.

```typescript
const orchestrator = createIdentity({ type: "agent", name: "Orchestrator" });

// Alice delegates to Orchestrator (re-delegatable)
const certA = await createDelegation({
  issuer: alice.signer,
  subject: orchestrator.identity.id,
  scope: { actions: ["deploy-code"], delegatable: true },
  expiresIn: 3600,
});

// Orchestrator delegates to DeployBot (terminal)
const certB = await createDelegation({
  issuer: orchestrator.signer,
  subject: deployBot.identity.id,
  scope: { actions: ["deploy-code"], delegatable: false },
  expiresIn: 1800,
  proofs: [certA.payload.id],
});

// Verify the full chain back to Alice
const proofStore = new Map([[certA.payload.id, certA]]);

const result = await verifyDelegationChain({
  action: await signAction(deployBot.signer, { action: "deploy-code" }, { delegation: certB }),
  delegation: certB,
  expectedRoot: alice.identity.id,
  resolveProof: (id) => Promise.resolve(proofStore.get(id) ?? null),
  revocation: registry, // checks revocation at EVERY level
});
```

Chain verification walks upward: DeployBot → Orchestrator → Alice. If any link is expired, revoked, or has an invalid signature, the entire chain fails.

### The Flow

```
Alice (Human)
  │
  ├── createIdentity() → did:key:z6MkAlice...
  │
  ├── createDelegation() → signed certificate
  │       │                  "DeployBot may deploy-code, 1h"
  │       ▼
  │   DeployBot (Agent)
  │       │
  │       ├── signAction() → signed action + delegation attached
  │       │
  │       ▼
  │   Verifier
  │       │
  │       ├── verify(action, { revocation }) → { valid: true/false }
  │       │
  │       └── Checks: Signature → Delegation → Expiry → Revocation → Scope
  │
  └── createRevocation() → immediately invalidates delegation
```

## API Reference

### Identity

| Function | Description |
|----------|-------------|
| `createIdentity(opts?)` | Create a new identity with fresh Ed25519 key pair |
| `resolveDidKey(did)` | Resolve a `did:key` to an `IdentityDocument` |
| `decryptSigner(bundle, identity, passphrase)` | Restore a `Signer` from an encrypted key bundle |
| `serializeIdentity(identity)` | Canonical JSON string of an identity |

### Signing & Verification

| Function | Description |
|----------|-------------|
| `signAction(signer, payload, opts?)` | Sign a payload with timestamp and optional delegation |
| `verify(action, opts?)` | Verify a `SignedAction` (signature, delegation, revocation) — async |
| `verifySignedPayload(signed)` | Verify the raw signature on a `SignedPayload` |

### Delegation

| Function | Description |
|----------|-------------|
| `createDelegation(opts)` | Create a signed, scoped, time-limited delegation |
| `verifyDelegation(delegation, opts?)` | Verify a single delegation certificate — async |
| `verifyDelegationChain(opts)` | Verify a full delegation chain back to root |

### Revocation

| Function | Description |
|----------|-------------|
| `createRevocation(signer, delegationId, opts?)` | Create a signed revocation for a delegation |
| `verifyRevocation(revocation)` | Verify a revocation entry's signature |

| Class | Description |
|-------|-------------|
| `MemoryRevocationRegistry` | In-memory revocation registry (testing / ephemeral) |
| `FilesystemRevocationRegistry` | Persistent registry in `~/.agentid/revocations/` |

All verification functions accept an optional `{ revocation: RevocationChecker }` to check revocation status.

### Attestation

| Function | Description |
|----------|-------------|
| `createAttestation(signer, subject, claim, opts?)` | Create a signed attestation about an agent |
| `verifyAttestation(attestation)` | Verify an attestation's signature and expiry |

### DID Utilities

| Function | Description |
|----------|-------------|
| `didFromPublicKey(pubkey)` | Create `did:key:z...` from 32-byte Ed25519 public key |
| `publicKeyFromDid(did)` | Extract raw public key from `did:key` |
| `parseDid(input)` | Parse and validate a DID string |
| `isValidDid(input)` | Non-throwing DID validation |

### Storage

| Class | Description |
|-------|-------------|
| `MemoryStore` | In-memory identity store (testing / ephemeral) |
| `FilesystemStore` | Persistent store in `~/.agentid/` |

Both implement the `IdentityStore` interface: `save()`, `load()`, `list()`, `delete()`.

## Core Concepts

### Addressing: `did:key`

Self-certifying, no registry needed. An Ed25519 public key is encoded as:

```
did:key:z + base58btc(0xed01 + 32-byte-pubkey)
```

The DID itself contains everything needed to verify signatures.

### Signer Abstraction

Private keys are captured in closures — never exposed via the public API. The only way to extract a key is through passphrase-encrypted export (PBKDF2 + AES-256-GCM).

### Delegation Certificates

UCAN-inspired, action-scoped delegation with mandatory expiry. Chainable: Human -> Agent A -> Agent B (if `delegatable: true`). Each link is signed by the issuer.

```
scope: {
  actions: ["deploy-code", "sign-contract"],
  resources: ["repo:org/myapp"],  // optional
  delegatable: false               // default
}
```

### Verification as Result

`verify()` returns a `VerificationResult` with a typed error union — it never throws for invalid signatures. That's a normal outcome, not an exception.

```typescript
type VerificationError =
  | { code: "INVALID_SIGNATURE"; message: string }
  | { code: "EXPIRED_DELEGATION"; message: string; expiredAt: string }
  | { code: "SCOPE_VIOLATION"; message: string; action: string; allowed: string[] }
  | { code: "CHAIN_BROKEN"; message: string; brokenAt: AgentDID }
  | { code: "REVOKED_DELEGATION"; message: string; delegationId: string; revokedAt: string }
  | { code: "INVALID_DID"; message: string };
```

## Security

- **Strict Ed25519** — `zip215: false` enforces RFC 8032 / FIPS 186-5 compliance, preventing small-order public key forgery
- **Signer/issuer consistency** — all verification paths check that the envelope signer matches the claimed issuer
- **Chain depth limit** — delegation chains capped at 32 levels to prevent circular reference attacks
- **Canonical signing** — RFC 8785 deterministic JSON ensures cross-platform signature reproducibility
- **Revocation support** — delegations can be revoked before expiry via signed `RevocationEntry`; all verify paths support optional revocation checking
- **Key encapsulation** — private keys never leave the `Signer` closure; export requires passphrase encryption

## Project Structure

```
src/
  index.ts              Public API
  types.ts              All type definitions
  errors.ts             Typed error classes
  version.ts            Protocol version
  codec/                Base58, hex, base64url, canonical JSON
  did/                  did:key encoding/decoding/parsing
  crypto/               Key generation, signing, verification
  identity/             Identity creation, resolution, serialization
  delegation/           Delegation creation, verification, chain walking
  revocation/           Revocation creation, verification, registries
  attestation/          Attestation creation, verification
  storage/              MemoryStore, FilesystemStore
test/                   178 tests across 22 files
examples/               5 runnable example scripts
```

## Development

```bash
npm install
npm test              # run 178 tests
npm run build         # ESM + CJS + DTS output
npm run typecheck     # strict TypeScript check
```

### Run Examples

```bash
npx tsx examples/01-create-identity.ts
npx tsx examples/02-sign-and-verify.ts
npx tsx examples/03-delegate-authority.ts
npx tsx examples/04-verify-delegation-chain.ts
npx tsx examples/05-revoke-delegation.ts
```

## Tech Stack

| Component | Choice | Why |
|-----------|--------|-----|
| Language | TypeScript (ESM + CJS dual) | Agent ecosystem is JS/TS-heavy |
| Crypto | `@noble/ed25519` + `@noble/hashes` | Audited, zero deps, ~20KB |
| Canonicalization | `canonicalize` (RFC 8785) | Deterministic JSON for signing, ~2KB |
| Build | `tsup` | ESM + CJS + DTS in one config |
| Test | `vitest` | Fast, native TS/ESM |

**Total runtime deps: 3 packages, ~25KB.**

## License

MIT