envpkt 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,25 +3,60 @@
3
3
  [![Node.js CI](https://github.com/jordanburke/envpkt/actions/workflows/node.js.yml/badge.svg)](https://github.com/jordanburke/envpkt/actions/workflows/node.js.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/envpkt.svg)](https://www.npmjs.com/package/envpkt)
5
5
 
6
- Credential lifecycle and fleet management for AI agents.
6
+ **Credentials your agents actually understand.**
7
7
 
8
- **fnox handles access. envpkt handles awareness.** One file (`envpkt.toml`) answers five questions per credential: **What / Where / Why / When / How**.
8
+ Structured metadata for every secret capabilities, constraints, expiration, and fleet health so agents operate within their boundaries instead of flying blind.
9
9
 
10
- ## Why envpkt?
10
+ Every credential in your system gets an `envpkt.toml` entry describing _what service it authenticates to_, _what it's allowed to do_, _when it expires_, and _how to rotate it_. Your agents query this metadata via MCP to understand their operating constraints. Your operators audit credential health across entire agent fleets. The secrets themselves stay where they belong — in your secrets manager, encrypted at rest, or injected at runtime — never in the agent's conversation context.
11
11
 
12
- Secrets managers store values. envpkt stores _metadata about_ those values — what service each credential authenticates to, when it expires, how to rotate it, and why it exists. This gives AI agents (and their operators) a structured way to:
12
+ ## MCP Integration
13
13
 
14
- - Audit credential health across a fleet of agents
15
- - Get warnings before secrets expire
16
- - Detect stale or orphaned credentials
17
- - Understand what capabilities each secret grants
18
- - Automate rotation workflows
19
- - Share secret metadata across agents via a central catalog
14
+ envpkt ships an [MCP](https://modelcontextprotocol.io/) server that gives AI agents structured awareness of their credentials. Add it to Claude, Cursor, VS Code, or any MCP-compatible client:
20
15
 
21
- envpkt never touches secret values. It works alongside your existing secrets manager (Vault, fnox, CI variables, etc.).
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "envpkt": {
20
+ "command": "envpkt",
21
+ "args": ["mcp"]
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ### Tools
28
+
29
+ | Tool | Description |
30
+ | ------------------ | ------------------------------------------------------- |
31
+ | `getPacketHealth` | Get overall health status with per-secret audit results |
32
+ | `listCapabilities` | List agent and per-secret capabilities |
33
+ | `getSecretMeta` | Get metadata for a specific secret by key |
34
+ | `checkExpiration` | Check expiration status and days remaining |
35
+ | `getEnvMeta` | Get metadata for environment defaults and drift status |
36
+
37
+ ### Resources
38
+
39
+ | URI | Description |
40
+ | ----------------------- | --------------------------------- |
41
+ | `envpkt://health` | Current credential health summary |
42
+ | `envpkt://capabilities` | Agent and secret capabilities |
43
+
44
+ The MCP server exposes metadata only — it does not have access to secret values. See [Security Model](#security-model) for details.
45
+
46
+ ## Security Model
47
+
48
+ envpkt operates a three-tier trust model. Each tier has different guarantees, and we're explicit about what each one protects against.
49
+
50
+ **Tier 1: MCP metadata (agent-facing)** — The MCP server never returns raw credential values. This isn't a policy choice — architecturally, the server reads `envpkt.toml` which contains metadata (service names, capabilities, expiration dates, rotation URLs) but never plaintext secrets. The agent gets structured awareness of its constraints without any secret material entering the LLM context window. Prompt injection attacks cannot leak what isn't there.
51
+
52
+ **Tier 2: Runtime injection (process-facing)** — `boot()` resolves secrets (from sealed packets, fnox, or environment variables) and injects them into `process.env` at startup, outside the LLM context. This is the same trust model as every Node.js application that reads from `.env`, except now secrets are encrypted at rest, scoped per-agent, and auditable. This is defense-in-depth against prompt injection — the most common attack vector — but it is not a hard boundary against agents with code execution capabilities.
53
+
54
+ **Tier 3: Shell-level agents** — Agents with shell access (Claude Code, Devin, etc.) can read environment variables directly. Prevention isn't possible at this tier. envpkt provides encrypted storage, scoped access, and audit trails — because when prevention isn't possible, visibility is what matters.
22
55
 
23
56
  ## Quick Start
24
57
 
58
+ Start where your credentials already are — environment variables — and graduate to encrypted, per-agent-scoped metadata.
59
+
25
60
  ```bash
26
61
  # Install
27
62
  npm install -g envpkt
@@ -51,7 +86,7 @@ Every project gets one `envpkt.toml` that describes its credentials. Here's a mi
51
86
 
52
87
  version = 1
53
88
 
54
- [meta.API_KEY]
89
+ [secret.API_KEY]
55
90
  service = "stripe"
56
91
  ```
57
92
 
@@ -74,7 +109,7 @@ stale_warning_days = 90
74
109
  require_expiration = true
75
110
  require_service = true
76
111
 
77
- [meta.STRIPE_SECRET_KEY]
112
+ [secret.STRIPE_SECRET_KEY]
78
113
  service = "stripe"
79
114
  purpose = "Process customer payments and manage subscriptions"
80
115
  capabilities = ["charges:write", "subscriptions:write"]
@@ -83,7 +118,7 @@ expires = "2027-01-15"
83
118
  rotation_url = "https://dashboard.stripe.com/apikeys"
84
119
  source = "vault"
85
120
 
86
- [meta.DATABASE_URL]
121
+ [secret.DATABASE_URL]
87
122
  service = "postgres"
88
123
  purpose = "Read/write access to the billing database"
89
124
  capabilities = ["SELECT", "INSERT", "UPDATE"]
@@ -93,13 +128,81 @@ rotation_url = "https://wiki.internal/runbooks/rotate-db-creds"
93
128
  source = "vault"
94
129
  ```
95
130
 
131
+ For non-secret configuration defaults (runtime mode, log levels, etc.), use `[env.*]`:
132
+
133
+ ```toml
134
+ [env.NODE_ENV]
135
+ value = "production"
136
+ purpose = "Runtime environment mode"
137
+ comment = "Override to 'development' for local testing"
138
+
139
+ [env.LOG_LEVEL]
140
+ value = "info"
141
+ purpose = "Application log verbosity"
142
+ ```
143
+
96
144
  See [`examples/`](./examples/) for more configurations.
97
145
 
98
- ## Shared Secret Catalog
146
+ ## Sealed Packets
147
+
148
+ Sealed packets embed age-encrypted secret values directly in `envpkt.toml`. This makes your config fully self-contained — no external secrets backend needed at runtime.
149
+
150
+ ### Setup
151
+
152
+ ```bash
153
+ # Generate an age keypair
154
+ age-keygen -o identity.txt
155
+ # public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
156
+ ```
157
+
158
+ Add the public key to your config and the identity file to `.gitignore`:
159
+
160
+ ```toml
161
+ [agent]
162
+ name = "my-agent"
163
+ recipient = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"
164
+ identity = "identity.txt"
165
+ ```
166
+
167
+ The `identity` path supports `~` expansion and environment variables (`$VAR`, `${VAR}`), so you can use paths like `~/keys/identity.txt` or `$KEYS_DIR/identity.txt`. Relative paths are resolved from the config file's directory.
168
+
169
+ ### Seal
170
+
171
+ ```bash
172
+ envpkt seal
173
+ ```
174
+
175
+ Each secret gets an `encrypted_value` field with age-armored ciphertext. The TOML (including ciphertext) is safe to commit.
176
+
177
+ ### Boot
178
+
179
+ At runtime, sealed values are automatically decrypted:
180
+
181
+ ```typescript
182
+ import { boot } from "envpkt"
183
+
184
+ const result = boot() // decrypts sealed values, injects into process.env
185
+ ```
186
+
187
+ Mixed mode is supported — sealed values take priority, with fnox as fallback for keys without `encrypted_value`.
188
+
189
+ ## Fleet Management
190
+
191
+ When you're running multiple agents, `envpkt fleet` scans a directory tree for `envpkt.toml` files and aggregates credential health across your entire fleet.
192
+
193
+ ```bash
194
+ envpkt fleet # Scan current directory (depth 3)
195
+ envpkt fleet -d /opt/agents # Scan specific directory
196
+ envpkt fleet --depth 5 # Increase scan depth
197
+ envpkt fleet --format json # JSON output
198
+ envpkt fleet --status critical # Filter agents by health status
199
+ ```
200
+
201
+ ### Shared Catalog
99
202
 
100
203
  When multiple agents consume the same secrets, a **shared catalog** prevents metadata duplication. Define secret metadata once in a central file, then have each agent reference it.
101
204
 
102
- ### Catalog file (`infra/envpkt.toml`)
205
+ #### Catalog file (`infra/envpkt.toml`)
103
206
 
104
207
  ```toml
105
208
  version = 1
@@ -108,7 +211,7 @@ version = 1
108
211
  stale_warning_days = 90
109
212
  require_expiration = true
110
213
 
111
- [meta.DATABASE_URL]
214
+ [secret.DATABASE_URL]
112
215
  service = "postgres"
113
216
  purpose = "Primary application database"
114
217
  capabilities = ["SELECT", "INSERT", "UPDATE", "DELETE"]
@@ -117,14 +220,14 @@ source = "vault"
117
220
  created = "2025-11-01"
118
221
  expires = "2026-11-01"
119
222
 
120
- [meta.REDIS_URL]
223
+ [secret.REDIS_URL]
121
224
  service = "redis"
122
225
  purpose = "Caching and session storage"
123
226
  created = "2025-11-01"
124
227
  expires = "2026-11-01"
125
228
  ```
126
229
 
127
- ### Agent file (`agents/pipeline/envpkt.toml`)
230
+ #### Agent file (`agents/pipeline/envpkt.toml`)
128
231
 
129
232
  ```toml
130
233
  version = 1
@@ -136,11 +239,11 @@ consumer = "agent"
136
239
  secrets = ["DATABASE_URL", "REDIS_URL"]
137
240
 
138
241
  # Optional: narrow the catalog definition for this agent
139
- [meta.DATABASE_URL]
242
+ [secret.DATABASE_URL]
140
243
  capabilities = ["SELECT"]
141
244
  ```
142
245
 
143
- ### Resolve to a flat config
246
+ #### Resolve to a flat config
144
247
 
145
248
  ```bash
146
249
  envpkt resolve -c agents/pipeline/envpkt.toml
@@ -148,13 +251,32 @@ envpkt resolve -c agents/pipeline/envpkt.toml
148
251
 
149
252
  This produces a self-contained config with catalog metadata merged in and agent overrides applied. The resolved output has no `catalog` reference — it's ready for deployment.
150
253
 
151
- ### Merge rules
254
+ #### Merge rules
152
255
 
153
- - Each field in the agent's `[meta.KEY]` override **replaces** the catalog field (shallow merge)
256
+ - Each field in the agent's `[secret.KEY]` override **replaces** the catalog field (shallow merge)
154
257
  - Omitted fields keep the catalog value
155
258
  - `agent.secrets` is the source of truth for which keys the agent needs
156
259
 
157
- ## CLI Commands
260
+ ## How envpkt Compares
261
+
262
+ The agentic credential space is splitting into approaches. Here's where envpkt fits:
263
+
264
+ | | envpkt | agent-vault | AgentSecrets | 1Password Agentic | Infisical |
265
+ | --------------------------- | ----------------------------------------------------------- | --------------------------- | -------------------------- | ----------------------------- | -------------------- |
266
+ | **Core approach** | Metadata sidecar | Git-based secret storage | Proxy injection | Browser autofill | Secret retrieval API |
267
+ | **What agents see** | Structured metadata (capabilities, constraints, expiration) | Raw secret values | Nothing (proxy handles it) | Nothing (autofill handles it) | Raw secret values |
268
+ | **MCP server** | Yes | Yes | No | No | Yes |
269
+ | **Encryption at rest** | age sealed packets | Git-crypt | N/A (proxy model) | Vault encryption | Vault encryption |
270
+ | **Per-agent scoping** | Yes (agent.secrets, capabilities) | Yes (policies) | Yes (proxy rules) | No | Yes (policies) |
271
+ | **Fleet health monitoring** | Yes (fleet scan, aggregated audit) | No | No | No | No |
272
+ | **Credential metadata** | Rich (purpose, capabilities, rotation, lifecycle) | Minimal | Minimal | Minimal | Moderate |
273
+ | **Adoption path** | Scan existing env vars, add metadata incrementally | New secret storage workflow | Proxy configuration | Browser extension | API integration |
274
+
275
+ **envpkt's angle**: Competitors are fighting over how secrets move — retrieval vs. proxy vs. autofill. envpkt owns what secrets _mean_. Rate limits, expiration policies, capability scopes, rotation runbooks — structured semantics that travel with the credential. That's the layer the others don't have.
276
+
277
+ > **Note**: This comparison reflects publicly available information. Verify current feature sets before making procurement decisions.
278
+
279
+ ## CLI Reference
158
280
 
159
281
  ### `envpkt init`
160
282
 
@@ -196,18 +318,6 @@ envpkt resolve -c agent.toml --dry-run # Preview without writing
196
318
 
197
319
  Configs without a `catalog` field pass through unchanged.
198
320
 
199
- ### `envpkt fleet`
200
-
201
- Scan a directory tree for `envpkt.toml` files and aggregate health.
202
-
203
- ```bash
204
- envpkt fleet # Scan current directory (depth 3)
205
- envpkt fleet -d /opt/agents # Scan specific directory
206
- envpkt fleet --depth 5 # Increase scan depth
207
- envpkt fleet --format json # JSON output
208
- envpkt fleet --status critical # Filter agents by health status
209
- ```
210
-
211
321
  ### `envpkt inspect`
212
322
 
213
323
  Display a structured view of an `envpkt.toml` file. Automatically resolves catalog references.
@@ -234,6 +344,26 @@ envpkt exec --strict -- ./deploy.sh # Abort if audit is not healthy
234
344
  envpkt exec --profile staging -- ... # Use a specific fnox profile
235
345
  ```
236
346
 
347
+ ### `envpkt seal`
348
+
349
+ Encrypt secret values into `envpkt.toml` using [age](https://age-encryption.org/). Sealed values are safe to commit to git — only the holder of the private key can decrypt them.
350
+
351
+ ```bash
352
+ envpkt seal # Seal all secrets in envpkt.toml
353
+ envpkt seal -c path/to/envpkt.toml # Specify config path
354
+ envpkt seal --profile staging # Use a specific fnox profile for value resolution
355
+ ```
356
+
357
+ Requires `agent.recipient` (age public key) in your config. Values are resolved via cascade:
358
+
359
+ 1. **fnox** (if available)
360
+ 2. **Environment variables** (e.g. `OPENAI_API_KEY` in your shell)
361
+ 3. **Interactive prompt** (asks you to paste each value)
362
+
363
+ After sealing, each secret gets an `encrypted_value` field. At boot time, `envpkt exec` or `boot()` automatically decrypts sealed values using the `agent.identity` file.
364
+
365
+ See [`examples/sealed-agent.toml`](./examples/sealed-agent.toml) for a complete example.
366
+
237
367
  ### `envpkt env scan`
238
368
 
239
369
  Auto-discover credentials from your shell environment. Matches env vars against ~45 known services (exact name), ~13 generic suffix patterns (`*_API_KEY`, `*_SECRET`, `*_TOKEN`, etc.), and ~29 value shape patterns (`sk-*`, `ghp_*`, `AKIA*`, `postgres://`, etc.).
@@ -262,9 +392,30 @@ envpkt env check --strict # Exit non-zero on any drift
262
392
  envpkt env check -c path/to/envpkt.toml # Specify config path
263
393
  ```
264
394
 
395
+ ### `envpkt env export`
396
+
397
+ Output `export KEY='VALUE'` statements for sourcing secrets into the current shell. Secrets are resolved via sealed packets and/or fnox — the same pipeline as `envpkt exec`, but instead of spawning a subprocess, the output is designed to be `eval`'d.
398
+
399
+ ```bash
400
+ # Source secrets into the current shell
401
+ eval "$(envpkt env export)"
402
+
403
+ # Use a specific fnox profile
404
+ eval "$(envpkt env export --profile staging)"
405
+
406
+ # Specify config path
407
+ eval "$(envpkt env export -c path/to/envpkt.toml)"
408
+ ```
409
+
410
+ Add to your shell startup (e.g. `~/.zshrc` or `~/.bashrc`) for automatic secret loading. envpkt's [config discovery chain](#config-resolution) finds your config automatically — no platform-specific shell logic needed:
411
+
412
+ ```bash
413
+ eval "$(envpkt env export 2>/dev/null)"
414
+ ```
415
+
265
416
  ### `envpkt shell-hook`
266
417
 
267
- Output a shell function that runs `envpkt audit --format minimal` whenever you `cd` into a directory containing `envpkt.toml`.
418
+ Output a shell function that runs `envpkt audit --format minimal` whenever you `cd` into a directory. envpkt's config discovery chain automatically finds config files beyond CWD (see [Config Resolution](#config-resolution)), so the hook works even in directories without a local `envpkt.toml`.
268
419
 
269
420
  ```bash
270
421
  # Add to your .zshrc
@@ -282,85 +433,34 @@ Start the envpkt MCP server (stdio transport) for AI agent integration.
282
433
  envpkt mcp
283
434
  ```
284
435
 
285
- ## MCP Server
436
+ ## Config Resolution
286
437
 
287
- envpkt ships an [MCP](https://modelcontextprotocol.io/) server that exposes credential metadata to AI agents. Add it to your MCP client config:
438
+ Commands that read `envpkt.toml` resolve the config path via a priority chain:
288
439
 
289
- ```json
290
- {
291
- "mcpServers": {
292
- "envpkt": {
293
- "command": "envpkt",
294
- "args": ["mcp"]
295
- }
296
- }
297
- }
298
- ```
299
-
300
- ### Tools
301
-
302
- | Tool | Description |
303
- | ------------------ | ------------------------------------------------------- |
304
- | `getPacketHealth` | Get overall health status with per-secret audit results |
305
- | `listCapabilities` | List agent and per-secret capabilities |
306
- | `getSecretMeta` | Get metadata for a specific secret by key |
307
- | `checkExpiration` | Check expiration status and days remaining |
308
-
309
- ### Resources
310
-
311
- | URI | Description |
312
- | ----------------------- | --------------------------------- |
313
- | `envpkt://health` | Current credential health summary |
314
- | `envpkt://capabilities` | Agent and secret capabilities |
315
-
316
- No secret values are ever exposed through the MCP server.
317
-
318
- ## Schema
319
-
320
- envpkt.toml is validated against a JSON Schema. Editors with TOML + JSON Schema support will provide autocompletion and validation when the `#:schema` directive is present on line 1.
321
-
322
- The schema is published at:
323
-
324
- - npm: `envpkt/schema` (importable via package exports)
325
- - GitHub: `schemas/envpkt.schema.json`
326
-
327
- ### Secret Metadata Fields
328
-
329
- Each `[meta.<KEY>]` section describes a secret:
330
-
331
- | Tier | Fields | Description |
332
- | --------------- | ----------------------------------------------- | ----------------------------------------- |
333
- | **Scan-first** | `service`, `expires`, `rotation_url` | Key health indicators for audit |
334
- | **Context** | `purpose`, `capabilities`, `created` | Why this secret exists and what it grants |
335
- | **Operational** | `rotates`, `rate_limit`, `model_hint`, `source` | Runtime and provisioning info |
336
- | **Enforcement** | `required`, `tags` | Filtering, grouping, and policy |
337
-
338
- ### Agent Identity
440
+ 1. **Explicit flag** — `-c path/to/envpkt.toml`
441
+ 2. **Environment variable** — `ENVPKT_CONFIG`
442
+ 3. **Discovery chain** — searches in order:
443
+ - `./envpkt.toml` (current working directory)
444
+ - Custom paths in `ENVPKT_SEARCH_PATH` (colon-separated)
445
+ - `~/.envpkt/envpkt.toml` (user home)
446
+ - Cloud storage paths (OneDrive, iCloud, Dropbox, Google Drive)
339
447
 
340
- The optional `[agent]` section identifies the AI agent:
448
+ When a config is discovered outside CWD, envpkt prints where it loaded from to stderr:
341
449
 
342
- ```toml
343
- [agent]
344
- name = "data-pipeline-agent"
345
- consumer = "agent" # agent | service | developer | ci
346
- description = "ETL pipeline processor"
347
- capabilities = ["read-s3", "write-postgres"]
348
- expires = "2027-01-01"
349
- services = ["aws", "postgres"]
350
- secrets = ["DATABASE_URL", "AWS_KEY"] # When using a catalog
450
+ ```
451
+ envpkt: loaded /Users/you/.envpkt/envpkt.toml
351
452
  ```
352
453
 
353
- ### Lifecycle Policy
454
+ ### `ENVPKT_SEARCH_PATH`
354
455
 
355
- The optional `[lifecycle]` section configures audit behavior:
456
+ Prepend custom search locations (colon-separated paths to `envpkt.toml` files):
356
457
 
357
- ```toml
358
- [lifecycle]
359
- stale_warning_days = 90 # Flag secrets older than N days without updates
360
- require_expiration = true # Require expires on all secrets
361
- require_service = true # Require service on all secrets
458
+ ```bash
459
+ export ENVPKT_SEARCH_PATH="$HOME/OneDrive/.envpkt/envpkt.toml:/custom/path/envpkt.toml"
362
460
  ```
363
461
 
462
+ These are searched after CWD but before the built-in candidate paths. Useful for corporate OneDrive names, Google Drive with email in the path, or any non-standard location.
463
+
364
464
  ## Library API
365
465
 
366
466
  envpkt is also available as a TypeScript library with a functional programming API built on [functype](https://github.com/jordanburke/functype). All functions return `Either<Error, Result>` or `Option<T>` — no thrown exceptions.
@@ -371,6 +471,7 @@ import { boot, bootSafe, loadConfig, computeAudit, scanFleet, resolveConfig } fr
371
471
  // Boot API — load config, resolve catalog, audit, inject secrets
372
472
  const result = boot({ configPath: "envpkt.toml", inject: true })
373
473
  console.log(result.audit.status) // "healthy" | "degraded" | "critical"
474
+ console.log(result.configSource) // "flag" | "env" | "cwd" | "search"
374
475
 
375
476
  // Safe variant returns Either instead of throwing
376
477
  const safe = bootSafe({ configPath: "envpkt.toml" })
@@ -399,6 +500,21 @@ const fleet = scanFleet("/opt/agents", { maxDepth: 3 })
399
500
  console.log(`${fleet.total_agents} agents, ${fleet.total_secrets} secrets`)
400
501
  ```
401
502
 
503
+ ### Framework Integration
504
+
505
+ `boot()` runs before your agent framework initializes, making it compatible with any framework:
506
+
507
+ ```typescript
508
+ import { boot } from "envpkt"
509
+
510
+ // Resolve and inject credentials before agent startup
511
+ const result = boot({ configPath: "envpkt.toml", inject: true })
512
+ console.log(`${result.audit.status} — ${result.injected.length} secrets loaded`)
513
+
514
+ // Now start your agent framework — process.env is populated
515
+ // Works with LangChain, CrewAI, AutoGen, or any framework that reads from process.env
516
+ ```
517
+
402
518
  ### Packet Formatting API
403
519
 
404
520
  ```typescript
@@ -460,6 +576,32 @@ matchEnvVar("OPENAI_API_KEY", "sk-test123").fold(
460
576
  )
461
577
  ```
462
578
 
579
+ ### Seal API
580
+
581
+ ```typescript
582
+ import { ageEncrypt, ageDecrypt, sealSecrets, unsealSecrets } from "envpkt"
583
+
584
+ // Encrypt a single value
585
+ const encrypted = ageEncrypt("sk-my-api-key", "age1ql3z7hjy...")
586
+ encrypted.fold(
587
+ (err) => console.error("Encrypt failed:", err.message),
588
+ (ciphertext) => console.log(ciphertext), // -----BEGIN AGE ENCRYPTED FILE-----
589
+ )
590
+
591
+ // Decrypt a single value
592
+ const decrypted = ageDecrypt(ciphertext, "/path/to/identity.txt")
593
+
594
+ // Seal all secrets in a config's meta
595
+ const sealed = sealSecrets(config.meta, { OPENAI_API_KEY: "sk-..." }, recipientPublicKey)
596
+
597
+ // Unseal all encrypted_value entries
598
+ const values = unsealSecrets(config.meta, "/path/to/identity.txt")
599
+ values.fold(
600
+ (err) => console.error("Unseal failed:", err.message),
601
+ (secrets) => console.log(secrets), // { OPENAI_API_KEY: "sk-..." }
602
+ )
603
+ ```
604
+
463
605
  ### Catalog Resolution API
464
606
 
465
607
  ```typescript
@@ -482,11 +624,58 @@ loadConfig(configPath).fold(
482
624
  )
483
625
  ```
484
626
 
627
+ ## Schema
628
+
629
+ envpkt.toml is validated against a JSON Schema. Editors with TOML + JSON Schema support will provide autocompletion and validation when the `#:schema` directive is present on line 1.
630
+
631
+ The schema is published at:
632
+
633
+ - npm: `envpkt/schema` (importable via package exports)
634
+ - GitHub: `schemas/envpkt.schema.json`
635
+
636
+ ### Secret Metadata Fields
637
+
638
+ Each `[secret.<KEY>]` section describes a secret:
639
+
640
+ | Tier | Fields | Description |
641
+ | --------------- | ----------------------------------------------- | ------------------------------------------- |
642
+ | **Scan-first** | `service`, `expires`, `rotation_url` | Key health indicators for audit |
643
+ | **Context** | `purpose`, `comment`, `capabilities`, `created` | Why this secret exists and what it grants |
644
+ | **Operational** | `rotates`, `rate_limit`, `model_hint`, `source` | Runtime and provisioning info |
645
+ | **Sealed** | `encrypted_value` | Age-encrypted secret value (safe to commit) |
646
+ | **Enforcement** | `required`, `tags` | Filtering, grouping, and policy |
647
+
648
+ ### Agent Identity
649
+
650
+ The optional `[agent]` section identifies the AI agent:
651
+
652
+ ```toml
653
+ [agent]
654
+ name = "data-pipeline-agent"
655
+ consumer = "agent" # agent | service | developer | ci
656
+ description = "ETL pipeline processor"
657
+ capabilities = ["read-s3", "write-postgres"]
658
+ expires = "2027-01-01"
659
+ services = ["aws", "postgres"]
660
+ secrets = ["DATABASE_URL", "AWS_KEY"] # When using a catalog
661
+ ```
662
+
663
+ ### Lifecycle Policy
664
+
665
+ The optional `[lifecycle]` section configures audit behavior:
666
+
667
+ ```toml
668
+ [lifecycle]
669
+ stale_warning_days = 90 # Flag secrets older than N days without updates
670
+ require_expiration = true # Require expires on all secrets
671
+ require_service = true # Require service on all secrets
672
+ ```
673
+
485
674
  ## fnox Integration
486
675
 
487
676
  envpkt integrates with [fnox](https://github.com/jordanburke/fnox) for secret resolution:
488
677
 
489
- - `envpkt init --from-fnox` scaffolds `[meta.*]` entries from `fnox.toml`
678
+ - `envpkt init --from-fnox` scaffolds `[secret.*]` entries from `fnox.toml`
490
679
  - `envpkt audit` detects orphaned keys (in envpkt but not in fnox, or vice versa)
491
680
  - `envpkt exec` injects fnox secrets into the subprocess environment
492
681