@serfab/cadre-host 0.8.1 → 0.9.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/trust-circle.d.ts +5 -5
- package/dist/auth/trust-circle.js +10 -10
- package/dist/auth/trust-circle.js.map +1 -1
- package/dist/auth/types.d.ts +2 -2
- package/dist/bin/host.js +255 -54
- package/dist/bin/host.js.map +1 -1
- package/dist/donation/donation-service.d.ts +136 -0
- package/dist/donation/donation-service.js +306 -0
- package/dist/donation/donation-service.js.map +1 -0
- package/dist/donation/donation-store.d.ts +42 -0
- package/dist/donation/donation-store.js +141 -0
- package/dist/donation/donation-store.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 +18 -0
- package/dist/donation/index.js +16 -0
- package/dist/donation/index.js.map +1 -0
- package/dist/donation/types.d.ts +175 -0
- package/dist/donation/types.js +40 -0
- package/dist/donation/types.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -2
- 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 +35 -27
- package/dist/orchestrator/host-process-orchestrator.js +79 -53
- 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/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/{authority/authority-node-client.d.ts → owner/owner-node-client.d.ts} +32 -14
- package/dist/{authority/authority-node-client.js → owner/owner-node-client.js} +39 -15
- package/dist/owner/owner-node-client.js.map +1 -0
- package/dist/server/error-handler.d.ts +15 -0
- package/dist/server/error-handler.js +41 -0
- package/dist/server/error-handler.js.map +1 -1
- package/dist/server/index.d.ts +26 -2
- package/dist/server/index.js +37 -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 +163 -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/update/release-key.js +7 -5
- package/dist/update/release-key.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/{host-authority.smoke.test.ts → host-owner.smoke.test.ts} +7 -7
- 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/auth/__tests__/trust-circle-integration.test.ts +11 -11
- package/src/auth/__tests__/trust-circle.test.ts +10 -10
- package/src/auth/trust-circle.ts +10 -10
- package/src/auth/types.ts +2 -2
- package/src/bin/host.ts +273 -53
- package/src/donation/__tests__/donation-service.test.ts +290 -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 +392 -0
- package/src/donation/donation-store.ts +164 -0
- package/src/donation/grant-service.ts +200 -0
- package/src/donation/grant-store.ts +152 -0
- package/src/donation/index.ts +49 -0
- package/src/donation/types.ts +220 -0
- package/src/index.ts +37 -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/host-process-orchestrator.ts +88 -60
- package/src/orchestrator/index.ts +3 -3
- 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} +55 -21
- 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 +189 -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/error-handler.ts +43 -0
- package/src/server/index.ts +62 -17
- package/src/server/routes/grants-admin.ts +41 -0
- package/src/server/routes/grants.ts +186 -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/update/__tests__/release-key.test.ts +16 -3
- package/src/update/release-key.ts +7 -5
- 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/src/authority/index.ts +0 -12
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Donation grant **admin** HTTP routes — thin adapters around
|
|
3
|
+
* `GrantAdminHandlers` (`createGrantAdminHandlers(service)`).
|
|
4
|
+
*
|
|
5
|
+
* Mount path: `/grants-admin` — the admin surface the `cadre-host grant` CLI
|
|
6
|
+
* targets. It is **loopback, no bearer**: same-machine admin, matching
|
|
7
|
+
* cadre-host's local-UI "no login" posture (see docs/cadre-host.md §
|
|
8
|
+
* Security posture). This is distinct from the grantee-facing `/grants`
|
|
9
|
+
* provisioning surface that carries the bearer gate — that arrives in the
|
|
10
|
+
* `2-donation-service` ticket.
|
|
11
|
+
*/
|
|
12
|
+
export function registerGrantsAdminRoutes(app, opts) {
|
|
13
|
+
const { handlers } = opts;
|
|
14
|
+
app.get('/grants-admin', async () => {
|
|
15
|
+
return handlers.listGrants();
|
|
16
|
+
});
|
|
17
|
+
app.post('/grants-admin', async (request) => {
|
|
18
|
+
const body = (request.body ?? {});
|
|
19
|
+
const label = typeof body.label === 'string' ? body.label : '';
|
|
20
|
+
const args = { label };
|
|
21
|
+
if (typeof body.maxNodes === 'number')
|
|
22
|
+
args.maxNodes = body.maxNodes;
|
|
23
|
+
if (typeof body.ttlMs === 'number')
|
|
24
|
+
args.ttlMs = body.ttlMs;
|
|
25
|
+
return handlers.postGrant(args);
|
|
26
|
+
});
|
|
27
|
+
app.delete('/grants-admin/:token', async (request) => {
|
|
28
|
+
await handlers.deleteGrant(request.params.token);
|
|
29
|
+
return { ok: true };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=grants-admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants-admin.js","sourceRoot":"","sources":["../../../src/server/routes/grants-admin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,MAAM,UAAU,yBAAyB,CAAC,GAAoB,EAAE,IAA8B;IAC5F,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAE1B,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAClC,OAAO,QAAQ,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAA6D,CAAC;QAC9F,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAyD,EAAE,KAAK,EAAE,CAAC;QAC7E,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACrE,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5D,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAgC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAClF,MAAM,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
import type { FastifyInstance } from 'fastify';
|
|
21
|
+
import type { DonationService } from '../../donation/donation-service.js';
|
|
22
|
+
import type { GrantValidator } from '../../donation/types.js';
|
|
23
|
+
export interface GrantsRoutesOptions {
|
|
24
|
+
donations: DonationService;
|
|
25
|
+
/** Validates the presented bearer for non-provisioning ops (provision revalidates internally). */
|
|
26
|
+
grants: GrantValidator;
|
|
27
|
+
}
|
|
28
|
+
export declare function registerGrantsRoutes(app: FastifyInstance, opts: GrantsRoutesOptions): void;
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
if (body.profile !== undefined && body.profile !== 'storage' && body.profile !== 'transaction') {
|
|
97
|
+
return errorResponse(reply, 'invalid_request', 'profile must be "storage" or "transaction"', 400);
|
|
98
|
+
}
|
|
99
|
+
const donation = await donations.provision({
|
|
100
|
+
grantToken: token,
|
|
101
|
+
partyId: body.partyId,
|
|
102
|
+
bootstrapNodes: body.bootstrapNodes,
|
|
103
|
+
ownerKeys: body.ownerKeys,
|
|
104
|
+
...(body.profile ? { profile: body.profile } : {}),
|
|
105
|
+
});
|
|
106
|
+
return reply.status(201).send({ ok: true, data: { donation } });
|
|
107
|
+
});
|
|
108
|
+
// GET /grants — list only the donations this grant authorized.
|
|
109
|
+
app.get('/grants', async (request, reply) => {
|
|
110
|
+
const token = authenticate(request, reply, grants);
|
|
111
|
+
if (!token)
|
|
112
|
+
return reply;
|
|
113
|
+
return reply.send({ ok: true, data: { donations: donations.list(token) } });
|
|
114
|
+
});
|
|
115
|
+
// GET /grants/:id — one donation (redacted), owned by this grant.
|
|
116
|
+
app.get('/grants/:id', async (request, reply) => {
|
|
117
|
+
const token = authenticate(request, reply, grants);
|
|
118
|
+
if (!token)
|
|
119
|
+
return reply;
|
|
120
|
+
const donation = requireOwned(donations, request.params.id, token, reply);
|
|
121
|
+
if (!donation)
|
|
122
|
+
return reply;
|
|
123
|
+
return reply.send({ ok: true, data: { donation } });
|
|
124
|
+
});
|
|
125
|
+
// GET /grants/:id/peer — live { peerId, multiaddrs } for the requester's addDrone.
|
|
126
|
+
app.get('/grants/:id/peer', async (request, reply) => {
|
|
127
|
+
const token = authenticate(request, reply, grants);
|
|
128
|
+
if (!token)
|
|
129
|
+
return reply;
|
|
130
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
131
|
+
return reply;
|
|
132
|
+
const peer = await donations.getPeer(request.params.id);
|
|
133
|
+
return reply.send({ ok: true, data: peer });
|
|
134
|
+
});
|
|
135
|
+
// PUT /grants/:id/seed — apply the requester's phone-signed seed.
|
|
136
|
+
app.put('/grants/:id/seed', async (request, reply) => {
|
|
137
|
+
const token = authenticate(request, reply, grants);
|
|
138
|
+
if (!token)
|
|
139
|
+
return reply;
|
|
140
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
141
|
+
return reply;
|
|
142
|
+
const body = (request.body ?? {});
|
|
143
|
+
if (typeof body.seed !== 'string' || body.seed.length === 0) {
|
|
144
|
+
return errorResponse(reply, 'invalid_request', 'seed is required', 400);
|
|
145
|
+
}
|
|
146
|
+
const result = await donations.applySeed(request.params.id, body.seed);
|
|
147
|
+
if (!result.success) {
|
|
148
|
+
return errorResponse(reply, 'seed_failed', result.error ?? 'Node rejected the seed', 502);
|
|
149
|
+
}
|
|
150
|
+
return reply.send({ ok: true, data: { peersAdded: result.peersAdded ?? 0 } });
|
|
151
|
+
});
|
|
152
|
+
// DELETE /grants/:id — terminate the donated node.
|
|
153
|
+
app.delete('/grants/:id', async (request, reply) => {
|
|
154
|
+
const token = authenticate(request, reply, grants);
|
|
155
|
+
if (!token)
|
|
156
|
+
return reply;
|
|
157
|
+
if (!requireOwned(donations, request.params.id, token, reply))
|
|
158
|
+
return reply;
|
|
159
|
+
await donations.terminate(request.params.id);
|
|
160
|
+
return reply.send({ ok: true });
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//# 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":"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"}
|
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createPublicKey } from 'node:crypto';
|
|
11
11
|
/**
|
|
12
|
-
* Raw 32-byte Ed25519 public key, base64-encoded.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* Raw 32-byte Ed25519 public key, base64-encoded. This is the real production
|
|
13
|
+
* public key embedded by the release operator (see the "Release signing"
|
|
14
|
+
* runbook in `docs/cadre-host.md`). Before a real key was embedded the source
|
|
15
|
+
* shipped an all-zeros placeholder; `isPlaceholderReleaseKey` still detects that
|
|
16
|
+
* state and the publish guard refuses to ship it. Mismatch with the real signer
|
|
17
|
+
* surfaces as `signature_invalid`, which the UI treats as "update available but
|
|
18
|
+
* unverifiable" rather than auto-applying.
|
|
17
19
|
*/
|
|
18
20
|
const PROD_KEY_BASE64 = 'XyRVnOY9DVgU6xdMgJIguOsc9B2L1o2KoU9626Nk+OE=';
|
|
19
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release-key.js","sourceRoot":"","sources":["../../src/update/release-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"release-key.js","sourceRoot":"","sources":["../../src/update/release-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,eAAe,GAAG,8CAA8C,CAAC;AAEvE;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5E,MAAM,QAAQ,GAAG,GAAG,CAAC,yBAAyB,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,OAAO,cAAc,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClE,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,4CAA4C,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,0DAA0D;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1C,OAAO,eAAe,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AACrE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serfab/cadre-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted cadre node manager for basement-PC deployments",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"@libp2p/crypto": "^5.1.13",
|
|
65
65
|
"@libp2p/interface": "^3.1.0",
|
|
66
66
|
"@libp2p/peer-id": "^6.0.4",
|
|
67
|
-
"@serfab/cadre-cli": "^0.
|
|
68
|
-
"@serfab/cadre-core": "^0.
|
|
69
|
-
"@serfab/cadre-provider": "^0.
|
|
67
|
+
"@serfab/cadre-cli": "^0.9.0",
|
|
68
|
+
"@serfab/cadre-core": "^0.9.0",
|
|
69
|
+
"@serfab/cadre-provider": "^0.9.0",
|
|
70
70
|
"commander": "^14.0.2",
|
|
71
71
|
"debug": "^4.4.3",
|
|
72
72
|
"fastify": "^5.7.1",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server-level smoke: the /auth/* HTTP contract over the
|
|
2
|
+
* Server-level smoke: the /auth/* HTTP contract over the owner-node
|
|
3
3
|
* channel adapter. Validates the two behaviours the realignment must
|
|
4
4
|
* preserve:
|
|
5
5
|
* 1. With the node up, POST /auth/invites returns an encodedInvite (no 500).
|
|
6
6
|
* 2. With the node refusing connections, GET /auth/trust-circle still lists
|
|
7
7
|
* local labels, and POST /auth/invites returns 503 (not a raw 500).
|
|
8
8
|
*
|
|
9
|
-
* The
|
|
9
|
+
* The owner node is represented by a stub HTTP server matching the 6.6
|
|
10
10
|
* admin contract (the spawn path itself is covered by
|
|
11
|
-
* orchestrator-
|
|
11
|
+
* orchestrator-owner.test.ts).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
@@ -19,7 +19,7 @@ import { tmpdir } from 'node:os';
|
|
|
19
19
|
import { join } from 'node:path';
|
|
20
20
|
|
|
21
21
|
import { TrustCircleService, TrustCircleStore, createTrustCircleHandlers } from '../auth/index.js';
|
|
22
|
-
import {
|
|
22
|
+
import { OwnerNodeClient } from '../owner/index.js';
|
|
23
23
|
import { registerErrorHandler } from '../server/error-handler.js';
|
|
24
24
|
import { registerTrustCircleRoutes } from '../server/routes/trust-circle.js';
|
|
25
25
|
import { EventBus } from '../server/events/bus.js';
|
|
@@ -51,7 +51,7 @@ describe('host /auth delegation — node up', () => {
|
|
|
51
51
|
}
|
|
52
52
|
if (req.url === '/admin/invites' && req.method === 'POST') {
|
|
53
53
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
54
|
-
res.end(JSON.stringify({ ok: true, data: { invite: { partyId: 'p',
|
|
54
|
+
res.end(JSON.stringify({ ok: true, data: { invite: { partyId: 'p', ownerAddrs: [], token: 't', createdAt: 1 }, encodedInvite: 'ENC' } }));
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
if (req.url === '/admin/members' && req.method === 'GET') {
|
|
@@ -70,7 +70,7 @@ describe('host /auth delegation — node up', () => {
|
|
|
70
70
|
afterEach(async () => { await new Promise<void>((resolve) => server.close(() => resolve())); });
|
|
71
71
|
|
|
72
72
|
it('POST /auth/invites returns an encodedInvite (no 500)', async () => {
|
|
73
|
-
const client = new
|
|
73
|
+
const client = new OwnerNodeClient({ baseUrl, token: TOKEN });
|
|
74
74
|
const service = new TrustCircleService({ cadreNode: client, store: new TrustCircleStore(tmpRoot) });
|
|
75
75
|
const app = buildApp(service);
|
|
76
76
|
try {
|
|
@@ -103,7 +103,7 @@ describe('host /auth delegation — node refusing connections', () => {
|
|
|
103
103
|
const store = new TrustCircleStore(tmpRoot);
|
|
104
104
|
store.addMember({ peerId: '12D3KooWLocal', label: 'My laptop', addedAt: '2025-01-01T00:00:00Z' });
|
|
105
105
|
|
|
106
|
-
const client = new
|
|
106
|
+
const client = new OwnerNodeClient({ baseUrl: closedBaseUrl, token: TOKEN });
|
|
107
107
|
const service = new TrustCircleService({ cadreNode: client, store });
|
|
108
108
|
const app = buildApp(service);
|
|
109
109
|
try {
|