@forgezero/vault 0.1.9 → 0.1.11

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
@@ -1,90 +1,200 @@
1
+ <!--
2
+ GENERATED FILE — do not edit.
3
+
4
+ Change scripts/generate-guides.ts or its typed sources, run `bun run guides`,
5
+ and commit the generator and rendered files together.
6
+ -->
7
+
1
8
  # @forgezero/vault
2
9
 
3
- **Read your secrets at runtime instead of shipping them in a file.**
10
+ One scoped contract over platform-direct, managed-agent and external API-key trust paths.
11
+
12
+ ## Global package root and supported runtimes
13
+
14
+ The scoped vault facade. A tenant application uses its local agent or an API key; trusted platform code injects a direct realm backend instead of calling itself. The one package that ends up in somebody else's production dependency tree, which is why it stays alone and stays small. Supported runtimes: bun, node, workers, deno. The global base/root import is @forgezero/vault. Every public import or command is listed below; the documentation inventory is checked in both directions against package.json exports.
4
15
 
5
- The client discovers its own credential and uses one stable API origin. Node
6
- routing and failover stay behind that edge contract; the SDK never discovers or
7
- stores a node hostname. The same code runs on a laptop and in production:
8
- outside managed compute the key is a local signing seed; on managed compute
9
- there is no remote credential in the application.
16
+ ```text
17
+ import * as root from '@forgezero/vault';
18
+ ```
19
+
20
+ ## Commands
10
21
 
11
- This one talks to a ForgeZero vault, so it needs an account. The other three
12
- packages do not.
22
+ bun add @forgezero/vault Install the scoped Vault client and its public subpaths.
13
23
 
14
- ```bash
24
+ ```text
15
25
  bun add @forgezero/vault
16
26
  ```
17
27
 
18
- ```ts
28
+ ## @forgezero/vault
29
+
30
+ One scoped contract over an explicitly injected platform backend, a managed tenant socket or an automatically derived external API-key signer.
31
+
32
+ ```text
33
+ import * as api from '@forgezero/vault';
34
+ ```
35
+
36
+ ## @forgezero/vault/schema
37
+
38
+ Where an entry’s shape comes from: pulled from the platform when ForgeZero has to render it, declared locally when only the tenant needs it.
39
+
40
+ ```text
41
+ import * as api from '@forgezero/vault/schema';
42
+ ```
43
+
44
+ ## @forgezero/vault/config
45
+
46
+ Read `.fz/config.json` — which project, which environment, which secrets, and what to call them.
47
+
48
+ ```text
49
+ import * as api from '@forgezero/vault/config';
50
+ ```
51
+
52
+ ## @forgezero/vault/env
53
+
54
+ Envless: fill `process.env` from the vault at boot, refusing to run during a build.
55
+
56
+ ```text
57
+ import * as api from '@forgezero/vault/env';
58
+ ```
59
+
60
+ ## @forgezero/vault/frameworks
61
+
62
+ SvelteKit and Next.js wiring, attached at the one place that runs once, on the server, before any request.
63
+
64
+ ```text
65
+ import * as api from '@forgezero/vault/frameworks';
66
+ ```
67
+
68
+ ## @forgezero/vault/providers
69
+
70
+ Adapters that make Vault a provider-configuration and credential source without coupling the providers package to ForgeZero.
71
+
72
+ ```text
73
+ import * as api from '@forgezero/vault/providers';
74
+ ```
75
+
76
+ ## Read a scoped secret
77
+
78
+ The same client discovers the managed Agent socket or uses an external API-key signer. Project and environment remain explicit.
79
+
80
+ ```text
81
+ import { createVault } from '@forgezero/vault';
82
+
83
+ const vault = createVault({ project: 'payments', environment: 'production' });
84
+ const token = await vault.get('STRIPE_KEY');
85
+ ```
86
+
87
+ ## Three paths, and why they stay explicit
88
+
89
+ Platform API logic already holds the requested realm master seed, so it opens the scoped envelope directly through an injected internal backend — no HTTP call, API key or local replica. A tenant on managed compute uses the local agent and its memory-only project scope. A tenant elsewhere signs HTTPS with an API-key seed. These share vault semantics and never silently fall through from a stronger posture to a weaker one.
90
+
91
+ ```text
92
+ PLATFORM API resident realm seed -> direct scoped envelope
93
+ MANAGED TENANT local unix socket -> agent RAM scope
94
+ EXTERNAL TENANT scoped API-key seed -> signed HTTPS
95
+ ```
96
+
97
+ ## Current implementation boundary
98
+
99
+ All three trust paths now have code: the platform opens its resident realm seed directly, an enrolled tenant agent derives its project binding and keeps every environment in that project in RAM, and createVault automatically uses either the local socket or a hybrid signer derived from an API-key seed. Live SEV-SNP report acquisition and an end-to-end production deployment are still gates; installation alone is not presented as proof.
100
+
101
+ ## 1. Install
102
+
103
+ One ForgeZero identity dependency plus its required audited Noble crypto peers. The HTTPS path works anywhere fetch and Web Crypto exist; managed socket discovery requires a Bun or Node server runtime.
104
+
105
+ ```text
106
+ bun add @forgezero/vault
107
+ ```
108
+
109
+ ## 2. Read a secret
110
+
111
+ The project and environment are explicit application scope. Managed compute uses the local Agent socket; external runtimes use the stable API origin and their scoped key.
112
+
113
+ ```text
19
114
  import { ForgeZero } from '@forgezero/vault';
20
115
 
21
116
  const fz = new ForgeZero({ project: 'altpilot', environment: 'production' });
22
117
 
23
118
  const key = await fz.get('STRIPE_KEY');
24
- const names = await fz.list(); // names; remote reads also include metadata
119
+ const names = await fz.list(); // names; HTTPS reads also carry metadata
25
120
  ```
