@forgezero/vault 0.1.22 → 0.1.24

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
@@ -41,6 +41,7 @@ These are supported consumer entry points, not every internal module shipped for
41
41
  | @forgezero/vault/env | Envless: fill `process.env` from the vault at boot, refusing to run during a build. | portable | [Reference + usage](#forgezero-vault-env) |
42
42
  | @forgezero/vault/frameworks | SvelteKit and Next.js wiring, attached at the one place that runs once, on the server, before any request. | portable | [Reference + usage](#forgezero-vault-frameworks) |
43
43
  | @forgezero/vault/providers | Adapters that make Vault a provider-configuration and credential source without coupling the providers package to ForgeZero. | portable | [Reference + usage](#forgezero-vault-providers) |
44
+ | @forgezero/vault/runtime | Explicit, separately granted password, envelope-encryption, signing, chain-address and SSH-certificate operations whose private material never leaves the unlocked Vault runtime. | portable | [Reference + usage](#forgezero-vault-runtime) |
44
45
 
45
46
  ## Commands
46
47
 
@@ -231,6 +232,63 @@ const repositories = await git.call('listRepositories', { installationId: '42' }
231
232
  if (!repositories.ok) throw repositories.error;
232
233
  ```
233
234
 
235
+ <a id="forgezero-vault-runtime"></a>
236
+ ## @forgezero/vault/runtime
237
+
238
+ Explicit, separately granted password, envelope-encryption, signing, chain-address and SSH-certificate operations whose private material never leaves the unlocked Vault runtime. This is a supported entry point. Import only the named values used by the adjacent task example; the declaration file remains the complete API reference.
239
+
240
+ ```text
241
+ import {
242
+ VaultRuntime,
243
+ } from '@forgezero/vault/runtime';
244
+ ```
245
+
246
+ ## @forgezero/vault/runtime — Encrypt with a non-exportable, versioned DEK
247
+
248
+ Runtime operations require explicit external API-key grants. The returned envelope carries the immutable key version, so data remains decryptable after rotation; the DEK itself is never returned.
249
+
250
+ ```text
251
+ import {
252
+ createVault,
253
+ } from '@forgezero/vault';
254
+
255
+ const vault = createVault({ project: 'payments', environment: 'production' });
256
+ const dek = await vault.runtime.create({
257
+ kind: 'data-encryption-key',
258
+ algorithm: 'aes-256-gcm',
259
+ label: 'customer database envelope'
260
+ });
261
+
262
+ const plaintext = new TextEncoder().encode('sensitive row');
263
+ const encrypted = await vault.runtime.encrypt(dek.runtimeKey, plaintext, 'customers:42');
264
+ await vault.runtime.rotate(dek.runtimeKey);
265
+
266
+ // encrypted.keyVersion selects the old immutable version after rotation.
267
+ const opened = await vault.runtime.decrypt(dek.runtimeKey, encrypted, 'customers:42');
268
+ ```
269
+
270
+ ## @forgezero/vault/runtime — Derive an address and sign without exporting its key
271
+
272
+ Ethereum and Solana are the initial reviewed chain profiles. ForgeZero derives the documented path and signs canonical bytes or a 32-byte digest; transaction serialization remains the chain SDK’s responsibility.
273
+
274
+ ```text
275
+ import {
276
+ createVault,
277
+ } from '@forgezero/vault';
278
+
279
+ const vault = createVault({ project: 'treasury', environment: 'production' });
280
+ const signer = await vault.runtime.create({
281
+ kind: 'chain-key',
282
+ algorithm: 'ethereum-secp256k1',
283
+ label: 'settlement signer',
284
+ chain: { chain: 'ethereum', network: 'sepolia', account: 0, change: 0, index: 0 }
285
+ });
286
+
287
+ console.log(signer.address, signer.path, signer.publicKey);
288
+ declare const transactionDigest: Uint8Array; // exactly 32 bytes
289
+ const signed = await vault.runtime.sign(signer.runtimeKey, transactionDigest);
290
+ ```
291
+
234
292
  ## Three paths, and why they stay explicit
235
293
 
236
294
  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.
package/dist/index.d.ts CHANGED
@@ -28,6 +28,9 @@
28
28
  * returns the shape structurally, because a dependency edge in either direction
29
29
  * would make each package need the other.
30
30
  */
31
+ import { VaultRuntime, type RuntimeRequest } from './runtime';
32
+ export { VaultRuntime } from './runtime';
33
+ export type { CreateRuntimeKeyOptions, RuntimeKeyInfo, SshCertificateOptions } from './runtime';
31
34
  export declare class VaultError extends Error {
32
35
  readonly code: string;
33
36
  constructor(code: string, message: string);
@@ -127,6 +130,16 @@ export interface VaultOptions {
127
130
  fetch?: typeof globalThis.fetch;
128
131
  signer?: Signer;
129
132
  agentTransport?: AgentTransport;
133
+ /**
134
+ * Separately authorized Vault-runtime transport for managed workloads.
135
+ *
136
+ * The ordinary Agent socket is deliberately read-only. A supervisor may
137
+ * inject a request function backed by a distinct systemd credential or a
138
+ * future capability-scoped runtime socket. The function receives paths
139
+ * relative to this project's runtime API and must enforce the exact machine
140
+ * grants itself. It is never inferred from Agent access.
141
+ */
142
+ runtimeRequest?: RuntimeRequest;
130
143
  credential?: Credential;
131
144
  discovery?: DiscoveryEnvironment;
132
145
  }
@@ -147,6 +160,7 @@ export declare const DEFAULT_API_URL = "https://api.forgezero.net";
147
160
  export declare const DEFAULT_REQUEST_TIMEOUT_MS = 15000;
148
161
  export declare class ForgeZero {
149
162
  readonly credential: Credential;
163
+ readonly runtime: VaultRuntime;
150
164
  private readonly apiUrl;
151
165
  private readonly doFetch;
152
166
  private readonly signer?;
@@ -282,4 +296,4 @@ export declare function systemdCredentials(options?: SystemdCredentialOptions):
282
296
  readonly name: 'systemd';
283
297
  get(reference: string, field: string): Promise<string>;
284
298
  };
285
- export declare const VERSION = "0.1.22";
299
+ export declare const VERSION = "0.1.24";
package/dist/index.js CHANGED
@@ -1,3 +1,124 @@
1
+ // src/runtime.ts
2
+ import {
3
+ fromBase64Url,
4
+ toBase64Url
5
+ } from "@forgezero/runtime/vault-runtime";
6
+
7
+ class VaultRuntimeClientError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "VaultRuntimeClientError";
13
+ }
14
+ }
15
+ var json = (value) => ({ method: "POST", body: JSON.stringify(value) });
16
+
17
+ class VaultRuntime {
18
+ request;
19
+ transport;
20
+ constructor(request, transport) {
21
+ this.request = request;
22
+ this.transport = transport;
23
+ }
24
+ available() {
25
+ if (this.transport === "managed-read-only") {
26
+ throw new VaultRuntimeClientError("MANAGED_RUNTIME_DELEGATION_REQUIRED", "Vault runtime operations require a separately granted external API key; the managed Agent replica is read-only.");
27
+ }
28
+ }
29
+ async create(options) {
30
+ this.available();
31
+ return this.request("/keys", json(options));
32
+ }
33
+ async rotate(runtimeKey) {
34
+ this.available();
35
+ return this.request(`/${encodeURIComponent(runtimeKey)}/rotate`, json({}));
36
+ }
37
+ async info(runtimeKey, version) {
38
+ this.available();
39
+ const query = version === undefined ? "" : `?version=${version}`;
40
+ return this.request(`/${encodeURIComponent(runtimeKey)}/public${query}`);
41
+ }
42
+ async password(runtimeKey, version) {
43
+ this.available();
44
+ return this.request(`/${encodeURIComponent(runtimeKey)}/secret`, json({ version }));
45
+ }
46
+ async encrypt(runtimeKey, plaintext, aad) {
47
+ this.available();
48
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
49
+ operation: "encrypt",
50
+ plaintext: toBase64Url(plaintext),
51
+ aad
52
+ }));
53
+ return { keyVersion: result.version, ciphertext: result.ciphertext };
54
+ }
55
+ async decrypt(runtimeKey, encrypted, aad) {
56
+ this.available();
57
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
58
+ operation: "decrypt",
59
+ ciphertext: encrypted.ciphertext,
60
+ aad,
61
+ version: encrypted.keyVersion
62
+ }));
63
+ return fromBase64Url(result.plaintext);
64
+ }
65
+ async wrap(runtimeKey, plaintextKey) {
66
+ this.available();
67
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
68
+ operation: "wrap",
69
+ plaintextKey: toBase64Url(plaintextKey)
70
+ }));
71
+ return { keyVersion: result.version, wrapped: result.wrapped };
72
+ }
73
+ async unwrap(runtimeKey, wrapped) {
74
+ this.available();
75
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
76
+ operation: "unwrap",
77
+ wrapped: wrapped.wrapped,
78
+ version: wrapped.keyVersion
79
+ }));
80
+ return fromBase64Url(result.plaintextKey);
81
+ }
82
+ async sign(runtimeKey, message) {
83
+ this.available();
84
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "sign", message: toBase64Url(message) }));
85
+ return { keyVersion: result.version, signature: fromBase64Url(result.signature) };
86
+ }
87
+ async verify(runtimeKey, message, signed) {
88
+ this.available();
89
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
90
+ operation: "verify",
91
+ message: toBase64Url(message),
92
+ signature: toBase64Url(signed.signature),
93
+ version: signed.keyVersion
94
+ }));
95
+ return result.valid;
96
+ }
97
+ async signJwt(runtimeKey, claims) {
98
+ this.available();
99
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "jwt-sign", claims }));
100
+ return { keyVersion: result.version, token: result.token };
101
+ }
102
+ async verifyJwt(runtimeKey, signed, options = {}) {
103
+ this.available();
104
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
105
+ operation: "jwt-verify",
106
+ token: signed.token,
107
+ version: signed.keyVersion,
108
+ ...options
109
+ }));
110
+ return result.claims;
111
+ }
112
+ async issueSshCertificate(runtimeKey, options) {
113
+ this.available();
114
+ return this.request(`/${encodeURIComponent(runtimeKey)}/ssh/certificates`, json({
115
+ ...options,
116
+ publicKey: toBase64Url(options.publicKey),
117
+ serial: options.serial?.toString()
118
+ }));
119
+ }
120
+ }
121
+
1
122
  // src/index.ts
2
123
  import {
3
124
  deriveKeysFromSeed,
@@ -7,7 +128,6 @@ import {
7
128
  RESPONSE_KEY_HEADER,
8
129
  signRequest
9
130
  } from "@forgezero/runtime/identity";
10
-
11
131
  class VaultError extends Error {
12
132
  code;
13
133
  constructor(code, message) {
@@ -136,6 +256,7 @@ function apiOrigin(value) {
136
256
 
137
257
  class ForgeZero {
138
258
  credential;
259
+ runtime;
139
260
  apiUrl;
140
261
  doFetch;
141
262
  signer;
@@ -156,6 +277,8 @@ class ForgeZero {
156
277
  }
157
278
  this.project = options.project ?? "default";
158
279
  this.environment = options.environment ?? "production";
280
+ const runtimeRequest = options.runtimeRequest ?? ((path, init) => this.request(`/v1/vault/${this.scope()}/runtime${path}`, init));
281
+ this.runtime = new VaultRuntime(runtimeRequest, this.credential.mode === "external" ? "external" : options.runtimeRequest ? "managed-delegated" : "managed-read-only");
159
282
  }
160
283
  scope() {
161
284
  return `${encodeURIComponent(this.project)}/${encodeURIComponent(this.environment)}`;
@@ -382,7 +505,7 @@ function systemdCredentials(options = {}) {
382
505
  }
383
506
  };
384
507
  }
385
- var VERSION = "0.1.22";
508
+ var VERSION = "0.1.24";
386
509
  export {
387
510
  vaultCredentials,
388
511
  systemdCredentials,
@@ -391,6 +514,7 @@ export {
391
514
  discover,
392
515
  directCredentials,
393
516
  createVault,
517
+ VaultRuntime,
394
518
  VaultError,
395
519
  VERSION,
396
520
  ForgeZero,
package/dist/providers.js CHANGED
@@ -1,3 +1,124 @@
1
+ // src/runtime.ts
2
+ import {
3
+ fromBase64Url,
4
+ toBase64Url
5
+ } from "@forgezero/runtime/vault-runtime";
6
+
7
+ class VaultRuntimeClientError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "VaultRuntimeClientError";
13
+ }
14
+ }
15
+ var json = (value) => ({ method: "POST", body: JSON.stringify(value) });
16
+
17
+ class VaultRuntime {
18
+ request;
19
+ transport;
20
+ constructor(request, transport) {
21
+ this.request = request;
22
+ this.transport = transport;
23
+ }
24
+ available() {
25
+ if (this.transport === "managed-read-only") {
26
+ throw new VaultRuntimeClientError("MANAGED_RUNTIME_DELEGATION_REQUIRED", "Vault runtime operations require a separately granted external API key; the managed Agent replica is read-only.");
27
+ }
28
+ }
29
+ async create(options) {
30
+ this.available();
31
+ return this.request("/keys", json(options));
32
+ }
33
+ async rotate(runtimeKey) {
34
+ this.available();
35
+ return this.request(`/${encodeURIComponent(runtimeKey)}/rotate`, json({}));
36
+ }
37
+ async info(runtimeKey, version) {
38
+ this.available();
39
+ const query = version === undefined ? "" : `?version=${version}`;
40
+ return this.request(`/${encodeURIComponent(runtimeKey)}/public${query}`);
41
+ }
42
+ async password(runtimeKey, version) {
43
+ this.available();
44
+ return this.request(`/${encodeURIComponent(runtimeKey)}/secret`, json({ version }));
45
+ }
46
+ async encrypt(runtimeKey, plaintext, aad) {
47
+ this.available();
48
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
49
+ operation: "encrypt",
50
+ plaintext: toBase64Url(plaintext),
51
+ aad
52
+ }));
53
+ return { keyVersion: result.version, ciphertext: result.ciphertext };
54
+ }
55
+ async decrypt(runtimeKey, encrypted, aad) {
56
+ this.available();
57
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
58
+ operation: "decrypt",
59
+ ciphertext: encrypted.ciphertext,
60
+ aad,
61
+ version: encrypted.keyVersion
62
+ }));
63
+ return fromBase64Url(result.plaintext);
64
+ }
65
+ async wrap(runtimeKey, plaintextKey) {
66
+ this.available();
67
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
68
+ operation: "wrap",
69
+ plaintextKey: toBase64Url(plaintextKey)
70
+ }));
71
+ return { keyVersion: result.version, wrapped: result.wrapped };
72
+ }
73
+ async unwrap(runtimeKey, wrapped) {
74
+ this.available();
75
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
76
+ operation: "unwrap",
77
+ wrapped: wrapped.wrapped,
78
+ version: wrapped.keyVersion
79
+ }));
80
+ return fromBase64Url(result.plaintextKey);
81
+ }
82
+ async sign(runtimeKey, message) {
83
+ this.available();
84
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "sign", message: toBase64Url(message) }));
85
+ return { keyVersion: result.version, signature: fromBase64Url(result.signature) };
86
+ }
87
+ async verify(runtimeKey, message, signed) {
88
+ this.available();
89
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
90
+ operation: "verify",
91
+ message: toBase64Url(message),
92
+ signature: toBase64Url(signed.signature),
93
+ version: signed.keyVersion
94
+ }));
95
+ return result.valid;
96
+ }
97
+ async signJwt(runtimeKey, claims) {
98
+ this.available();
99
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "jwt-sign", claims }));
100
+ return { keyVersion: result.version, token: result.token };
101
+ }
102
+ async verifyJwt(runtimeKey, signed, options = {}) {
103
+ this.available();
104
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
105
+ operation: "jwt-verify",
106
+ token: signed.token,
107
+ version: signed.keyVersion,
108
+ ...options
109
+ }));
110
+ return result.claims;
111
+ }
112
+ async issueSshCertificate(runtimeKey, options) {
113
+ this.available();
114
+ return this.request(`/${encodeURIComponent(runtimeKey)}/ssh/certificates`, json({
115
+ ...options,
116
+ publicKey: toBase64Url(options.publicKey),
117
+ serial: options.serial?.toString()
118
+ }));
119
+ }
120
+ }
121
+
1
122
  // src/index.ts
2
123
  import {
3
124
  deriveKeysFromSeed,
@@ -7,7 +128,6 @@ import {
7
128
  RESPONSE_KEY_HEADER,
8
129
  signRequest
9
130
  } from "@forgezero/runtime/identity";
10
-
11
131
  class VaultError extends Error {
12
132
  code;
13
133
  constructor(code, message) {
@@ -136,6 +256,7 @@ function apiOrigin(value) {
136
256
 
137
257
  class ForgeZero {
138
258
  credential;
259
+ runtime;
139
260
  apiUrl;
140
261
  doFetch;
141
262
  signer;
@@ -156,6 +277,8 @@ class ForgeZero {
156
277
  }
157
278
  this.project = options.project ?? "default";
158
279
  this.environment = options.environment ?? "production";
280
+ const runtimeRequest = options.runtimeRequest ?? ((path, init) => this.request(`/v1/vault/${this.scope()}/runtime${path}`, init));
281
+ this.runtime = new VaultRuntime(runtimeRequest, this.credential.mode === "external" ? "external" : options.runtimeRequest ? "managed-delegated" : "managed-read-only");
159
282
  }
160
283
  scope() {
161
284
  return `${encodeURIComponent(this.project)}/${encodeURIComponent(this.environment)}`;
@@ -382,7 +505,7 @@ function systemdCredentials(options = {}) {
382
505
  }
383
506
  };
384
507
  }
385
- var VERSION = "0.1.22";
508
+ var VERSION = "0.1.24";
386
509
 
387
510
  // src/providers.ts
388
511
  var MISSING = new Set(["ENTRY_NOT_FOUND", "VERSION_NOT_FOUND"]);
@@ -0,0 +1,97 @@
1
+ import { type RuntimeCiphertext, type RuntimeWrappedKey, type VaultRuntimeAlgorithm, type VaultRuntimeCreateDescriptor, type VaultRuntimeDescriptor, type VaultRuntimeKind } from '@forgezero/runtime/vault-runtime';
2
+ export type RuntimeRequest = <T>(path: string, init?: RequestInit) => Promise<T>;
3
+ export interface RuntimeKeyInfo {
4
+ runtimeKey: string;
5
+ version: number;
6
+ kind: VaultRuntimeKind;
7
+ algorithm: VaultRuntimeAlgorithm;
8
+ label: string;
9
+ publicKey?: string;
10
+ address?: string;
11
+ path?: string;
12
+ chain?: string;
13
+ network?: string;
14
+ sshPublicKey?: string;
15
+ }
16
+ export type CreateRuntimeKeyOptions = VaultRuntimeCreateDescriptor;
17
+ export interface SshCertificateOptions {
18
+ publicKey: Uint8Array;
19
+ type?: 1 | 2;
20
+ keyId: string;
21
+ principals: readonly string[];
22
+ validAfterSec: number;
23
+ validBeforeSec: number;
24
+ serial?: bigint;
25
+ }
26
+ export interface VersionedRuntimeCiphertext {
27
+ keyVersion: number;
28
+ ciphertext: RuntimeCiphertext;
29
+ }
30
+ export interface VersionedRuntimeWrappedKey {
31
+ keyVersion: number;
32
+ wrapped: RuntimeWrappedKey;
33
+ }
34
+ export interface VersionedRuntimeSignature {
35
+ keyVersion: number;
36
+ signature: Uint8Array;
37
+ }
38
+ export declare class VaultRuntimeClientError extends Error {
39
+ readonly code: 'MANAGED_RUNTIME_DELEGATION_REQUIRED';
40
+ constructor(code: 'MANAGED_RUNTIME_DELEGATION_REQUIRED', message: string);
41
+ }
42
+ /**
43
+ * How runtime operations reach the control plane.
44
+ *
45
+ * Managed secret replication remains read-only by default. A managed workload
46
+ * may use cryptographic runtime operations only when its supervisor injects a
47
+ * separate, capability-scoped request function. Keeping that delegation
48
+ * explicit prevents membership of the local Vault-reader group from silently
49
+ * granting signing, decryption or certificate authority.
50
+ */
51
+ export type VaultRuntimeTransport = 'external' | 'managed-read-only' | 'managed-delegated';
52
+ /**
53
+ * Explicit non-exporting cryptographic capabilities.
54
+ *
55
+ * This object is separate from ordinary Vault reads. Each method maps to a
56
+ * distinct API-key grant, so an application allowed to read a database URL is
57
+ * not silently able to sign transactions or decrypt application data.
58
+ */
59
+ export declare class VaultRuntime {
60
+ private readonly request;
61
+ private readonly transport;
62
+ constructor(request: RuntimeRequest, transport: VaultRuntimeTransport);
63
+ private available;
64
+ create(options: CreateRuntimeKeyOptions): Promise<RuntimeKeyInfo & {
65
+ created: boolean;
66
+ }>;
67
+ rotate(runtimeKey: string): Promise<RuntimeKeyInfo>;
68
+ info(runtimeKey: string, version?: number): Promise<RuntimeKeyInfo>;
69
+ password(runtimeKey: string, version?: number): Promise<{
70
+ password: string;
71
+ version: number;
72
+ }>;
73
+ encrypt(runtimeKey: string, plaintext: Uint8Array, aad: string): Promise<VersionedRuntimeCiphertext>;
74
+ decrypt(runtimeKey: string, encrypted: VersionedRuntimeCiphertext, aad: string): Promise<Uint8Array>;
75
+ wrap(runtimeKey: string, plaintextKey: Uint8Array): Promise<VersionedRuntimeWrappedKey>;
76
+ unwrap(runtimeKey: string, wrapped: VersionedRuntimeWrappedKey): Promise<Uint8Array>;
77
+ sign(runtimeKey: string, message: Uint8Array): Promise<VersionedRuntimeSignature>;
78
+ verify(runtimeKey: string, message: Uint8Array, signed: VersionedRuntimeSignature): Promise<boolean>;
79
+ signJwt(runtimeKey: string, claims: Record<string, unknown>): Promise<{
80
+ keyVersion: number;
81
+ token: string;
82
+ }>;
83
+ verifyJwt(runtimeKey: string, signed: {
84
+ keyVersion: number;
85
+ token: string;
86
+ }, options?: {
87
+ issuer?: string;
88
+ audience?: string;
89
+ }): Promise<Record<string, unknown>>;
90
+ issueSshCertificate(runtimeKey: string, options: SshCertificateOptions): Promise<{
91
+ version: number;
92
+ certificate: string;
93
+ caPublicKey: string;
94
+ subjectPublicKey: string;
95
+ }>;
96
+ }
97
+ export type { VaultRuntimeDescriptor };
@@ -0,0 +1,124 @@
1
+ // src/runtime.ts
2
+ import {
3
+ fromBase64Url,
4
+ toBase64Url
5
+ } from "@forgezero/runtime/vault-runtime";
6
+
7
+ class VaultRuntimeClientError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "VaultRuntimeClientError";
13
+ }
14
+ }
15
+ var json = (value) => ({ method: "POST", body: JSON.stringify(value) });
16
+
17
+ class VaultRuntime {
18
+ request;
19
+ transport;
20
+ constructor(request, transport) {
21
+ this.request = request;
22
+ this.transport = transport;
23
+ }
24
+ available() {
25
+ if (this.transport === "managed-read-only") {
26
+ throw new VaultRuntimeClientError("MANAGED_RUNTIME_DELEGATION_REQUIRED", "Vault runtime operations require a separately granted external API key; the managed Agent replica is read-only.");
27
+ }
28
+ }
29
+ async create(options) {
30
+ this.available();
31
+ return this.request("/keys", json(options));
32
+ }
33
+ async rotate(runtimeKey) {
34
+ this.available();
35
+ return this.request(`/${encodeURIComponent(runtimeKey)}/rotate`, json({}));
36
+ }
37
+ async info(runtimeKey, version) {
38
+ this.available();
39
+ const query = version === undefined ? "" : `?version=${version}`;
40
+ return this.request(`/${encodeURIComponent(runtimeKey)}/public${query}`);
41
+ }
42
+ async password(runtimeKey, version) {
43
+ this.available();
44
+ return this.request(`/${encodeURIComponent(runtimeKey)}/secret`, json({ version }));
45
+ }
46
+ async encrypt(runtimeKey, plaintext, aad) {
47
+ this.available();
48
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
49
+ operation: "encrypt",
50
+ plaintext: toBase64Url(plaintext),
51
+ aad
52
+ }));
53
+ return { keyVersion: result.version, ciphertext: result.ciphertext };
54
+ }
55
+ async decrypt(runtimeKey, encrypted, aad) {
56
+ this.available();
57
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
58
+ operation: "decrypt",
59
+ ciphertext: encrypted.ciphertext,
60
+ aad,
61
+ version: encrypted.keyVersion
62
+ }));
63
+ return fromBase64Url(result.plaintext);
64
+ }
65
+ async wrap(runtimeKey, plaintextKey) {
66
+ this.available();
67
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
68
+ operation: "wrap",
69
+ plaintextKey: toBase64Url(plaintextKey)
70
+ }));
71
+ return { keyVersion: result.version, wrapped: result.wrapped };
72
+ }
73
+ async unwrap(runtimeKey, wrapped) {
74
+ this.available();
75
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
76
+ operation: "unwrap",
77
+ wrapped: wrapped.wrapped,
78
+ version: wrapped.keyVersion
79
+ }));
80
+ return fromBase64Url(result.plaintextKey);
81
+ }
82
+ async sign(runtimeKey, message) {
83
+ this.available();
84
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "sign", message: toBase64Url(message) }));
85
+ return { keyVersion: result.version, signature: fromBase64Url(result.signature) };
86
+ }
87
+ async verify(runtimeKey, message, signed) {
88
+ this.available();
89
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
90
+ operation: "verify",
91
+ message: toBase64Url(message),
92
+ signature: toBase64Url(signed.signature),
93
+ version: signed.keyVersion
94
+ }));
95
+ return result.valid;
96
+ }
97
+ async signJwt(runtimeKey, claims) {
98
+ this.available();
99
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "jwt-sign", claims }));
100
+ return { keyVersion: result.version, token: result.token };
101
+ }
102
+ async verifyJwt(runtimeKey, signed, options = {}) {
103
+ this.available();
104
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
105
+ operation: "jwt-verify",
106
+ token: signed.token,
107
+ version: signed.keyVersion,
108
+ ...options
109
+ }));
110
+ return result.claims;
111
+ }
112
+ async issueSshCertificate(runtimeKey, options) {
113
+ this.available();
114
+ return this.request(`/${encodeURIComponent(runtimeKey)}/ssh/certificates`, json({
115
+ ...options,
116
+ publicKey: toBase64Url(options.publicKey),
117
+ serial: options.serial?.toString()
118
+ }));
119
+ }
120
+ }
121
+ export {
122
+ VaultRuntimeClientError,
123
+ VaultRuntime
124
+ };
package/dist/schema.js CHANGED
@@ -1,3 +1,124 @@
1
+ // src/runtime.ts
2
+ import {
3
+ fromBase64Url,
4
+ toBase64Url
5
+ } from "@forgezero/runtime/vault-runtime";
6
+
7
+ class VaultRuntimeClientError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "VaultRuntimeClientError";
13
+ }
14
+ }
15
+ var json = (value) => ({ method: "POST", body: JSON.stringify(value) });
16
+
17
+ class VaultRuntime {
18
+ request;
19
+ transport;
20
+ constructor(request, transport) {
21
+ this.request = request;
22
+ this.transport = transport;
23
+ }
24
+ available() {
25
+ if (this.transport === "managed-read-only") {
26
+ throw new VaultRuntimeClientError("MANAGED_RUNTIME_DELEGATION_REQUIRED", "Vault runtime operations require a separately granted external API key; the managed Agent replica is read-only.");
27
+ }
28
+ }
29
+ async create(options) {
30
+ this.available();
31
+ return this.request("/keys", json(options));
32
+ }
33
+ async rotate(runtimeKey) {
34
+ this.available();
35
+ return this.request(`/${encodeURIComponent(runtimeKey)}/rotate`, json({}));
36
+ }
37
+ async info(runtimeKey, version) {
38
+ this.available();
39
+ const query = version === undefined ? "" : `?version=${version}`;
40
+ return this.request(`/${encodeURIComponent(runtimeKey)}/public${query}`);
41
+ }
42
+ async password(runtimeKey, version) {
43
+ this.available();
44
+ return this.request(`/${encodeURIComponent(runtimeKey)}/secret`, json({ version }));
45
+ }
46
+ async encrypt(runtimeKey, plaintext, aad) {
47
+ this.available();
48
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
49
+ operation: "encrypt",
50
+ plaintext: toBase64Url(plaintext),
51
+ aad
52
+ }));
53
+ return { keyVersion: result.version, ciphertext: result.ciphertext };
54
+ }
55
+ async decrypt(runtimeKey, encrypted, aad) {
56
+ this.available();
57
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
58
+ operation: "decrypt",
59
+ ciphertext: encrypted.ciphertext,
60
+ aad,
61
+ version: encrypted.keyVersion
62
+ }));
63
+ return fromBase64Url(result.plaintext);
64
+ }
65
+ async wrap(runtimeKey, plaintextKey) {
66
+ this.available();
67
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
68
+ operation: "wrap",
69
+ plaintextKey: toBase64Url(plaintextKey)
70
+ }));
71
+ return { keyVersion: result.version, wrapped: result.wrapped };
72
+ }
73
+ async unwrap(runtimeKey, wrapped) {
74
+ this.available();
75
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/crypt`, json({
76
+ operation: "unwrap",
77
+ wrapped: wrapped.wrapped,
78
+ version: wrapped.keyVersion
79
+ }));
80
+ return fromBase64Url(result.plaintextKey);
81
+ }
82
+ async sign(runtimeKey, message) {
83
+ this.available();
84
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "sign", message: toBase64Url(message) }));
85
+ return { keyVersion: result.version, signature: fromBase64Url(result.signature) };
86
+ }
87
+ async verify(runtimeKey, message, signed) {
88
+ this.available();
89
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
90
+ operation: "verify",
91
+ message: toBase64Url(message),
92
+ signature: toBase64Url(signed.signature),
93
+ version: signed.keyVersion
94
+ }));
95
+ return result.valid;
96
+ }
97
+ async signJwt(runtimeKey, claims) {
98
+ this.available();
99
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({ operation: "jwt-sign", claims }));
100
+ return { keyVersion: result.version, token: result.token };
101
+ }
102
+ async verifyJwt(runtimeKey, signed, options = {}) {
103
+ this.available();
104
+ const result = await this.request(`/${encodeURIComponent(runtimeKey)}/sign`, json({
105
+ operation: "jwt-verify",
106
+ token: signed.token,
107
+ version: signed.keyVersion,
108
+ ...options
109
+ }));
110
+ return result.claims;
111
+ }
112
+ async issueSshCertificate(runtimeKey, options) {
113
+ this.available();
114
+ return this.request(`/${encodeURIComponent(runtimeKey)}/ssh/certificates`, json({
115
+ ...options,
116
+ publicKey: toBase64Url(options.publicKey),
117
+ serial: options.serial?.toString()
118
+ }));
119
+ }
120
+ }
121
+
1
122
  // src/index.ts
