@forgezero/vault 0.1.22 → 0.1.23
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 +58 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.js +126 -2
- package/dist/providers.js +125 -2
- package/dist/runtime.d.ts +97 -0
- package/dist/runtime.js +124 -0
- package/dist/schema.js +125 -2
- package/package.json +6 -2
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.
|
|
299
|
+
export declare const VERSION = "0.1.23";
|
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.
|
|
508
|
+
var VERSION = "0.1.23";
|
|
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.
|
|
508
|
+
var VERSION = "0.1.23";
|
|
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 };
|
package/dist/runtime.js
ADDED
|
@@ -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.
|
|
508
|
+
var VERSION = "0.1.23";
|
|
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.
|
|
3
|
+
"version": "0.1.23",
|
|
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,7 +46,7 @@
|
|
|
42
46
|
"@types/bun": "latest"
|
|
43
47
|
},
|
|
44
48
|
"dependencies": {
|
|
45
|
-
"@forgezero/runtime": "^0.1.
|
|
49
|
+
"@forgezero/runtime": "^0.1.18"
|
|
46
50
|
},
|
|
47
51
|
"peerDependencies": {
|
|
48
52
|
"@noble/curves": "^2.2.0",
|