26
121
 
27
- ## Envless
122
+ ## 3. Ship it unchanged — target discovery
28
123
 
29
- Fill `process.env` at boot and change nothing else in your application:
124
+ On managed tenant compute the Agent holds the node identity and the complete project RAM replica; the application holds no reusable remote credential and selects its environment in each client instance. The client finds the group-scoped socket instead of the environment variable. Outside managed compute it automatically derives the exact hybrid request signer from FORGEZERO_API_KEY.
30
125
 
31
- ```ts
32
- import '@forgezero/vault/env'; // refuses to run during a build
126
+ ```text
127
+ new ForgeZero()
128
+ |- /run/forgezero/vault.sock exists -> MANAGED, app holds nothing
129
+ |- FORGEZERO_API_KEY present -> EXTERNAL, derive and sign locally
130
+ `- neither -> throws, naming BOTH remedies
33
131
  ```
34
132
 
35
- Rotation stops being a project. A new version is written, readers pick it up on
36
- their next fetch, and nothing is redeployed.
133
+ ## When both are present, the socket wins
37
134
 
38
- ## Two application transports, and the socket wins
135
+ On managed compute an API key in the environment is a leftover. Honouring it would silently downgrade a machine that holds nothing into one holding a signing seed, so discovery always prefers the stronger posture.
39
136
 
40
- On a ForgeZero compute the Agent keeps the enrolled project's environments in
41
- RAM and the client selects one environment over its group-scoped Unix socket.
42
- Anywhere else it derives the same hybrid request identity from an API-key seed
43
- and reads over HTTPS. Each remote request also carries a signed one-use hybrid
44
- ML-KEM-768 + X25519 response key; the API seals the JSON value to that key with
45
- AES-256-GCM, so a TLS-terminating edge can route the call but cannot read its
46
- secret response. When both are present the socket wins, because the
47
- application then holds no reusable remote credential.
137
+ ## Rotation is human-authorised and application-observed
48
138
 
49
- ## The API key is a seed, not a password
139
+ Writes stay on the authenticated platform surface with a human session, step-up proof and named audit actor. The application credential is read-only. External API-key reads may request a historical version; the managed RAM replica intentionally serves only the current value.
50
140
 