2
123
  import {
3
124
  deriveKeysFromSeed,
@@ -7,7 +128,6 @@ import {
7
128
  RESPONSE_KEY_HEADER,
8
129
  signRequest
9
130
  } from "@forgezero/runtime/identity";
10
-
11
131
  class VaultError extends Error {
12
132
  code;
13
133
  constructor(code, message) {
@@ -136,6 +256,7 @@ function apiOrigin(value) {
136
256
 
137
257
  class ForgeZero {
138
258
  credential;
259
+ runtime;
139
260
  apiUrl;
140
261
  doFetch;
141
262
  signer;
@@ -156,6 +277,8 @@ class ForgeZero {
156
277
  }
157
278
  this.project = options.project ?? "default";
158
279
  this.environment = options.environment ?? "production";
280
+ const runtimeRequest = options.runtimeRequest ?? ((path, init) => this.request(`/v1/vault/${this.scope()}/runtime${path}`, init));
281
+ this.runtime = new VaultRuntime(runtimeRequest, this.credential.mode === "external" ? "external" : options.runtimeRequest ? "managed-delegated" : "managed-read-only");
159
282
  }
160
283
  scope() {
161
284
  return `${encodeURIComponent(this.project)}/${encodeURIComponent(this.environment)}`;
@@ -382,7 +505,7 @@ function systemdCredentials(options = {}) {
382
505
  }
383
506
  };
384
507
  }
385
- var VERSION = "0.1.22";
508
+ var VERSION = "0.1.24";
386
509
 
387
510
  // src/schema.ts
388
511
  function managedSchemas(vault, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/vault",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -30,6 +30,10 @@
30
30
  "./providers": {
31
31
  "types": "./dist/providers.d.ts",
32
32
  "default": "./dist/providers.js"
33
+ },
34
+ "./runtime": {
35
+ "types": "./dist/runtime.d.ts",
36
+ "default": "./dist/runtime.js"
33
37
  }
34
38
  },
35
39
  "scripts": {
@@ -42,12 +46,14 @@
42
46
  "@types/bun": "latest"
43
47
  },
44
48
  "dependencies": {
45
- "@forgezero/runtime": "^0.1.17"
49
+ "@forgezero/runtime": "^0.1.18"
46
50
  },
47
51
  "peerDependencies": {
52
+ "@noble/ciphers": "^2.2.0",
48
53
  "@noble/curves": "^2.2.0",
49
54
  "@noble/post-quantum": "^0.6.1",
50
- "@noble/hashes": "^2.2.0"
55
+ "@noble/hashes": "^2.2.0",
56
+ "@scure/base": "^2.2.0"
51
57
  },
52
58
  "description": "ForgeZero vault client. Credential discovery and versioned secrets through one stable API origin.",
53
59
  "keywords": [