@forgezero/agent 0.1.18 → 0.1.20
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 +15 -17
- package/dist/cache.d.ts +2 -1
- package/dist/fz-agent.js +20 -15
- package/dist/fz.js +1 -1
- package/dist/guest-enrolment.js +9 -12
- package/dist/index.d.ts +1 -1
- package/dist/node-vault.js +9 -12
- package/dist/provisioning-pull.js +8 -11
- package/dist/signed-node-http.d.ts +1 -1
- package/dist/socket.d.ts +10 -14
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,10 +90,11 @@ evidence: report acquisition and AMD-rooted measurement verification still have
|
|
|
90
90
|
to cross the production fleet. Until then the node is honestly `enrolled`; the
|
|
91
91
|
Agent never manufactures an attestation-shaped fallback.
|
|
92
92
|
|
|
93
|
-
Every
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
there is no Ed25519-only or
|
|
93
|
+
Every Agent request uses the same hybrid signature codec as an external API key
|
|
94
|
+
and signs its one-use hybrid ML-KEM-768 + X25519 response key. Vault replication,
|
|
95
|
+
deployment claims, attestation and metal provisioning responses are AES-256-GCM
|
|
96
|
+
sealed to that request before crossing the edge; there is no Ed25519-only or
|
|
97
|
+
successful plaintext-response downgrade.
|
|
97
98
|
|
|
98
99
|
The first guest enrolment uses the same contract before the identity exists in
|
|
99
100
|
the database: the guest signs the exact token and public-key body with both keys
|
|
@@ -103,23 +104,19 @@ acknowledgement. The short-lived token is therefore not an unsigned Agent path.
|
|
|
103
104
|
## The socket interface
|
|
104
105
|
|
|
105
106
|
```
|
|
106
|
-
identity who this node is
|
|
107
|
-
sign sign a request with the node key
|
|
108
|
-
attest a hardware report, or a refusal — never a fake
|
|
109
107
|
get one secret
|
|
110
108
|
sync what changed since a cursor
|
|
111
109
|
held what this guest is holding, by name only
|
|
112
110
|
```
|
|
113
111
|
|
|
114
|
-
The
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
when nothing produced one is worse off than one told plainly it is unavailable.
|
|
112
|
+
The application Vault socket never accepts identity, signing, attestation,
|
|
113
|
+
commands or deployment definitions. The Agent signs and attests only inside its
|
|
114
|
+
own outbound clients. Otherwise a compromised application could ask for a valid
|
|
115
|
+
node signature over a deployment or whole-project sync request and impersonate
|
|
116
|
+
the Agent without ever extracting its key. Deployment enters through the
|
|
117
|
+
root-only control socket or signed outbound claim path, then the deployment
|
|
118
|
+
manager validates the checked-in definition and submits it to the common keyed
|
|
119
|
+
queue.
|
|
123
120
|
|
|
124
121
|
## Deployment definitions
|
|
125
122
|
|
|
@@ -139,7 +136,8 @@ code, and only an awaited successful pipeline writes `deployed`. An expired
|
|
|
139
136
|
claim can be recovered; its stale token cannot renew or finish.
|
|
140
137
|
|
|
141
138
|
The same package also runs the identity-only Metal Agent. It initiates outbound
|
|
142
|
-
signed HTTPS
|
|
139
|
+
hybrid-signed HTTPS and requires request-bound PQ-sealed responses while pulling
|
|
140
|
+
only claims assigned to its enrolled hostname, then passes
|
|
143
141
|
the fixed claim over a local Unix socket to a narrowly privileged root helper.
|
|
144
142
|
The helper can materialize the audited QEMU profile; it cannot clone a project,
|
|
145
143
|
read Vault data, accept arbitrary commands, or retain tenant credentials. Root
|
package/dist/cache.d.ts
CHANGED
|
@@ -60,7 +60,8 @@ export interface CacheOptions {
|
|
|
60
60
|
* Every name in the assigned scope. Present means REPLICA mode.
|
|
61
61
|
*
|
|
62
62
|
* Absent means the old behaviour — fetch on demand, hold what was asked for.
|
|
63
|
-
* Present means the agent pulls the whole project
|
|
63
|
+
* Present means the agent pulls the whole bound project across every
|
|
64
|
+
* environment at startup and
|
|
64
65
|
* on every resync, so an application reads with no round trip and keeps
|
|
65
66
|
* working while the platform is unreachable.
|
|
66
67
|
*
|
package/dist/fz-agent.js
CHANGED
|
@@ -123,19 +123,19 @@ class SignedNodeHttpError extends Error {
|
|
|
123
123
|
this.name = "SignedNodeHttpError";
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
async function postSignedNode(options, path, body
|
|
126
|
+
async function postSignedNode(options, path, body) {
|
|
127
127
|
const url = new URL(options.apiUrl);
|
|
128
128
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/${path.replace(/^\//, "")}`.replace(/\/+/g, "/");
|
|
129
129
|
url.search = "";
|
|
130
130
|
url.hash = "";
|
|
131
131
|
const raw = JSON.stringify(body);
|
|
132
|
-
const recipient =
|
|
132
|
+
const recipient = generateResponseRecipient();
|
|
133
133
|
const envelope = signRequest(options.keys, options.nodeKey, {
|
|
134
134
|
method: "POST",
|
|
135
135
|
path: url.pathname,
|
|
136
136
|
query: "",
|
|
137
137
|
body: raw,
|
|
138
|
-
responseKey: recipient
|
|
138
|
+
responseKey: recipient.publicKey
|
|
139
139
|
});
|
|
140
140
|
const signature = encodeSignatureHeader(envelope);
|
|
141
141
|
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
@@ -144,7 +144,7 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
144
144
|
"content-type": "application/json",
|
|
145
145
|
"x-fz-node": options.nodeKey,
|
|
146
146
|
"x-fz-signature": signature,
|
|
147
|
-
|
|
147
|
+
[RESPONSE_KEY_HEADER]: recipient.publicKey
|
|
148
148
|
},
|
|
149
149
|
body: raw,
|
|
150
150
|
signal: AbortSignal.timeout(options.requestTimeoutMs ?? 15000)
|
|
@@ -155,14 +155,11 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
155
155
|
const reason = failure ? failure.error?.message ?? failure.message : undefined;
|
|
156
156
|
throw new SignedNodeHttpError(response.status, reason || `signed node request returned HTTP ${response.status}`);
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
163
|
-
}
|
|
158
|
+
try {
|
|
159
|
+
return await openResponse(recipient.secretKey, signature, payload);
|
|
160
|
+
} catch {
|
|
161
|
+
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
164
162
|
}
|
|
165
|
-
return payload;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
// src/node-vault.ts
|
|
@@ -194,7 +191,7 @@ function tenantNodeApiUrl(apiUrl, tenantSlug) {
|
|
|
194
191
|
return url.toString().replace(/\/$/, "");
|
|
195
192
|
}
|
|
196
193
|
function createNodeVaultCache(options) {
|
|
197
|
-
const post = (operation, body) => postSignedNode(options, `v1/node/vault/${operation}`, body
|
|
194
|
+
const post = (operation, body) => postSignedNode(options, `v1/node/vault/${operation}`, body);
|
|
198
195
|
return createSecretCache({
|
|
199
196
|
ttlMs: options.ttlMs,
|
|
200
197
|
maxStaleMs: options.maxStaleMs,
|
|
@@ -255,6 +252,13 @@ function startNodeVaultSync(cache, options = {}) {
|
|
|
255
252
|
}
|
|
256
253
|
|
|
257
254
|
// src/socket.ts
|
|
255
|
+
var APPLICATION_VAULT_OPS = new Set(["get", "held", "sync"]);
|
|
256
|
+
function handleApplicationRequest(options, request) {
|
|
257
|
+
if (!APPLICATION_VAULT_OPS.has(request?.op)) {
|
|
258
|
+
return Promise.resolve(refuse("APP_OPERATION_REFUSED", "The application Vault socket serves scoped replica reads only; node signing and attestation are internal."));
|
|
259
|
+
}
|
|
260
|
+
return handleRequest(options, request);
|
|
261
|
+
}
|
|
258
262
|
var MAX_LINE_BYTES = 64 * 1024;
|
|
259
263
|
function handleRequest(options, request) {
|
|
260
264
|
switch (request?.op) {
|
|
@@ -390,7 +394,7 @@ async function respond(options, socket, line) {
|
|
|
390
394
|
return;
|
|
391
395
|
let response;
|
|
392
396
|
try {
|
|
393
|
-
response = await
|
|
397
|
+
response = await handleApplicationRequest(options, JSON.parse(line));
|
|
394
398
|
} catch (cause) {
|
|
395
399
|
response = refuse("MALFORMED", cause instanceof Error ? cause.message : "Malformed request.");
|
|
396
400
|
}
|
|
@@ -1209,7 +1213,7 @@ async function enrolGuestIdentity(options) {
|
|
|
1209
1213
|
ed25519: options.keys.ed25519.publicKey,
|
|
1210
1214
|
mlDsa: options.keys.mlDsa.publicKey
|
|
1211
1215
|
}
|
|
1212
|
-
}
|
|
1216
|
+
});
|
|
1213
1217
|
if (!payload?.ok || payload.nodeKey !== options.nodeKey || !payload.computeReference || !payload.projectKey || !payload.environmentKey || payload.realm !== "platform" && (payload.realm !== "tenant" || !payload.tenantSlug)) {
|
|
1214
1218
|
throw new Error(payload?.error?.message || "guest enrolment response was not accepted");
|
|
1215
1219
|
}
|
|
@@ -2711,7 +2715,7 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
|
|
|
2711
2715
|
}
|
|
2712
2716
|
|
|
2713
2717
|
// src/version.ts
|
|
2714
|
-
var VERSION = "0.1.
|
|
2718
|
+
var VERSION = "0.1.20";
|
|
2715
2719
|
|
|
2716
2720
|
// src/index.ts
|
|
2717
2721
|
function loadOrCreateSeed(path) {
|
|
@@ -3215,6 +3219,7 @@ export {
|
|
|
3215
3219
|
loadOrCreateSeed,
|
|
3216
3220
|
loadGuestBinding,
|
|
3217
3221
|
handleRequest,
|
|
3222
|
+
handleApplicationRequest,
|
|
3218
3223
|
guestNameFor,
|
|
3219
3224
|
enrolGuestIdentity,
|
|
3220
3225
|
createSystemdDeploymentSecrets,
|
package/dist/fz.js
CHANGED
package/dist/guest-enrolment.js
CHANGED
|
@@ -28,19 +28,19 @@ class SignedNodeHttpError extends Error {
|
|
|
28
28
|
this.name = "SignedNodeHttpError";
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
async function postSignedNode(options, path, body
|
|
31
|
+
async function postSignedNode(options, path, body) {
|
|
32
32
|
const url = new URL(options.apiUrl);
|
|
33
33
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/${path.replace(/^\//, "")}`.replace(/\/+/g, "/");
|
|
34
34
|
url.search = "";
|
|
35
35
|
url.hash = "";
|
|
36
36
|
const raw = JSON.stringify(body);
|
|
37
|
-
const recipient =
|
|
37
|
+
const recipient = generateResponseRecipient();
|
|
38
38
|
const envelope = signRequest(options.keys, options.nodeKey, {
|
|
39
39
|
method: "POST",
|
|
40
40
|
path: url.pathname,
|
|
41
41
|
query: "",
|
|
42
42
|
body: raw,
|
|
43
|
-
responseKey: recipient
|
|
43
|
+
responseKey: recipient.publicKey
|
|
44
44
|
});
|
|
45
45
|
const signature = encodeSignatureHeader(envelope);
|
|
46
46
|
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
@@ -49,7 +49,7 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
49
49
|
"content-type": "application/json",
|
|
50
50
|
"x-fz-node": options.nodeKey,
|
|
51
51
|
"x-fz-signature": signature,
|
|
52
|
-
|
|
52
|
+
[RESPONSE_KEY_HEADER]: recipient.publicKey
|
|
53
53
|
},
|
|
54
54
|
body: raw,
|
|
55
55
|
signal: AbortSignal.timeout(options.requestTimeoutMs ?? 15000)
|
|
@@ -60,14 +60,11 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
60
60
|
const reason = failure ? failure.error?.message ?? failure.message : undefined;
|
|
61
61
|
throw new SignedNodeHttpError(response.status, reason || `signed node request returned HTTP ${response.status}`);
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
68
|
-
}
|
|
63
|
+
try {
|
|
64
|
+
return await openResponse(recipient.secretKey, signature, payload);
|
|
65
|
+
} catch {
|
|
66
|
+
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
69
67
|
}
|
|
70
|
-
return payload;
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
// src/guest-enrolment.ts
|
|
@@ -121,7 +118,7 @@ async function enrolGuestIdentity(options) {
|
|
|
121
118
|
ed25519: options.keys.ed25519.publicKey,
|
|
122
119
|
mlDsa: options.keys.mlDsa.publicKey
|
|
123
120
|
}
|
|
124
|
-
}
|
|
121
|
+
});
|
|
125
122
|
if (!payload?.ok || payload.nodeKey !== options.nodeKey || !payload.computeReference || !payload.projectKey || !payload.environmentKey || payload.realm !== "platform" && (payload.realm !== "tenant" || !payload.tenantSlug)) {
|
|
126
123
|
throw new Error(payload?.error?.message || "guest enrolment response was not accepted");
|
|
127
124
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type NodeKeyPair } from '@forgezero/runtime/identity';
|
|
|
3
3
|
import { startAgent, type AgentOptions, type AttestationSource } from './socket';
|
|
4
4
|
import type { SecretCache } from './cache';
|
|
5
5
|
export { VERSION } from './version';
|
|
6
|
-
export { startAgent, handleRequest } from './socket';
|
|
6
|
+
export { startAgent, handleRequest, handleApplicationRequest } from './socket';
|
|
7
7
|
export type { AgentOptions, AttestationSource, Request, Response } from './socket';
|
|
8
8
|
export { createSecretCache, CacheError } from './cache';
|
|
9
9
|
export type { SecretCache, CacheOptions } from './cache';
|
package/dist/node-vault.js
CHANGED
|
@@ -108,19 +108,19 @@ class SignedNodeHttpError extends Error {
|
|
|
108
108
|
this.name = "SignedNodeHttpError";
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
async function postSignedNode(options, path, body
|
|
111
|
+
async function postSignedNode(options, path, body) {
|
|
112
112
|
const url = new URL(options.apiUrl);
|
|
113
113
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/${path.replace(/^\//, "")}`.replace(/\/+/g, "/");
|
|
114
114
|
url.search = "";
|
|
115
115
|
url.hash = "";
|
|
116
116
|
const raw = JSON.stringify(body);
|
|
117
|
-
const recipient =
|
|
117
|
+
const recipient = generateResponseRecipient();
|
|
118
118
|
const envelope = signRequest(options.keys, options.nodeKey, {
|
|
119
119
|
method: "POST",
|
|
120
120
|
path: url.pathname,
|
|
121
121
|
query: "",
|
|
122
122
|
body: raw,
|
|
123
|
-
responseKey: recipient
|
|
123
|
+
responseKey: recipient.publicKey
|
|
124
124
|
});
|
|
125
125
|
const signature = encodeSignatureHeader(envelope);
|
|
126
126
|
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
@@ -129,7 +129,7 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
129
129
|
"content-type": "application/json",
|
|
130
130
|
"x-fz-node": options.nodeKey,
|
|
131
131
|
"x-fz-signature": signature,
|
|
132
|
-
|
|
132
|
+
[RESPONSE_KEY_HEADER]: recipient.publicKey
|
|
133
133
|
},
|
|
134
134
|
body: raw,
|
|
135
135
|
signal: AbortSignal.timeout(options.requestTimeoutMs ?? 15000)
|
|
@@ -140,14 +140,11 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
140
140
|
const reason = failure ? failure.error?.message ?? failure.message : undefined;
|
|
141
141
|
throw new SignedNodeHttpError(response.status, reason || `signed node request returned HTTP ${response.status}`);
|
|
142
142
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
148
|
-
}
|
|
143
|
+
try {
|
|
144
|
+
return await openResponse(recipient.secretKey, signature, payload);
|
|
145
|
+
} catch {
|
|
146
|
+
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
149
147
|
}
|
|
150
|
-
return payload;
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
// src/node-vault.ts
|
|
@@ -179,7 +176,7 @@ function tenantNodeApiUrl(apiUrl, tenantSlug) {
|
|
|
179
176
|
return url.toString().replace(/\/$/, "");
|
|
180
177
|
}
|
|
181
178
|
function createNodeVaultCache(options) {
|
|
182
|
-
const post = (operation, body) => postSignedNode(options, `v1/node/vault/${operation}`, body
|
|
179
|
+
const post = (operation, body) => postSignedNode(options, `v1/node/vault/${operation}`, body);
|
|
183
180
|
return createSecretCache({
|
|
184
181
|
ttlMs: options.ttlMs,
|
|
185
182
|
maxStaleMs: options.maxStaleMs,
|
|
@@ -15,19 +15,19 @@ class SignedNodeHttpError extends Error {
|
|
|
15
15
|
this.name = "SignedNodeHttpError";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
async function postSignedNode(options, path, body
|
|
18
|
+
async function postSignedNode(options, path, body) {
|
|
19
19
|
const url = new URL(options.apiUrl);
|
|
20
20
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/${path.replace(/^\//, "")}`.replace(/\/+/g, "/");
|
|
21
21
|
url.search = "";
|
|
22
22
|
url.hash = "";
|
|
23
23
|
const raw = JSON.stringify(body);
|
|
24
|
-
const recipient =
|
|
24
|
+
const recipient = generateResponseRecipient();
|
|
25
25
|
const envelope = signRequest(options.keys, options.nodeKey, {
|
|
26
26
|
method: "POST",
|
|
27
27
|
path: url.pathname,
|
|
28
28
|
query: "",
|
|
29
29
|
body: raw,
|
|
30
|
-
responseKey: recipient
|
|
30
|
+
responseKey: recipient.publicKey
|
|
31
31
|
});
|
|
32
32
|
const signature = encodeSignatureHeader(envelope);
|
|
33
33
|
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
@@ -36,7 +36,7 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
36
36
|
"content-type": "application/json",
|
|
37
37
|
"x-fz-node": options.nodeKey,
|
|
38
38
|
"x-fz-signature": signature,
|
|
39
|
-
|
|
39
|
+
[RESPONSE_KEY_HEADER]: recipient.publicKey
|
|
40
40
|
},
|
|
41
41
|
body: raw,
|
|
42
42
|
signal: AbortSignal.timeout(options.requestTimeoutMs ?? 15000)
|
|
@@ -47,14 +47,11 @@ async function postSignedNode(options, path, body, sealedResponse = false) {
|
|
|
47
47
|
const reason = failure ? failure.error?.message ?? failure.message : undefined;
|
|
48
48
|
throw new SignedNodeHttpError(response.status, reason || `signed node request returned HTTP ${response.status}`);
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
55
|
-
}
|
|
50
|
+
try {
|
|
51
|
+
return await openResponse(recipient.secretKey, signature, payload);
|
|
52
|
+
} catch {
|
|
53
|
+
throw new SignedNodeHttpError(502, "The node response was not sealed to this request.");
|
|
56
54
|
}
|
|
57
|
-
return payload;
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
// src/provisioning-pull.ts
|
|
@@ -11,4 +11,4 @@ export declare class SignedNodeHttpError extends Error {
|
|
|
11
11
|
constructor(status: number, message: string);
|
|
12
12
|
}
|
|
13
13
|
/** One implementation of the hybrid-signed machine HTTP contract. */
|
|
14
|
-
export declare function postSignedNode<T>(options: SignedNodeHttpOptions, path: string, body: object
|
|
14
|
+
export declare function postSignedNode<T>(options: SignedNodeHttpOptions, path: string, body: object): Promise<T>;
|
package/dist/socket.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Server } from 'node:net';
|
|
|
2
2
|
import { signRequest, type NodeKeyPair } from '@forgezero/runtime/identity';
|
|
3
3
|
import type { SecretCache } from './cache';
|
|
4
4
|
/**
|
|
5
|
-
* The
|
|
5
|
+
* The application Vault socket — why managed code holds no remote credential.
|
|
6
6
|
*
|
|
7
7
|
* `@forgezero/vault` discovers `/run/forgezero/vault.sock` and prefers it over
|
|
8
8
|
* `FORGEZERO_API_KEY`, so moving an app onto managed compute is DELETING an
|
|
@@ -10,20 +10,14 @@ import type { SecretCache } from './cache';
|
|
|
10
10
|
* for a long time nothing was: the client knew how to prefer the socket and the
|
|
11
11
|
* agent was a version constant.
|
|
12
12
|
*
|
|
13
|
-
* ## The
|
|
13
|
+
* ## The node identity never becomes an application signing oracle
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* A token would have to be handed to the caller, which puts it in a process we
|
|
23
|
-
* do not control, in memory we do not own, for as long as the process lives. A
|
|
24
|
-
* signature is bound to one method, one path and one body digest by
|
|
25
|
-
* `canonicalString`, so a caller that captures one cannot reuse it for anything
|
|
26
|
-
* else — and it expires with the clock-skew window rather than with the process.
|
|
15
|
+
* The Agent hybrid-signs its own outbound HTTPS requests internally. This
|
|
16
|
+
* socket never exposes `identity`, `sign` or `attest`: otherwise any tenant
|
|
17
|
+
* process admitted to the Vault group could ask the Agent to sign a deployment,
|
|
18
|
+
* heartbeat or whole-project replication request and act as the node without
|
|
19
|
+
* ever extracting its key. Applications receive only the environment they ask
|
|
20
|
+
* for from the already-bound, memory-only project replica.
|
|
27
21
|
*
|
|
28
22
|
* ## Peer credentials, not a shared secret
|
|
29
23
|
*
|
|
@@ -99,6 +93,8 @@ export type Response = {
|
|
|
99
93
|
message: string;
|
|
100
94
|
};
|
|
101
95
|
};
|
|
96
|
+
/** The real Unix-socket boundary; privileged machine operations stay in-process. */
|
|
97
|
+
export declare function handleApplicationRequest(options: AgentOptions, request: Request): Promise<Response>;
|
|
102
98
|
/**
|
|
103
99
|
* Produces a hardware attestation report for this guest.
|
|
104
100
|
*
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.20";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "Publishing happens from an operator's machine, not CI \u2014 CLAUDE.md records that the absence of CI is deliberate. npm's `provenance` attests a tarball was built by a recognised CI provider from a named commit, so it cannot be produced here: it was set, and the first publish failed with `Automatic provenance generation not supported for provider: null`. A setting that can never be satisfied is worse than none, because it reads as a guarantee nobody is getting. Restore it the day this publishes from CI, and not before.",
|
|
3
3
|
"name": "@forgezero/agent",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.20",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"check": "tsc --noEmit",
|