51
- The key handed to you **is** the seed for a hybrid Ed25519 + ML-DSA-65 key pair.
52
- The client re-derives that pair on every start; the platform stores only the
53
- public halves. A dump of the key table yields nothing that can sign — which is
54
- not true of any scheme where the server holds something it compares against.
55
-
56
- ## Schema-declared secrets
141
+ ```text
142
+ // after an authorised rotation in the platform UI
143
+ for await (const change of fz.watch()) {
144
+ console.log(change.name, change.version);
145
+ }
146
+ ```
57
147
 
58
- An entry can declare fields the tenant never holds. Two custody modes:
148
+ ## getAll is managed-only, deliberately
59
149
 
60
- - **derived** the vault generates the key material from the master seed and it
61
- exists in plaintext only inside a signing call.
62
- - **supplied** — you generate it, the vault seals it.
150
+ One compromised API key should not hand over an entire environment in a single call, so a key holder gets list() plus reads by name. On managed compute there is no key to compromise, which is why the bulk read is allowed there and only there.
63
151
 
64
- Either way there is no call that returns a private key. Derivation and signing
65
- stay inside explicit trusted platform workflows; the general application
66
- credential remains a read-only secret client and is not a transaction-signing
67
- oracle.
152
+ ## One stable API origin
68
153
 
69
- ## Subpaths
154
+ The client always calls api.forgezero.net. Cloudflare owns routing and node health behind that name; the SDK never receives a node hostname, polls an assignment endpoint or replays a state-changing request against a different process.
70
155
 
71
- | import | what it is |
72
- |---|---|
73
- | `@forgezero/vault` | the read-only application client — get, list and watch rotation |
74
- | `/env` | envless: fill `process.env` at boot |
75
- | `/config` | read `.fz/config.json` — project, environment, which secrets |
76
- | `/schema` | where an entry's shape comes from: pulled from the platform, or local |
77
- | `/frameworks` | SvelteKit and Next.js wiring, at the one place that runs once |
156
+ ```text
157
+ 410 421 502 503 504 530 -> ask for a different node, retry once
158
+ 401 403 -> an answer, not a routing problem
159
+ ```
78
160
 
79
161
  ## Errors you will actually hit
80
162
 
81
- `VAULT_LOCKED` (423) a custodian quorum must unlock it; retrying will not
82
- help. `BLOCKED` — access was cut deliberately and lifts just as fast.
83
- `NO_CREDENTIAL` — neither a socket nor an API key was found.
163
+ Each names what to do next.
164
+
165
+ ```text
166
+ NO_CREDENTIAL neither the socket nor FORGEZERO_API_KEY was found
167
+ VAULT_LOCKED the realm has no seed in memory; a custodian must unlock
168
+ MANAGED_ONLY getAll needs the agent socket
169
+ NO_NODE no vault node is currently available
170
+ ```
171
+
172
+ ## API keys — the key is a seed, not a password
173
+
174
+ The SDK derives an Ed25519 + ML-DSA-65 keypair from it and signs every request, including a one-use hybrid ML-KEM-768 + X25519 response key. The API AES-256-GCM seals the result to that request before it crosses the edge. The seed itself is never transmitted, and ForgeZero stores only public halves.
175
+
176
+ ```text
177
+ fz.live.<keyId>.<seed> fz.test.<keyId>.<seed>
178
+ ```
179
+
180
+ ## Scope every key to one project and only the environments it needs
181
+
182
+ An API key is fixed to one project, optionally narrowed to named environments, and issued with exact route grants or project-owned API-key roles. Single-entry read, metadata listing and change following are the least-privilege default; bulk environment read and exact pipeline release are separate opt-ins.
183
+
184
+ ```text
185
+ project: payments
186
+ environments: production, staging
187
+ routes: read one, list metadata, follow changes
188
+ bulk read: explicit opt-in
189
+ pipeline release: explicit live-key role
190
+ ```
191
+
192
+ ## Controls, and what each is actually worth
193
+
194
+ Project, environment, exact route grants and revocation are real: status is read fresh on every call, so a revoked key stops working on the next request. API keys do not currently expire automatically; rotate or revoke them deliberately. An origin header is not treated as server authentication because any server-side caller can set it.
84
195
 
