@serfab/cadre-host 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/self-label.d.ts +36 -0
- package/dist/auth/self-label.js +57 -0
- package/dist/auth/self-label.js.map +1 -0
- package/dist/auth/trust-circle.d.ts +36 -9
- package/dist/auth/trust-circle.js +40 -20
- package/dist/auth/trust-circle.js.map +1 -1
- package/dist/auth/types.d.ts +2 -2
- package/dist/bin/host.js +291 -54
- package/dist/bin/host.js.map +1 -1
- package/dist/donation/__tests__/fake-orchestrator.d.ts +103 -0
- package/dist/donation/__tests__/fake-orchestrator.js +180 -0
- package/dist/donation/__tests__/fake-orchestrator.js.map +1 -0
- package/dist/donation/donation-service.d.ts +361 -0
- package/dist/donation/donation-service.js +763 -0
- package/dist/donation/donation-service.js.map +1 -0
- package/dist/donation/donation-store.d.ts +49 -0
- package/dist/donation/donation-store.js +148 -0
- package/dist/donation/donation-store.js.map +1 -0
- package/dist/donation/donation-supervisor.d.ts +167 -0
- package/dist/donation/donation-supervisor.js +341 -0
- package/dist/donation/donation-supervisor.js.map +1 -0
- package/dist/donation/grant-service.d.ts +71 -0
- package/dist/donation/grant-service.js +163 -0
- package/dist/donation/grant-service.js.map +1 -0
- package/dist/donation/grant-store.d.ts +38 -0
- package/dist/donation/grant-store.js +135 -0
- package/dist/donation/grant-store.js.map +1 -0
- package/dist/donation/index.d.ts +20 -0
- package/dist/donation/index.js +17 -0
- package/dist/donation/index.js.map +1 -0
- package/dist/donation/types.d.ts +201 -0
- package/dist/donation/types.js +40 -0
- package/dist/donation/types.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/installer/config.d.ts +26 -0
- package/dist/installer/config.js +13 -0
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +2 -0
- package/dist/installer/index.js +3 -0
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/wizard.d.ts +4 -0
- package/dist/installer/wizard.js +5 -1
- package/dist/installer/wizard.js.map +1 -1
- package/dist/nat/nat-service.d.ts +5 -5
- package/dist/nat/nat-service.js +9 -9
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/types.d.ts +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +144 -32
- package/dist/orchestrator/host-process-orchestrator.js +397 -128
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/orchestrator/index.d.ts +3 -3
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/node-identity.d.ts +26 -0
- package/dist/orchestrator/node-identity.js +36 -0
- package/dist/orchestrator/node-identity.js.map +1 -0
- package/dist/orchestrator/port-allocator.d.ts +14 -0
- package/dist/orchestrator/port-allocator.js +45 -0
- package/dist/orchestrator/port-allocator.js.map +1 -1
- package/dist/orchestrator/state-store.d.ts +5 -5
- package/dist/orchestrator/state-store.js +2 -2
- package/dist/orchestrator/state-store.js.map +1 -1
- package/dist/orchestrator/types.d.ts +8 -8
- package/dist/owner/index.d.ts +11 -0
- package/dist/owner/index.js +11 -0
- package/dist/owner/index.js.map +1 -0
- package/dist/owner/owner-node-client.d.ts +110 -0
- package/dist/{authority/authority-node-client.js → owner/owner-node-client.js} +58 -17
- package/dist/owner/owner-node-client.js.map +1 -0
- package/dist/server/error-handler.d.ts +22 -1
- package/dist/server/error-handler.js +64 -1
- package/dist/server/error-handler.js.map +1 -1
- package/dist/server/events/types.d.ts +3 -0
- package/dist/server/index.d.ts +33 -2
- package/dist/server/index.js +43 -16
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/grants-admin.d.ts +17 -0
- package/dist/server/routes/grants-admin.js +32 -0
- package/dist/server/routes/grants-admin.js.map +1 -0
- package/dist/server/routes/grants.d.ts +28 -0
- package/dist/server/routes/grants.js +182 -0
- package/dist/server/routes/grants.js.map +1 -0
- package/dist/server/routes/nodes.d.ts +5 -4
- package/dist/server/routes/nodes.js +19 -18
- package/dist/server/routes/nodes.js.map +1 -1
- package/dist/server/routes/settings.d.ts +6 -1
- package/dist/server/routes/settings.js +5 -2
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/status.d.ts +8 -4
- package/dist/server/routes/status.js +9 -4
- package/dist/server/routes/status.js.map +1 -1
- package/dist/server/routes/strands.d.ts +20 -0
- package/dist/server/routes/strands.js +38 -0
- package/dist/server/routes/strands.js.map +1 -0
- package/dist/strands/index.d.ts +7 -0
- package/dist/strands/index.js +6 -0
- package/dist/strands/index.js.map +1 -0
- package/dist/strands/strand-service.d.ts +50 -0
- package/dist/strands/strand-service.js +103 -0
- package/dist/strands/strand-service.js.map +1 -0
- package/dist/strands/types.d.ts +93 -0
- package/dist/strands/types.js +23 -0
- package/dist/strands/types.js.map +1 -0
- package/dist/ui/assets/index-BVoqLjyX.css +1 -0
- package/dist/ui/assets/index-C-iks41M.js +17 -0
- package/dist/ui/assets/index-C-iks41M.js.map +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/update/release-key.d.ts +1 -1
- package/dist/update/release-key.js +8 -6
- package/dist/update/release-key.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/build-targets.test.ts +26 -0
- package/src/__tests__/global-setup.ts +51 -0
- package/src/__tests__/{host-authority.smoke.test.ts → host-owner.smoke.test.ts} +7 -7
- package/src/__tests__/orchestrator-env-scrub.test.ts +140 -0
- package/src/__tests__/orchestrator-node-identity.test.ts +204 -0
- package/src/__tests__/{orchestrator-authority.test.ts → orchestrator-owner.test.ts} +33 -33
- package/src/__tests__/orchestrator-pin-keys.test.ts +153 -0
- package/src/__tests__/orchestrator-push.test.ts +10 -10
- package/src/__tests__/orchestrator.test.ts +387 -6
- package/src/auth/__tests__/self-label.test.ts +85 -0
- package/src/auth/__tests__/trust-circle-integration.test.ts +152 -147
- package/src/auth/__tests__/trust-circle.test.ts +55 -11
- package/src/auth/index.ts +2 -0
- package/src/auth/self-label.ts +74 -0
- package/src/auth/trust-circle.ts +54 -22
- package/src/auth/types.ts +2 -2
- package/src/bin/host.ts +1299 -1038
- package/src/donation/__tests__/donation-service.test.ts +1068 -0
- package/src/donation/__tests__/donation-supervisor.test.ts +510 -0
- package/src/donation/__tests__/fake-orchestrator.test.ts +168 -0
- package/src/donation/__tests__/fake-orchestrator.ts +225 -0
- package/src/donation/__tests__/grant-service.test.ts +213 -0
- package/src/donation/__tests__/grant-store.test.ts +113 -0
- package/src/donation/donation-service.ts +962 -0
- package/src/donation/donation-store.ts +171 -0
- package/src/donation/donation-supervisor.ts +388 -0
- package/src/donation/grant-service.ts +200 -0
- package/src/donation/grant-store.ts +152 -0
- package/src/donation/index.ts +64 -0
- package/src/donation/types.ts +243 -0
- package/src/index.ts +51 -5
- package/src/installer/__tests__/config.test.ts +27 -1
- package/src/installer/__tests__/installer.smoke.test.ts +31 -1
- package/src/installer/__tests__/wizard.test.ts +5 -1
- package/src/installer/config.ts +34 -0
- package/src/installer/index.ts +5 -0
- package/src/installer/wizard.ts +12 -1
- package/src/nat/__tests__/nat-service.test.ts +5 -5
- package/src/nat/nat-service.ts +10 -10
- package/src/nat/types.ts +1 -1
- package/src/orchestrator/__tests__/node-identity.test.ts +62 -0
- package/src/orchestrator/host-process-orchestrator.ts +401 -134
- package/src/orchestrator/index.ts +3 -3
- package/src/orchestrator/node-identity.ts +40 -0
- package/src/orchestrator/port-allocator.ts +48 -0
- package/src/orchestrator/state-store.ts +7 -7
- package/src/orchestrator/types.ts +8 -8
- package/src/{authority/__tests__/authority-node-client.test.ts → owner/__tests__/owner-node-client.test.ts} +45 -17
- package/src/owner/index.ts +12 -0
- package/src/{authority/authority-node-client.ts → owner/owner-node-client.ts} +83 -23
- package/src/server/__tests__/error-handler.test.ts +22 -0
- package/src/server/__tests__/grants-admin-route.test.ts +73 -0
- package/src/server/__tests__/grants-route.test.ts +263 -0
- package/src/server/__tests__/nodes-route.test.ts +25 -25
- package/src/server/__tests__/server.smoke.test.ts +75 -0
- package/src/server/__tests__/settings-route.test.ts +17 -0
- package/src/server/__tests__/status-route.test.ts +21 -0
- package/src/server/__tests__/strands-route.test.ts +233 -0
- package/src/server/error-handler.ts +67 -1
- package/src/server/events/types.ts +1 -0
- package/src/server/index.ts +75 -17
- package/src/server/routes/grants-admin.ts +41 -0
- package/src/server/routes/grants.ts +210 -0
- package/src/server/routes/nodes.ts +19 -18
- package/src/server/routes/settings.ts +11 -3
- package/src/server/routes/status.ts +18 -8
- package/src/server/routes/strands.ts +54 -0
- package/src/strands/__tests__/strand-service.test.ts +165 -0
- package/src/strands/index.ts +14 -0
- package/src/strands/strand-service.ts +135 -0
- package/src/strands/types.ts +102 -0
- package/src/update/__tests__/release-key.test.ts +16 -3
- package/src/update/release-key.ts +8 -6
- package/dist/authority/authority-node-client.d.ts +0 -77
- package/dist/authority/authority-node-client.js.map +0 -1
- package/dist/authority/index.d.ts +0 -11
- package/dist/authority/index.js +0 -11
- package/dist/authority/index.js.map +0 -1
- package/dist/ui/assets/index-BlMXQPSw.js +0 -14
- package/dist/ui/assets/index-BlMXQPSw.js.map +0 -1
- package/dist/ui/assets/index-BwU5mJGF.css +0 -1
- package/src/authority/index.ts +0 -12
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grantee-facing donation routes — the HTTP surface an external cadre authority
|
|
3
|
+
* (a friend's phone) drives to have this host donate a node into *its* cadre.
|
|
4
|
+
*
|
|
5
|
+
* Mount path: `/grants`. Distinct from the loopback-only `/grants-admin` surface
|
|
6
|
+
* (issue/list/revoke, no bearer): this one is **bearer-gated** — every request
|
|
7
|
+
* carries `Authorization: Bearer <grant-token>`, and a grantee sees only the
|
|
8
|
+
* donations its own grant authorized (the single cross-grantee boundary,
|
|
9
|
+
* analogous to cadre-provider's per-customer ownership check).
|
|
10
|
+
*
|
|
11
|
+
* Reachability is out of scope here: in v1 this mounts on the loopback
|
|
12
|
+
* management server, same as the trust-circle / NAT surfaces. Physically exposing
|
|
13
|
+
* it to a remote phone is `backlog/feat-cadre-host-wan-grant-reachability`.
|
|
14
|
+
*
|
|
15
|
+
* Errors: handlers either send a `{ ok:false, error:{code,message} }` envelope
|
|
16
|
+
* inline (auth / body validation) or let the `DonationService` throw a
|
|
17
|
+
* `DonationError`, which `server/error-handler.ts` maps to the matching HTTP
|
|
18
|
+
* status and the same envelope.
|
|
19
|
+
*/
|
|
20
|
+
/** Uniform error envelope (matches `error-handler.ts` and the provider API). */
|
|
21
|
+
function errorResponse(reply, code, message, status) {
|
|
22
|
+
return reply.status(status).send({ ok: false, error: { code, message } });
|
|
23
|
+
}
|
|
24
|
+
/** Pull the `Bearer <token>` value out of the Authorization header, or undefined. */
|
|
25
|
+
function parseBearer(header) {
|
|
26
|
+
if (!header)
|
|
27
|
+
return undefined;
|
|
28
|
+
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
29
|
+
return match ? match[1].trim() : undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Map a grant denial reason to an HTTP status for the bearer gate. */
|
|
32
|
+
function denyStatus(reason) {
|
|
33
|
+
if (reason === 'expired' || reason === 'revoked')
|
|
34
|
+
return 403;
|
|
35
|
+
return 401; // unknown_token / missing
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Authenticate the grant bearer for a non-provisioning op (identity + not
|
|
39
|
+
* expired/revoked; no quota). Returns the token, or null after sending the
|
|
40
|
+
* failure response. Provision does its own quota-aware validation inside
|
|
41
|
+
* `DonationService.provision`.
|
|
42
|
+
*/
|
|
43
|
+
function authenticate(request, reply, grants) {
|
|
44
|
+
const token = parseBearer(request.headers.authorization);
|
|
45
|
+
if (!token) {
|
|
46
|
+
errorResponse(reply, 'unauthorized', 'Missing bearer grant token', 401);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const validation = grants.validate(token);
|
|
50
|
+
if (!validation.ok) {
|
|
51
|
+
const status = denyStatus(validation.reason);
|
|
52
|
+
// Keep the envelope code aligned with the status (and with the POST path,
|
|
53
|
+
// which surfaces an unknown token as `unauthorized`): 401 → unauthorized,
|
|
54
|
+
// 403 → forbidden. A 401 labelled `forbidden` is self-contradictory.
|
|
55
|
+
const code = status === 403 ? 'forbidden' : 'unauthorized';
|
|
56
|
+
errorResponse(reply, code, `Grant is ${validation.reason ?? 'invalid'}`, status);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return token;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a donation the presented grant owns. Returns the redacted view, or
|
|
63
|
+
* null after sending a 404 — an unknown id and one owned by a *different* grant
|
|
64
|
+
* are deliberately indistinguishable (a grantee never learns another grantee's
|
|
65
|
+
* donations exist).
|
|
66
|
+
*/
|
|
67
|
+
function requireOwned(donations, id, token, reply) {
|
|
68
|
+
const donation = donations.get(id);
|
|
69
|
+
if (!donation || donation.grantToken !== token) {
|
|
70
|
+
errorResponse(reply, 'not_found', `No such donation: ${id}`, 404);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return donation;
|
|
74
|
+
}
|
|
75
|
+
export function registerGrantsRoutes(app, opts) {
|
|
76
|
+
const { donations, grants } = opts;
|
|
77
|
+
// POST /grants — provision a donated node. The bearer's identity/expiry/
|
|
78
|
+
// revocation AND quota are validated inside provision(), which throws a
|
|
79
|
+
// DonationError (unauthorized / forbidden / quota_exceeded) the error handler
|
|
80
|
+
// maps. We only parse the bearer and validate the request body here.
|
|
81
|
+
app.post('/grants', async (request, reply) => {
|
|
82
|
+
const token = parseBearer(request.headers.authorization);
|
|
83
|
+
if (!token) {
|
|
84
|
+
return errorResponse(reply, 'unauthorized', 'Missing bearer grant token', 401);
|
|
85
|
+
}
|
|
86
|
+
const body = (request.body ?? {});
|
|
87
|
+
if (typeof body.partyId !== 'string' || body.partyId.length === 0) {
|
|
88
|
+
return errorResponse(reply, 'invalid_request', 'partyId is required', 400);
|
|
89
|
+
}
|
|
90
|
+
if (!Array.isArray(body.bootstrapNodes) || body.bootstrapNodes.length === 0) {
|
|
91
|
+
return errorResponse(reply, 'invalid_request', 'bootstrapNodes is required', 400);
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(body.ownerKeys) || body.ownerKeys.length === 0) {
|
|
94
|
+
return errorResponse(reply, 'invalid_request', 'ownerKeys is required', 400);
|
|
95
|
+
}
|
|
96
|
+
// Element types too, not just the array: `provision`'s shape check trims each
|
|
97
|
+
// entry, so a non-string reaches the caller as an internal `.trim is not a
|
|
98
|
+
// function` rather than as a message naming what is wrong with the request.
|
|
99
|
+
if (!body.ownerKeys.every(key => typeof key === 'string')) {
|
|
100
|
+
return errorResponse(reply, 'invalid_request', 'ownerKeys must be an array of strings', 400);
|
|
101
|
+
}
|
|
102
|
+
if (body.profile !== undefined && body.profile !== 'storage' && body.profile !== 'transaction') {
|
|
103
|
+
return errorResponse(reply, 'invalid_request', 'profile must be "storage" or "transaction"', 400);
|
|
104
|
+
}
|
|
105
|
+
const donation = await donations.provision({
|
|
106
|
+
grantToken: token,
|
|
107
|
+
partyId: body.partyId,
|
|
108
|
+
bootstrapNodes: body.bootstrapNodes,
|
|
109
|
+
ownerKeys: body.ownerKeys,
|
|
110
|
+
...(body.profile ? { profile: body.profile } : {}),
|
|
111
|
+
});
|
|
112
|
+
return reply.status(201).send({ ok: true, data: { donation } });
|
|
113
|
+
});
|
|
114
|
+
// GET /grants — list only the donations this grant authorized.
|
|
115
|
+
app.get('/grants', async (request, reply) => {
|
|
116
|
+
const token = authenticate(request, reply, grants);
|
|
117
|
+
if (!token)
|
|
118
|
+
return reply;
|
|
119
|
+
return reply.send({ ok: true, data: { donations: donations.list(token) } });
|
|
120
|
+
});
|
|
121
|
+
// GET /grants/:id — one donation (redacted), owned by this grant.
|
|
122
|
+
app.get('/grants/:id', async (request, reply) => {
|
|
123
|
+
const token = authenticate(request, reply, grants);
|
|
124
|
+
if (!token)
|
|
125
|
+
return reply;
|
|
126
|
+
const donation = requireOwned(donations, request.params.id, token, reply);
|
|
127
|
+
if (!donation)
|
|
128
|
+
return reply;
|
|
129
|
+
return reply.send({ ok: true, data: { donation } });
|
|
130
|
+
});
|
|
131
|
+
// GET /grants/:id/peer — live { peerId, multiaddrs } for the requester's addDrone.
|
|
132
|
+
app.get('/grants/:id/peer', async (request, reply) => {
|
|
133
|
+
const token = authenticate(request, reply, grants);
|
|
134
|
+
if (!token)
|
|
135
|
+
return reply;
|
|
136
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
137
|
+
return reply;
|
|
138
|
+
const peer = await donations.getPeer(request.params.id);
|
|
139
|
+
return reply.send({ ok: true, data: peer });
|
|
140
|
+
});
|
|
141
|
+
// PUT /grants/:id/seed — apply the requester's phone-signed seed.
|
|
142
|
+
app.put('/grants/:id/seed', async (request, reply) => {
|
|
143
|
+
const token = authenticate(request, reply, grants);
|
|
144
|
+
if (!token)
|
|
145
|
+
return reply;
|
|
146
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
147
|
+
return reply;
|
|
148
|
+
const body = (request.body ?? {});
|
|
149
|
+
if (typeof body.seed !== 'string' || body.seed.length === 0) {
|
|
150
|
+
return errorResponse(reply, 'invalid_request', 'seed is required', 400);
|
|
151
|
+
}
|
|
152
|
+
const result = await donations.applySeed(request.params.id, body.seed);
|
|
153
|
+
// NOTE: exhaustive over today's three outcomes, and nothing forces it to stay
|
|
154
|
+
// that way — an unhandled fourth would fall out of the handler as `undefined`
|
|
155
|
+
// and hang the request rather than fail to compile. If `DonationSeedResult`
|
|
156
|
+
// ever grows a variant, add a `default:` that 500s here.
|
|
157
|
+
switch (result.outcome) {
|
|
158
|
+
case 'rejected':
|
|
159
|
+
return errorResponse(reply, 'seed_failed', result.error ?? 'Node rejected the seed', 502);
|
|
160
|
+
// The loan ended while the seed was in flight. The ending won, so the
|
|
161
|
+
// caller is told the record it seeded is no longer seedable (409) — or is
|
|
162
|
+
// gone entirely (404) — rather than a 200 that implies a live node.
|
|
163
|
+
case 'abandoned':
|
|
164
|
+
return result.status
|
|
165
|
+
? errorResponse(reply, 'invalid_state', `Donation ${request.params.id} ended (${result.status}) while the seed was in flight`, 409)
|
|
166
|
+
: errorResponse(reply, 'not_found', `No such donation: ${request.params.id}`, 404);
|
|
167
|
+
case 'seeded':
|
|
168
|
+
return reply.send({ ok: true, data: { peersAdded: result.peersAdded } });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
// DELETE /grants/:id — terminate the donated node.
|
|
172
|
+
app.delete('/grants/:id', async (request, reply) => {
|
|
173
|
+
const token = authenticate(request, reply, grants);
|
|
174
|
+
if (!token)
|
|
175
|
+
return reply;
|
|
176
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
177
|
+
return reply;
|
|
178
|
+
await donations.terminate(request.params.id);
|
|
179
|
+
return reply.send({ ok: true });
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=grants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../../../src/server/routes/grants.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* `onStateChange` (forwarded by `createLocalUiServer.start`) — the route
|
|
7
7
|
* handlers don't re-publish.
|
|
8
8
|
*
|
|
9
|
-
* start/restart are real for the admin's
|
|
10
|
-
* persisted `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* start/restart are real for the admin's owner node (re-spawned from the
|
|
10
|
+
* persisted `OwnerSpawnConfig`). Generic node lifecycle is owned by the
|
|
11
|
+
* grantee-facing donation surface (`/grants`) — this route no longer spawns
|
|
12
|
+
* generic nodes; start/restart of a known non-owner node points the caller
|
|
13
|
+
* there. Stop works for any running node.
|
|
13
14
|
*/
|
|
14
15
|
import type { FastifyInstance } from 'fastify';
|
|
15
16
|
import type { HostProcessOrchestrator } from '../../orchestrator/index.js';
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* `onStateChange` (forwarded by `createLocalUiServer.start`) — the route
|
|
7
7
|
* handlers don't re-publish.
|
|
8
8
|
*
|
|
9
|
-
* start/restart are real for the admin's
|
|
10
|
-
* persisted `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* start/restart are real for the admin's owner node (re-spawned from the
|
|
10
|
+
* persisted `OwnerSpawnConfig`). Generic node lifecycle is owned by the
|
|
11
|
+
* grantee-facing donation surface (`/grants`) — this route no longer spawns
|
|
12
|
+
* generic nodes; start/restart of a known non-owner node points the caller
|
|
13
|
+
* there. Stop works for any running node.
|
|
13
14
|
*/
|
|
14
15
|
import { existsSync, openSync, readSync, closeSync, statSync } from 'node:fs';
|
|
15
16
|
import { defaultLogPath } from '../../orchestrator/log-rotator.js';
|
|
@@ -65,34 +66,34 @@ export function registerNodesRoutes(app, opts) {
|
|
|
65
66
|
// forwards that to the bus. Don't double-publish here.
|
|
66
67
|
return { ok: true };
|
|
67
68
|
});
|
|
68
|
-
// start / restart: real for the admin's
|
|
69
|
-
// persisted
|
|
70
|
-
//
|
|
71
|
-
// ids 404.
|
|
69
|
+
// start / restart: real for the admin's owner node (spawned from the
|
|
70
|
+
// persisted OwnerSpawnConfig). Generic node lifecycle now belongs to the
|
|
71
|
+
// donation surface (/grants) — those ids return a clear not_implemented that
|
|
72
|
+
// points there; unknown ids 404.
|
|
72
73
|
app.post('/api/nodes/:id/start', async (request, reply) => {
|
|
73
74
|
const { id } = request.params;
|
|
74
|
-
if (orchestrator.
|
|
75
|
-
if (!orchestrator.
|
|
76
|
-
return notImplemented(reply, `start ${id}:
|
|
75
|
+
if (orchestrator.isOwnerNode(id)) {
|
|
76
|
+
if (!orchestrator.hasOwnerConfig()) {
|
|
77
|
+
return notImplemented(reply, `start ${id}: owner node has no saved spawn config.`);
|
|
77
78
|
}
|
|
78
|
-
const node = await orchestrator.
|
|
79
|
+
const node = await orchestrator.ensureOwnerNode();
|
|
79
80
|
return { ok: true, data: { node } };
|
|
80
81
|
}
|
|
81
82
|
return startRestartFallback(reply, orchestrator, id, 'start');
|
|
82
83
|
});
|
|
83
84
|
app.post('/api/nodes/:id/restart', async (request, reply) => {
|
|
84
85
|
const { id } = request.params;
|
|
85
|
-
if (orchestrator.
|
|
86
|
-
if (!orchestrator.
|
|
87
|
-
return notImplemented(reply, `restart ${id}:
|
|
86
|
+
if (orchestrator.isOwnerNode(id)) {
|
|
87
|
+
if (!orchestrator.hasOwnerConfig()) {
|
|
88
|
+
return notImplemented(reply, `restart ${id}: owner node has no saved spawn config.`);
|
|
88
89
|
}
|
|
89
|
-
const node = await orchestrator.
|
|
90
|
+
const node = await orchestrator.restartOwnerNode();
|
|
90
91
|
return { ok: true, data: { node } };
|
|
91
92
|
}
|
|
92
93
|
return startRestartFallback(reply, orchestrator, id, 'restart');
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
|
-
/** 404 for unknown ids; 501 not_implemented for known non-
|
|
96
|
+
/** 404 for unknown ids; 501 not_implemented for known non-owner nodes. */
|
|
96
97
|
function startRestartFallback(reply, orchestrator, id, verb) {
|
|
97
98
|
if (!orchestrator.getNode(id)) {
|
|
98
99
|
return reply.code(404).send({
|
|
@@ -100,7 +101,7 @@ function startRestartFallback(reply, orchestrator, id, verb) {
|
|
|
100
101
|
error: { code: 'not_found', message: `Unknown node: ${id}` },
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
|
-
return notImplemented(reply, `${verb} ${id}: only the
|
|
104
|
+
return notImplemented(reply, `${verb} ${id}: only the owner node is start/restart-able here; generic donated-node lifecycle is owned by the donation surface — use POST /grants to provision and DELETE /grants/:id to terminate.`);
|
|
104
105
|
}
|
|
105
106
|
function notImplemented(reply, message) {
|
|
106
107
|
return reply.code(501).send({ ok: false, error: { code: 'not_implemented', message } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../../../src/server/routes/nodes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../../../src/server/routes/nodes.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,12 @@ import type { UpdateService } from '../../update/index.js';
|
|
|
20
20
|
import type { HostSettingsStore } from '../settings-store.js';
|
|
21
21
|
export interface SettingsRoutesOptions {
|
|
22
22
|
settingsStore: HostSettingsStore;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Present only when the host runs its own personal cadre. In donor-only mode
|
|
25
|
+
* an `upnpEnabled` write still persists to host.config.json but has no running
|
|
26
|
+
* NatService to propagate into.
|
|
27
|
+
*/
|
|
28
|
+
nat?: NatService;
|
|
24
29
|
update?: UpdateService;
|
|
25
30
|
}
|
|
26
31
|
export declare function registerSettingsRoutes(app: FastifyInstance, opts: SettingsRoutesOptions): void;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* GET returns the full `HostConfigFile` (the SPA wants the full shape so it
|
|
15
15
|
* can render a read-only view of the rejected keys).
|
|
16
16
|
*/
|
|
17
|
-
const FORBIDDEN_KEYS = new Set(['uiPort', 'libp2pPort', 'dataDir', 'identityPath', 'installId', 'installedAt', 'installerVersion', 'version']);
|
|
17
|
+
const FORBIDDEN_KEYS = new Set(['uiPort', 'libp2pPort', 'dataDir', 'identityPath', 'installId', 'installedAt', 'installerVersion', 'version', 'ownCadre']);
|
|
18
18
|
const WRITABLE_TOP_KEYS = new Set(['upnpEnabled', 'updates']);
|
|
19
19
|
const WRITABLE_UPDATES_KEYS = new Set(['autoApply', 'manifestUrl']);
|
|
20
20
|
export function registerSettingsRoutes(app, opts) {
|
|
@@ -39,7 +39,10 @@ export function registerSettingsRoutes(app, opts) {
|
|
|
39
39
|
// takes effect immediately. NatService persists into nat.json — that's
|
|
40
40
|
// independent of host.config.json's `upnpEnabled` (which the installer
|
|
41
41
|
// uses on first run to seed nat.json). Update both for consistency.
|
|
42
|
-
|
|
42
|
+
// In donor-only mode there is no NatService — the write still persists.
|
|
43
|
+
if (nat) {
|
|
44
|
+
await nat.putSettings({ upnpEnabled: body.upnpEnabled });
|
|
45
|
+
}
|
|
43
46
|
}
|
|
44
47
|
if ('updates' in body) {
|
|
45
48
|
const updates = body.updates;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/server/routes/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/server/routes/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,8 +13,10 @@ import type { NatStatusSnapshot } from '../../nat/types.js';
|
|
|
13
13
|
import type { UpdateService } from '../../update/index.js';
|
|
14
14
|
export interface StatusRouteOptions {
|
|
15
15
|
orchestrator: HostProcessOrchestrator;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Present only when the host runs its own personal cadre (`ownCadre.enabled`). */
|
|
17
|
+
trustCircle?: TrustCircleService;
|
|
18
|
+
/** Present only when the host runs its own personal cadre (`ownCadre.enabled`). */
|
|
19
|
+
nat?: NatService;
|
|
18
20
|
update?: UpdateService;
|
|
19
21
|
}
|
|
20
22
|
export interface StatusResponse {
|
|
@@ -29,11 +31,13 @@ export interface StatusResponse {
|
|
|
29
31
|
status: ManagedNodeInfo['status'];
|
|
30
32
|
profile: ManagedNodeInfo['profile'];
|
|
31
33
|
}>;
|
|
32
|
-
|
|
34
|
+
/** Omitted in donor-only mode (no host-own trust circle). */
|
|
35
|
+
trustCircle?: {
|
|
33
36
|
members: number;
|
|
34
37
|
pending: number;
|
|
35
38
|
};
|
|
36
|
-
|
|
39
|
+
/** Omitted in donor-only mode (no NAT service). */
|
|
40
|
+
connectivity?: NatStatusSnapshot;
|
|
37
41
|
update?: {
|
|
38
42
|
available?: string;
|
|
39
43
|
lastChecked?: string;
|
|
@@ -17,8 +17,6 @@ export function registerStatusRoute(app, opts) {
|
|
|
17
17
|
status: n.status,
|
|
18
18
|
profile: n.profile,
|
|
19
19
|
}));
|
|
20
|
-
const tc = await opts.trustCircle.list();
|
|
21
|
-
const connectivity = opts.nat.getStatus();
|
|
22
20
|
const response = {
|
|
23
21
|
service: {
|
|
24
22
|
name: 'cadre-host',
|
|
@@ -26,9 +24,16 @@ export function registerStatusRoute(app, opts) {
|
|
|
26
24
|
uptimeSeconds: secondsSince(PROCESS_STARTED_AT),
|
|
27
25
|
},
|
|
28
26
|
nodes,
|
|
29
|
-
trustCircle: { members: tc.members.length, pending: tc.pending.length },
|
|
30
|
-
connectivity,
|
|
31
27
|
};
|
|
28
|
+
// Trust-circle + connectivity exist only when the host runs its own
|
|
29
|
+
// personal cadre; donor-only mode omits both.
|
|
30
|
+
if (opts.trustCircle) {
|
|
31
|
+
const tc = await opts.trustCircle.list();
|
|
32
|
+
response.trustCircle = { members: tc.members.length, pending: tc.pending.length };
|
|
33
|
+
}
|
|
34
|
+
if (opts.nat) {
|
|
35
|
+
response.connectivity = opts.nat.getStatus();
|
|
36
|
+
}
|
|
32
37
|
if (opts.update) {
|
|
33
38
|
const state = await opts.update.getState();
|
|
34
39
|
const update = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/server/routes/status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAWzC,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/server/routes/status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAWzC,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;AA0BnD,MAAM,UAAU,mBAAmB,CAAC,GAAoB,EAAE,IAAwB;IAChF,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IAErC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,IAA6B,EAAE;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,OAAO,EAAE,CAAC,CAAC,OAAO;SACnB,CAAC,CAAC,CAAC;QAEJ,MAAM,QAAQ,GAAmB;YAC/B,OAAO,EAAE;gBACP,IAAI,EAAE,YAAY;gBAClB,OAAO;gBACP,aAAa,EAAE,YAAY,CAAC,kBAAkB,CAAC;aAChD;YACD,KAAK;SACN,CAAC;QAEF,oEAAoE;QACpE,8CAA8C;QAC9C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACzC,QAAQ,CAAC,WAAW,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpF,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QAC/C,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAiD,EAAE,CAAC;YAChE,IAAI,KAAK,CAAC,SAAS,EAAE,OAAO;gBAAE,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC;YACzE,IAAI,KAAK,CAAC,WAAW;gBAAE,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAC9D,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC;QAC3B,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC;IAC9B,OAAO,MAAM,CAAC,SAAS,GAAG,WAAc,CAAC,CAAC;AAC5C,CAAC;AAED,IAAI,aAAa,GAAkB,IAAI,CAAC;AACxC,SAAS,kBAAkB;IACzB,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,IAAI,CAAC;QACH,wDAAwD;QACxD,iEAAiE;QACjE,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG;YACjB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;YAC/C,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;SAC1C,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;gBAC3E,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;oBAC/B,OAAO,aAAa,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACxB,aAAa,GAAG,eAAe,CAAC;IAChC,OAAO,aAAa,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strand HTTP routes — thin adapters around `StrandHandlers`
|
|
3
|
+
* (`createStrandHandlers(service)`).
|
|
4
|
+
*
|
|
5
|
+
* Mount path: `/api/strands` — a UI-only surface with no CLI contract to
|
|
6
|
+
* preserve. (The `/auth/*` and `/nat/*` mounts sit outside `/api` only because
|
|
7
|
+
* the existing CLI already targets those URLs.) Envelope form therefore follows
|
|
8
|
+
* the other `/api/*` routes: `{ ok: true, data }`.
|
|
9
|
+
*
|
|
10
|
+
* A removal that actually issued a delete publishes `strands-changed` so other
|
|
11
|
+
* open tabs refresh. A call that found no row publishes nothing — nothing changed.
|
|
12
|
+
*/
|
|
13
|
+
import type { FastifyInstance } from 'fastify';
|
|
14
|
+
import type { StrandHandlers } from '../../strands/types.js';
|
|
15
|
+
import type { EventBus } from '../events/bus.js';
|
|
16
|
+
export interface StrandRoutesOptions {
|
|
17
|
+
handlers: StrandHandlers;
|
|
18
|
+
events: EventBus;
|
|
19
|
+
}
|
|
20
|
+
export declare function registerStrandRoutes(app: FastifyInstance, opts: StrandRoutesOptions): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strand HTTP routes — thin adapters around `StrandHandlers`
|
|
3
|
+
* (`createStrandHandlers(service)`).
|
|
4
|
+
*
|
|
5
|
+
* Mount path: `/api/strands` — a UI-only surface with no CLI contract to
|
|
6
|
+
* preserve. (The `/auth/*` and `/nat/*` mounts sit outside `/api` only because
|
|
7
|
+
* the existing CLI already targets those URLs.) Envelope form therefore follows
|
|
8
|
+
* the other `/api/*` routes: `{ ok: true, data }`.
|
|
9
|
+
*
|
|
10
|
+
* A removal that actually issued a delete publishes `strands-changed` so other
|
|
11
|
+
* open tabs refresh. A call that found no row publishes nothing — nothing changed.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The only accepted `confirm` values — the same strict pair the admin channel
|
|
15
|
+
* accepts. Not a general truthiness parser: `yes`, `on` and an empty value all
|
|
16
|
+
* count as NOT confirmed, because a guessy parser here would turn a typo into a
|
|
17
|
+
* destroyed membership key.
|
|
18
|
+
*
|
|
19
|
+
* NOTE: deliberately duplicated from cadre-cli's `admin-server.ts` rather than
|
|
20
|
+
* imported — cadre-host depends on cadre-cli for its bin path only, never its
|
|
21
|
+
* source. Keep the two in step by hand; do not DRY them into an import.
|
|
22
|
+
*/
|
|
23
|
+
const CONFIRM_VALUES = new Set(['1', 'true']);
|
|
24
|
+
export function registerStrandRoutes(app, opts) {
|
|
25
|
+
const { handlers, events } = opts;
|
|
26
|
+
app.get('/api/strands', async () => {
|
|
27
|
+
return { ok: true, data: await handlers.listStrands() };
|
|
28
|
+
});
|
|
29
|
+
app.delete('/api/strands/:id', async (request) => {
|
|
30
|
+
const confirm = typeof request.query.confirm === 'string' && CONFIRM_VALUES.has(request.query.confirm);
|
|
31
|
+
const result = await handlers.removeStrand(request.params.id, { confirm });
|
|
32
|
+
if (result.removed) {
|
|
33
|
+
events.publish({ type: 'strands-changed', kind: 'removed' });
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, data: result };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=strands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strands.js","sourceRoot":"","sources":["../../../src/server/routes/strands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH;;;;;;;;;GASG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,GAAoB,EAAE,IAAyB;IAClF,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAElC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,IAAI,EAAE;QACjC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CACR,kBAAkB,EAClB,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvG,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strand management for cadre-host. See ./strand-service.ts and ./types.ts.
|
|
3
|
+
*/
|
|
4
|
+
export { StrandService, createStrandHandlers } from './strand-service.js';
|
|
5
|
+
export type { StrandServiceOptions, CadreNodeLike } from './strand-service.js';
|
|
6
|
+
export { StrandError } from './types.js';
|
|
7
|
+
export type { StrandSummary, StrandListSnapshot, StrandRemovalResult, StrandHandlers, StrandErrorCode, } from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/strands/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { StrandHandlers, StrandListSnapshot, StrandRemovalResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal slice of the owner node that StrandService uses. Declared here (rather
|
|
4
|
+
* than reaching into `OwnerNodeClient`) so unit tests can inject a fake without
|
|
5
|
+
* standing up an admin channel — the third such trimmed interface the client
|
|
6
|
+
* satisfies, alongside the trust-circle and NAT ones.
|
|
7
|
+
*/
|
|
8
|
+
export interface CadreNodeLike {
|
|
9
|
+
listStrands(): Promise<StrandListSnapshot>;
|
|
10
|
+
removeStrand(strandId: string, opts: {
|
|
11
|
+
confirm: boolean;
|
|
12
|
+
}): Promise<StrandRemovalResult>;
|
|
13
|
+
}
|
|
14
|
+
/** Constructor options. */
|
|
15
|
+
export interface StrandServiceOptions {
|
|
16
|
+
/** The owner node — provides listStrands / removeStrand over the admin channel. */
|
|
17
|
+
cadreNode: CadreNodeLike;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* StrandService — validation and error translation over the owner node's strand
|
|
21
|
+
* admin routes.
|
|
22
|
+
*
|
|
23
|
+
* It owns no state and makes no policy. In particular it does NOT decide whether a
|
|
24
|
+
* closed strand needs confirmation: it never reads the row, so it could only guess.
|
|
25
|
+
* The node enforces that rule and answers `confirmation_required`; this layer
|
|
26
|
+
* forwards the caller's flag verbatim and surfaces the refusal as a distinct code
|
|
27
|
+
* so the UI can raise the right dialog.
|
|
28
|
+
*/
|
|
29
|
+
export declare class StrandService {
|
|
30
|
+
private readonly cadreNode;
|
|
31
|
+
constructor(opts: StrandServiceOptions);
|
|
32
|
+
/** This party's strands, plus the node's current control-connection count. */
|
|
33
|
+
list(): Promise<StrandListSnapshot>;
|
|
34
|
+
/**
|
|
35
|
+
* Remove this party's participation in one strand.
|
|
36
|
+
*
|
|
37
|
+
* `confirm` is forwarded exactly as given — never defaulted to true, never
|
|
38
|
+
* re-sent as true after a refusal. A closed strand's row carries this party's
|
|
39
|
+
* membership key for that network and is stored nowhere else, so an automatic
|
|
40
|
+
* retry would destroy it on the caller's behalf.
|
|
41
|
+
*/
|
|
42
|
+
remove(rawId: string, opts: {
|
|
43
|
+
confirm: boolean;
|
|
44
|
+
}): Promise<StrandRemovalResult>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrap a StrandService into the typed handler shape the local UI server mounts.
|
|
48
|
+
* Errors propagate as StrandError; `server/error-handler.ts` maps `.code` → status.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createStrandHandlers(service: StrandService): StrandHandlers;
|