agent-vault 0.1.0

Zero-trust credential manager for AI agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# agent-vault: Product Requirements Document

## Overview

agent-vault is an open-source, zero-trust credential manager designed for AI agents. Secrets are encrypted locally and synced via Git. There is no server, no SaaS dependency, and no third-party trust required. The Git repository serves as the single source of truth for credentials, access policy, agent identity, and disaster recovery.

GitHub (or any Git provider) is treated as an untrusted encrypted blob store. All encryption and decryption happens locally. Even full compromise of the repository exposes nothing but encrypted data.

---

## Problem Statement

AI agents increasingly need access to APIs, databases, and third-party services. Today, credentials for these agents are managed through ad hoc methods: hardcoded values, `.env` files, or manual configuration per deployment. There is no standardized, agent-native approach to credential management that provides:

- Unique agent identity with scoped access
- Encrypted, version-controlled credential storage
- Zero-infrastructure self-hosted operation
- Owner-controlled recovery when agent keys are lost

---

## Design Principles

1. **Zero trust toward the Git provider.** All data stored in the repository is encrypted. The repo could be made public without exposing secrets.
2. **Self-contained.** The repository holds everything needed for management and recovery: encrypted secrets, agent public keys, escrowed private keys, and access policy.
3. **No infrastructure.** No daemon, no server, no database. The tool is a CLI and library that interacts with Git.
4. **Owner authority.** The repo owner (a human) has full governance. They provision agents, grant access, and can recover any agent's credentials via key escrow.
5. **Agent autonomy at runtime.** Once provisioned, agents retrieve and decrypt their own credentials without human intervention.

---

## Architecture

### Trust Model

The security model rests on two foundations:

- **The owner's private key.** This is the master key for the system. It never enters the repository. It lives on the owner's machine (file, OS keychain, hardware key, etc.). Loss of this key requires reprovisioning the entire vault.
- **Each agent's private key.** Generated during agent provisioning. Stored locally on the machine where the agent runs. Also stored in the repository as an escrow copy encrypted with the owner's public key, enabling recovery.

GitHub/Git sees only encrypted blobs and plaintext policy metadata. Compromise of the repository without the owner's key or an agent's private key yields no usable data.

### Key Escrow Model

When an agent is created, its private key is encrypted with the owner's public key and committed to the repository. This means:

- The owner can recover any agent's private key using their own key
- The owner can impersonate any agent (this is intentional and mirrors the sysadmin/service-account trust model: the person who provisioned the agent already has full authority)
- Agent recovery does not require re-encryption of all secrets; the original key can simply be restored to a new environment

### Encryption