85
- Full documentation: **https://www.forgezero.net/docs/vault-package**
196
+ ## Vault read-only; release intent is the narrow machine write
86
197
 
87
- ## Licence
198
+ The external Vault SDK has no secret write endpoint. A live API key may separately receive pipeline:release:create: it queues one full Git commit on an existing enabled pipeline, requires a stable idempotency key, and cannot edit a pipeline, target, secret or compute. Sandbox and legacy keys never inherit it, and the bound Agent still claims execution.
88
199
 
89
- MIT. Part of [ForgeZero](https://www.forgezero.net) — secrets, attested compute and
90
- deploys.
200
+ Full rendered documentation: https://www.forgezero.net/docs/vault-package
package/dist/index.d.ts CHANGED
@@ -282,4 +282,4 @@ export declare function systemdCredentials(options?: SystemdCredentialOptions):
282
282
  readonly name: 'systemd';
283
283
  get(reference: string, field: string): Promise<string>;
284
284
  };
285
- export declare const VERSION = "0.1.9";
285
+ export declare const VERSION = "0.1.11";
package/dist/index.js CHANGED
@@ -382,7 +382,7 @@ function systemdCredentials(options = {}) {
382
382
  }
383
383
  };
384
384
  }
385
- var VERSION = "0.1.9";
385
+ var VERSION = "0.1.11";
386
386
  export {
387
387
  vaultCredentials,
388
388
  systemdCredentials,
@@ -68,5 +68,9 @@ export declare function vaultConfig(vault: ForgeZero, options?: VaultConfigOptio
68
68
  */
69
69
  export declare function vaultCredentials(vault: ForgeZero): {
70
70
  name: string;
71
- get: (reference: string, field: string) => Promise<string>;
71
+ get(reference: string, field: string): Promise<string>;
72
+ };
73
+ export declare function vaultCredentials(vault: ForgeZero, reference: string): {
74
+ name: string;
75
+ get(field: string): Promise<string>;
72
76
  };
package/dist/providers.js CHANGED
@@ -382,7 +382,7 @@ function systemdCredentials(options = {}) {
382
382
  }
383
383
  };
384
384
  }
385
- var VERSION = "0.1.9";
385
+ var VERSION = "0.1.11";
386
386
 
387
387
  // src/providers.ts
388
388
  var MISSING = new Set(["ENTRY_NOT_FOUND", "VERSION_NOT_FOUND"]);
@@ -428,10 +428,16 @@ function vaultConfig(vault, options = {}) {
428
428
  }
429
429
  };
430
430
  }
431
- function vaultCredentials2(vault) {
431
+ function vaultCredentials2(vault, reference) {
432
+ if (reference !== undefined) {
433
+ return {
434
+ name: `vault:${reference}`,
435
+ get: (field) => vault.get(`${reference}.${field}`)
436
+ };
437
+ }
432
438
  return {
433
439
  name: "vault",
434
- get: (reference, field) => vault.get(`${reference}.${field}`)
440
+ get: (reference2, field) => vault.get(`${reference2}.${field}`)
435
441
  };
436
442
  }
437
443
  export {
package/dist/schema.js CHANGED
@@ -382,7 +382,7 @@ function systemdCredentials(options = {}) {
382
382
  }
383
383
  };
384
384
  }
385
- var VERSION = "0.1.9";
385
+ var VERSION = "0.1.11";
386
386
 
387
387
  // src/schema.ts
388
388
  function managedSchemas(vault, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/vault",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -43,7 +43,7 @@
43
43
  "@types/bun": "latest"
44
44
  },
45
45
  "dependencies": {
46
- "@forgezero/runtime": "^0.1.6"
46
+ "@forgezero/runtime": "^0.1.7"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@noble/curves": "^2.2.0",