The tool should use [age](https://age-encryption.org/) as the encryption layer. age is modern, audited, simple, and supports multi-recipient encryption natively.

- Secrets are encrypted using the `age` format
- Each secret is encrypted for all authorized agents (multi-recipient)
- Escrow files are encrypted for the owner (single-recipient)
- The Go reference implementation (`filippo.io/age`) or Rust implementation (`rage`) are preferred for the core tool
- Python bindings (`pyrage`) should be used for the SDK/library

---

## Repository Structure

```
.agent-vault/
  config.yaml              # vault configuration (age version, defaults)
  owner.pub                # owner's age public key (plaintext)
  manifest.yaml            # access control policy (plaintext, see below)
  agents/
    seo-crawler/
      public.key           # agent's age public key (plaintext)
      private.key.escrow   # agent's private key, encrypted with owner.pub
    billing-agent/
      public.key
      private.key.escrow
  secrets/
    stripe/
      api-key.enc          # encrypted secret value
      api-key.meta         # plaintext metadata (see below)
      webhook-secret.enc
      webhook-secret.meta
    postgres/
      connection-string.enc
      connection-string.meta
  .gitignore               # blocks any unencrypted key material
```

### manifest.yaml (Plaintext Access Policy)

```yaml
version: 1
owners:
  - name: eric
    public_key: owner.pub

agents:
  - name: seo-crawler
    groups: [stripe]
  - name: billing-agent
    groups: [stripe, postgres]

groups:
  - name: stripe
    secrets:
      - stripe/api-key
      - stripe/webhook-secret
  - name: postgres
    secrets:
      - postgres/connection-string
```

This file contains no secret material. It defines which agents can access which credential groups. Changes to this file should be the mechanism for granting or revoking access.

### Secret Metadata Files (Plaintext)

```yaml
name: stripe-api-key
group: stripe
created: 2026-02-20T14:30:00Z
rotated: 2026-02-25T10:00:00Z
expires: 2026-08-20T14:30:00Z
authorized_agents: [seo-crawler, billing-agent]
```

Metadata enables browsing and auditing the vault without decrypting anything. The tool should warn when credentials are approaching expiration.

---

## CLI Specification

### Initialization

```bash
agent-vault init [directory]
```

- Creates the `.agent-vault/` directory structure
- Generates the owner's age keypair
- Saves the owner's private key to `~/.agent-vault/owner.key` with `chmod 600`
- Commits the owner's public key and initial config to the repo
- Generates `.gitignore` with aggressive patterns to block unencrypted key material
- Installs a Git pre-commit hook that scans for unencrypted key material and blocks the commit if found
- Prints a warning that the owner key is the master recovery key and should be backed up

### Agent Management

```bash
agent-vault add-agent <name>
```

- Generates a new age keypair for the agent
- Saves the agent's private key to `~/.agent-vault/agents/<name>.key` with `chmod 600`
- Encrypts the agent's private key with the owner's public key and commits as `private.key.escrow`
- Commits the agent's public key to the repo
- Adds the agent to `manifest.yaml` with no group access (access must be explicitly granted)

```bash
agent-vault remove-agent <name>
```

- Removes the agent from `manifest.yaml`
- Removes the agent's key files from the repo
- Re-encrypts all secrets the agent previously had access to (excluding the removed agent's key)
- Prints a warning listing all credentials that should be rotated at the source, since the removed agent previously had access to the decrypted values

```bash
agent-vault list-agents
```

- Lists all agents with their group memberships

### Access Control

```bash
agent-vault grant <agent-name> <group>
```

- Adds the agent to the specified group in `manifest.yaml`
- Re-encrypts all secrets in that group to include the new agent as a recipient
- Commits the changes

```bash
agent-vault revoke <agent-name> <group>
```

- Removes the agent from the specified group in `manifest.yaml`
- Re-encrypts all secrets in that group without the revoked agent
- Commits the changes
- Prints a warning that the revoked agent previously had access to these secrets and they should be rotated at the source

### Secret Management

```bash
agent-vault set <path> <value> [--agents agent1,agent2] [--group group-name]
```

- Encrypts the value for all authorized agents (per manifest or per explicit flag)
- Creates/updates the `.enc` file and `.meta` file
- Commits to the repo

```bash
agent-vault set <path> --from-file <filepath>
```

- Same behavior, but reads the secret value from a file (useful for certificates, multi-line values)

```bash
agent-vault get <path>
```

- Pulls latest from Git
- Decrypts the secret using the caller's private key (owner or agent)
- Returns the plaintext value to stdout (never writes to disk)

```bash
agent-vault list [--group group-name]
```

- Lists all secrets with metadata (name, group, last rotated, expiration, authorized agents)
- Does not decrypt anything

```bash
agent-vault check
```

- Audits the vault for issues: expiring credentials, agents with no access, orphaned secrets, manifest inconsistencies

### Recovery

```bash
agent-vault recover-agent <name>
```

- Decrypts the agent's escrowed private key using the owner's key
- Generates a new keypair for the agent
- Re-encrypts all secrets for the new agent key
- Creates a new escrow file
- Commits the changes
- Outputs the new private key path for deployment

```bash
agent-vault restore-agent <name> --to <path>
```

- Decrypts the escrowed private key using the owner's key
- Writes the original private key to the specified path
- No re-encryption needed; the agent resumes with its original identity

### Multi-Owner (v2)

```bash
agent-vault add-owner <name> --key <path-to-public-key>
```

- Adds an additional owner public key
- Re-encrypts all escrow files for all owners
- Future: support threshold-based recovery (e.g., 2-of-3 owners required)

---

## Agent SDK / Library

Beyond the CLI, the tool should ship as a library that agents can import directly. Priority languages:

1. **Python** (highest priority due to agent ecosystem overlap)
2. **Node.js / TypeScript**
3. **Rust** (if the core is written in Rust)

### Python SDK Example

```python
from agent_vault import Vault

vault = Vault(
    repo_path="/path/to/vault",      # or a Git remote URL
    key_path="~/.agent-vault/agents/seo-crawler.key"
)

# Pull latest and decrypt
stripe_key = vault.get("stripe/api-key")

# Use it
stripe.api_key = stripe_key
```

The SDK should:

- Handle Git pull internally (fetch latest encrypted state)
- Decrypt in memory only (never write plaintext to disk)
- Raise clear errors if the agent is not authorized for a requested secret
- Support a read-only mode (agents cannot modify secrets)

---

## MCP Server Interface

An MCP (Model Context Protocol) server wrapper should be provided so any MCP-compatible agent can request credentials through the standard tool-use protocol.

The MCP server:

- Runs locally alongside the agent
- Holds the agent's private key in memory
- Handles Git sync and decryption
- Exposes a single tool:

```json
{
  "name": "agent_vault_get",
  "parameters": {
    "secret": "stripe/api-key"
  }
}
```

This creates a cleaner security boundary than direct library usage, as the agent process itself never touches the private key. The MCP server is the only process that holds key material.

---

## Private Key Storage

The agent's local private key is the one piece of the system that cannot be protected by the repository. The tool should support multiple storage backends, selectable during agent creation.

### Tier 1: File on Disk (Default)

- Stored at `~/.agent-vault/agents/<name>.key`
- Permissions set to `chmod 600`
- Relies on OS-level user isolation
- This is the SSH model and is the sensible default

### Tier 2: OS Keychain (Optional)

- macOS Keychain, Linux `secret-service` (GNOME Keyring / KWallet), Windows Credential Manager
- Selected via `agent-vault add-agent <name> --key-backend keychain`
- Key never exists as a raw file on disk

### Tier 3: TPM / Hardware-Backed (Optional)

- Private key generated inside the TPM and never extracted
- Crypto operations happen on the hardware
- Selected via `agent-vault add-agent <name> --key-backend tpm`
- Requires `tpm2-tss` / PKCS#11 support on the host
- Key is bound to the physical machine (non-extractable)
- Not available in all environments (containers, cheap VPS)

Regardless of backend, the escrow copy in the repository always exists as the recovery path.

---

## Security Requirements

### Pre-Commit Hook

The tool must install a Git pre-commit hook during `agent-vault init` that:

- Scans all staged files for unencrypted private key material (age private key headers, raw key patterns)
- Blocks the commit if any unencrypted key material is detected
- Can be bypassed with `--no-verify` (standard Git escape hatch, but the user accepts the risk)

### .gitignore

Generated during `init` with patterns including:

```
*.key
*.pem
**/private.*
!**/*.escrow
```

### Decrypted Material

The CLI and SDK must never write decrypted secret values to disk. All decryption returns values in memory or to stdout only.

### Revocation

When an agent's access is revoked or the agent is removed:

- All secrets the agent had access to are re-encrypted without the agent's key
- The tool prints an explicit warning that the secrets should be rotated at the source, since the agent previously held decrypted copies

---

## Credential Rotation

For v1, agents are read-only. They can retrieve secrets but cannot write new values back to the vault. Credential rotation is performed by a human (or CI job) via the CLI.

Future versions may support:

- Agents submitting rotation requests as pull requests
- Agents with write access pushing directly (requires careful trust model consideration)

---

## Technology Recommendations

### Core Tool

**Rust** is recommended for the core CLI:

- Single static binary with no runtime dependencies
- `rage` library (Rust implementation of age) is mature
- Strong security properties (memory safety)
- Easy distribution (one binary, no interpreter needed)
- Cross-platform compilation

**Go** is a viable alternative with similar single-binary distribution and the canonical `age` library.

### Python SDK

- Use `pyrage` bindings for age encryption
- Publish to PyPI as `agent-vault`
- Minimal dependencies

### MCP Server

- Implement as a lightweight process (Rust or Python)
- Communicate via stdio (standard MCP transport)
- Single-tool interface for credential retrieval

---

## Out of Scope for v1

- Multi-owner quorum / Shamir's Secret Sharing
- Agent write access to the vault
- Automated credential rotation
- Time-based access windows
- Secret versioning (beyond Git history)
- GUI / web interface
- Git LFS support for large vaults
- Non-Git storage backends

---

## Success Criteria

- A developer can install the tool, initialize a vault, provision an agent, and have that agent retrieve a credential in under 5 minutes
- Zero plaintext secrets exist in the repository at any point in its history
- An agent's lost key can be recovered using only the repository and the owner's key
- The tool operates with no network calls other than Git push/pull

---

## Open Items for Developer Discussion

1. **Git conflict resolution strategy.** Since each secret is a separate encrypted file, conflicts should be rare. But the tool should handle the case where two people update the manifest simultaneously.
2. **Repo history bloat.** Frequent re-encryption (on access changes) creates new encrypted blobs. At scale, Git history grows. Consider whether periodic history squashing or shallow clones should be documented as a maintenance practice.
3. **Secret size limits.** age handles arbitrary data sizes, but very large secrets (certificates, large config files) may warrant chunking or compression before encryption.
4. **CI/CD integration patterns.** Document how the tool works in GitHub Actions, GitLab CI, etc., where the agent's key might be injected via the CI platform's native secret storage.