@serfab/cadre-host 0.7.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/README.md +296 -101
- package/dist/auth/trust-circle.d.ts +45 -7
- package/dist/auth/trust-circle.js +116 -59
- package/dist/auth/trust-circle.js.map +1 -1
- package/dist/auth/types.d.ts +4 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/bin/host.js +426 -112
- 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 +12 -6
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/installer/browser.js +16 -17
- package/dist/installer/browser.js.map +1 -1
- package/dist/installer/config.d.ts +51 -0
- package/dist/installer/config.js +14 -1
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +7 -2
- package/dist/installer/index.js +17 -10
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/service-host/launchd.js +1 -1
- package/dist/installer/service-host/launchd.js.map +1 -1
- package/dist/installer/service-host/systemd.js +1 -1
- package/dist/installer/service-host/systemd.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/index.d.ts +1 -1
- package/dist/nat/nat-service.d.ts +45 -3
- package/dist/nat/nat-service.js +116 -3
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/secrets/index.d.ts +7 -0
- package/dist/nat/secrets/index.js +28 -2
- package/dist/nat/secrets/index.js.map +1 -1
- package/dist/nat/types.d.ts +3 -1
- package/dist/nat/types.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +68 -1
- package/dist/orchestrator/host-process-orchestrator.js +272 -39
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/orchestrator/index.d.ts +3 -2
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/state-store.d.ts +6 -5
- package/dist/orchestrator/state-store.js +10 -2
- package/dist/orchestrator/state-store.js.map +1 -1
- package/dist/orchestrator/types.d.ts +45 -10
- package/dist/orchestrator/types.js.map +1 -1
- 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 +95 -0
- package/dist/owner/owner-node-client.js +167 -0
- package/dist/owner/owner-node-client.js.map +1 -0
- package/dist/push/index.d.ts +67 -0
- package/dist/push/index.js +101 -0
- package/dist/push/index.js.map +1 -0
- package/dist/server/error-handler.d.ts +17 -0
- package/dist/server/error-handler.js +45 -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 -3
- package/dist/server/routes/nodes.js +39 -24
- 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/static.d.ts +12 -0
- package/dist/server/static.js +24 -7
- package/dist/server/static.js.map +1 -1
- package/dist/ui/assets/index-BlMXQPSw.js +14 -0
- package/dist/ui/assets/{index-C9uChk2P.js.map → index-BlMXQPSw.js.map} +1 -1
- package/dist/ui/index.html +1 -1
- package/dist/update/apply.d.ts +23 -1
- package/dist/update/apply.js +46 -4
- package/dist/update/apply.js.map +1 -1
- package/dist/update/index.d.ts +14 -3
- package/dist/update/index.js +35 -2
- package/dist/update/index.js.map +1 -1
- package/dist/update/manifest.d.ts +28 -9
- package/dist/update/manifest.js +56 -29
- package/dist/update/manifest.js.map +1 -1
- package/dist/update/release-key.d.ts +18 -7
- package/dist/update/release-key.js +29 -14
- package/dist/update/release-key.js.map +1 -1
- package/dist/update/sign.d.ts +46 -0
- package/dist/update/sign.js +53 -0
- package/dist/update/sign.js.map +1 -0
- package/package.json +9 -4
- package/service/README.md +95 -86
- package/src/__tests__/cli-invite.smoke.test.ts +6 -1
- package/src/__tests__/cli-nat.smoke.test.ts +6 -1
- package/src/__tests__/cli.smoke.test.ts +6 -1
- package/src/__tests__/host-owner.smoke.test.ts +123 -0
- package/src/__tests__/orchestrator-owner.test.ts +198 -0
- package/src/__tests__/orchestrator-pin-keys.test.ts +153 -0
- package/src/__tests__/orchestrator-push.test.ts +145 -0
- package/src/auth/__tests__/trust-circle-integration.test.ts +44 -25
- package/src/auth/__tests__/trust-circle.test.ts +91 -24
- package/src/auth/trust-circle.ts +363 -304
- package/src/auth/types.ts +4 -1
- package/src/bin/host.ts +1258 -905
- 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 +66 -3
- package/src/installer/__tests__/config.test.ts +27 -1
- package/src/installer/__tests__/installer.smoke.test.ts +94 -0
- package/src/installer/__tests__/wizard.test.ts +5 -1
- package/src/installer/browser.ts +40 -42
- package/src/installer/config.ts +61 -1
- package/src/installer/index.ts +23 -12
- package/src/installer/service-host/launchd.ts +1 -1
- package/src/installer/service-host/systemd.ts +1 -1
- package/src/installer/wizard.ts +12 -1
- package/src/nat/__tests__/ddns-updater.test.ts +1 -1
- package/src/nat/__tests__/nat-service.test.ts +192 -0
- package/src/nat/__tests__/port-mapper.test.ts +1 -1
- package/src/nat/__tests__/secrets.test.ts +23 -1
- package/src/nat/index.ts +1 -1
- package/src/nat/nat-service.ts +131 -5
- package/src/nat/secrets/index.ts +32 -2
- package/src/nat/types.ts +3 -1
- package/src/orchestrator/host-process-orchestrator.ts +322 -54
- package/src/orchestrator/index.ts +9 -2
- package/src/orchestrator/state-store.ts +21 -3
- package/src/orchestrator/types.ts +48 -2
- package/src/owner/__tests__/owner-node-client.test.ts +194 -0
- package/src/owner/index.ts +12 -0
- package/src/owner/owner-node-client.ts +233 -0
- package/src/push/__tests__/push.test.ts +90 -0
- package/src/push/index.ts +146 -0
- package/src/server/__tests__/error-handler.test.ts +26 -4
- 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 +90 -10
- package/src/server/__tests__/publishers.test.ts +1 -1
- package/src/server/__tests__/server.smoke.test.ts +75 -0
- package/src/server/__tests__/settings-route.test.ts +17 -0
- package/src/server/__tests__/static.test.ts +145 -0
- package/src/server/__tests__/status-route.test.ts +23 -2
- package/src/server/error-handler.ts +47 -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 +49 -26
- package/src/server/routes/settings.ts +11 -3
- package/src/server/routes/status.ts +18 -8
- package/src/server/static.ts +24 -8
- package/src/update/__tests__/apply.test.ts +58 -1
- package/src/update/__tests__/manifest.test.ts +41 -6
- package/src/update/__tests__/release-key.test.ts +42 -0
- package/src/update/__tests__/sign.test.ts +78 -0
- package/src/update/__tests__/update-service.integration.test.ts +44 -9
- package/src/update/apply.ts +46 -4
- package/src/update/index.ts +41 -3
- package/src/update/manifest.ts +214 -172
- package/src/update/release-key.ts +31 -15
- package/src/update/sign.ts +74 -0
- package/dist/ui/assets/index-C9uChk2P.js +0 -14
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* invalid_label, invalid_token → 400
|
|
8
8
|
* not_found, already_redeemed → 404
|
|
9
9
|
* expired → 410
|
|
10
|
+
* node_unavailable → 503
|
|
10
11
|
* storage_error → 500
|
|
11
12
|
* - NatError
|
|
12
13
|
* invalid_config, ddns_provider_unknown,
|
|
@@ -14,18 +15,35 @@
|
|
|
14
15
|
* secrets_unavailable, storage_error,
|
|
15
16
|
* mapping_failed, router_unreachable,
|
|
16
17
|
* ip_detection_failed, ddns_update_failed → 500
|
|
18
|
+
* node_unavailable → 503
|
|
17
19
|
* - UpdateErrorException
|
|
18
20
|
* manifest_invalid, no_update_available,
|
|
19
21
|
* min_previous_version → 400
|
|
20
22
|
* apply_in_progress → 409
|
|
21
23
|
* signature_invalid, unsupported_state_version,
|
|
22
24
|
* apply_failed, rollback_failed, storage_error → 500
|
|
25
|
+
* - GrantError
|
|
26
|
+
* invalid_label, invalid_max_nodes,
|
|
27
|
+
* invalid_ttl → 400
|
|
28
|
+
* not_found → 404
|
|
29
|
+
* storage_error → 500
|
|
30
|
+
* - DonationError
|
|
31
|
+
* invalid_request → 400
|
|
32
|
+
* unauthorized → 401
|
|
33
|
+
* forbidden → 403
|
|
34
|
+
* not_found → 404
|
|
35
|
+
* invalid_state → 409
|
|
36
|
+
* quota_exceeded → 429
|
|
37
|
+
* orchestrator_error, storage_error → 500
|
|
38
|
+
* seed_failed → 502
|
|
39
|
+
* peer_unavailable → 503
|
|
23
40
|
* - everything else → 500 (code "internal")
|
|
24
41
|
*/
|
|
25
42
|
import debug from 'debug';
|
|
26
43
|
import { TrustCircleError } from '../auth/types.js';
|
|
27
44
|
import { NatError } from '../nat/types.js';
|
|
28
45
|
import { UpdateErrorException } from '../update/types.js';
|
|
46
|
+
import { GrantError, DonationError } from '../donation/types.js';
|
|
29
47
|
const log = debug('cadre:host:error-handler');
|
|
30
48
|
const TRUST_STATUS = {
|
|
31
49
|
invalid_label: 400,
|
|
@@ -34,6 +52,7 @@ const TRUST_STATUS = {
|
|
|
34
52
|
already_redeemed: 404,
|
|
35
53
|
expired: 410,
|
|
36
54
|
storage_error: 500,
|
|
55
|
+
node_unavailable: 503,
|
|
37
56
|
};
|
|
38
57
|
const NAT_STATUS = {
|
|
39
58
|
invalid_config: 400,
|
|
@@ -45,6 +64,26 @@ const NAT_STATUS = {
|
|
|
45
64
|
router_unreachable: 500,
|
|
46
65
|
ip_detection_failed: 500,
|
|
47
66
|
ddns_update_failed: 500,
|
|
67
|
+
node_unavailable: 503,
|
|
68
|
+
};
|
|
69
|
+
const GRANT_STATUS = {
|
|
70
|
+
invalid_label: 400,
|
|
71
|
+
invalid_max_nodes: 400,
|
|
72
|
+
invalid_ttl: 400,
|
|
73
|
+
not_found: 404,
|
|
74
|
+
storage_error: 500,
|
|
75
|
+
};
|
|
76
|
+
const DONATION_STATUS = {
|
|
77
|
+
unauthorized: 401,
|
|
78
|
+
forbidden: 403,
|
|
79
|
+
quota_exceeded: 429,
|
|
80
|
+
invalid_request: 400,
|
|
81
|
+
not_found: 404,
|
|
82
|
+
invalid_state: 409,
|
|
83
|
+
seed_failed: 502,
|
|
84
|
+
peer_unavailable: 503,
|
|
85
|
+
orchestrator_error: 500,
|
|
86
|
+
storage_error: 500,
|
|
48
87
|
};
|
|
49
88
|
const UPDATE_STATUS = {
|
|
50
89
|
manifest_invalid: 400,
|
|
@@ -82,6 +121,12 @@ function classify(err) {
|
|
|
82
121
|
if (err instanceof UpdateErrorException) {
|
|
83
122
|
return { status: UPDATE_STATUS[err.code] ?? 500, code: err.code, message: err.message };
|
|
84
123
|
}
|
|
124
|
+
if (err instanceof GrantError) {
|
|
125
|
+
return { status: GRANT_STATUS[err.code] ?? 500, code: err.code, message: err.message };
|
|
126
|
+
}
|
|
127
|
+
if (err instanceof DonationError) {
|
|
128
|
+
return { status: DONATION_STATUS[err.code] ?? 500, code: err.code, message: err.message };
|
|
129
|
+
}
|
|
85
130
|
// Fastify body-parsing / validation errors have a statusCode we should honour.
|
|
86
131
|
if (typeof err.statusCode === 'number' && err.statusCode >= 400 && err.statusCode < 600) {
|
|
87
132
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/server/error-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/server/error-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { HostProcessOrchestrator } from '../orchestrator/index.js';
|
|
|
17
17
|
import type { TrustCircleService } from '../auth/index.js';
|
|
18
18
|
import type { NatService } from '../nat/index.js';
|
|
19
19
|
import type { UpdateService } from '../update/index.js';
|
|
20
|
+
import type { GrantService, DonationService } from '../donation/index.js';
|
|
20
21
|
import { EventBus } from './events/bus.js';
|
|
21
22
|
import { HostSettingsStore } from './settings-store.js';
|
|
22
23
|
export interface LocalUiServerOptions {
|
|
@@ -26,10 +27,33 @@ export interface LocalUiServerOptions {
|
|
|
26
27
|
dataDir: string;
|
|
27
28
|
/** Wired dependencies — all owned by the caller. */
|
|
28
29
|
orchestrator: HostProcessOrchestrator;
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Trust-circle service — present only when the host runs its own personal
|
|
32
|
+
* cadre (`ownCadre.enabled`). Absent in donor-only mode, where `/auth/*`
|
|
33
|
+
* stays unmounted and 404s.
|
|
34
|
+
*/
|
|
35
|
+
trustCircle?: TrustCircleService;
|
|
36
|
+
/**
|
|
37
|
+
* NAT service — present only when the host runs its own personal cadre.
|
|
38
|
+
* Absent in donor-only mode (loopback-only in v1), where `/nat/*` stays
|
|
39
|
+
* unmounted and 404s.
|
|
40
|
+
*/
|
|
41
|
+
nat?: NatService;
|
|
31
42
|
/** Optional — 6.4.2 lands this; nullable while still iterating. */
|
|
32
43
|
update?: UpdateService;
|
|
44
|
+
/**
|
|
45
|
+
* Donation grant layer. When present, mounts the loopback admin surface at
|
|
46
|
+
* `/grants-admin` (issue/list/revoke). Optional so existing callers/tests
|
|
47
|
+
* that don't exercise donations need not wire it.
|
|
48
|
+
*/
|
|
49
|
+
grants?: GrantService;
|
|
50
|
+
/**
|
|
51
|
+
* Donation lifecycle service. When present (alongside `grants`), mounts the
|
|
52
|
+
* bearer-gated grantee-facing provisioning surface at `/grants`
|
|
53
|
+
* (provision / peer / seed / terminate). Optional so callers/tests that don't
|
|
54
|
+
* exercise the donor path need not wire it.
|
|
55
|
+
*/
|
|
56
|
+
donations?: DonationService;
|
|
33
57
|
/** Settings I/O facade. Defaults to a new one rooted at `dataDir`. */
|
|
34
58
|
settingsStore?: HostSettingsStore;
|
|
35
59
|
/** Bus instance — pass the same one to subsystems that publish events. */
|
package/dist/server/index.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { createTrustCircleHandlers } from '../auth/index.js';
|
|
16
16
|
import { createNatHandlers } from '../nat/index.js';
|
|
17
17
|
import { createUpdateHandlers } from '../update/index.js';
|
|
18
|
+
import { createGrantAdminHandlers } from '../donation/index.js';
|
|
18
19
|
import { EventBus } from './events/bus.js';
|
|
19
20
|
import { registerSseRoute } from './events/sse-route.js';
|
|
20
21
|
import { registerErrorHandler } from './error-handler.js';
|
|
@@ -27,6 +28,8 @@ import { registerUpdateRoutes } from './routes/update.js';
|
|
|
27
28
|
import { registerStatusRoute } from './routes/status.js';
|
|
28
29
|
import { registerNodesRoutes } from './routes/nodes.js';
|
|
29
30
|
import { registerSettingsRoutes } from './routes/settings.js';
|
|
31
|
+
import { registerGrantsAdminRoutes } from './routes/grants-admin.js';
|
|
32
|
+
import { registerGrantsRoutes } from './routes/grants.js';
|
|
30
33
|
import { HostSettingsStore } from './settings-store.js';
|
|
31
34
|
/** How often to poll UpdateService.getState() looking for new versions. */
|
|
32
35
|
const UPDATE_OBSERVER_INTERVAL_MS = 60000;
|
|
@@ -44,17 +47,32 @@ export function createLocalUiServer(opts) {
|
|
|
44
47
|
});
|
|
45
48
|
registerStatusRoute(app, {
|
|
46
49
|
orchestrator: opts.orchestrator,
|
|
47
|
-
trustCircle: opts.trustCircle,
|
|
48
|
-
nat: opts.nat,
|
|
50
|
+
...(opts.trustCircle ? { trustCircle: opts.trustCircle } : {}),
|
|
51
|
+
...(opts.nat ? { nat: opts.nat } : {}),
|
|
49
52
|
...(opts.update ? { update: opts.update } : {}),
|
|
50
53
|
});
|
|
51
54
|
registerNodesRoutes(app, { orchestrator: opts.orchestrator });
|
|
52
|
-
registerSettingsRoutes(app, { settingsStore, nat: opts.nat, ...(opts.update ? { update: opts.update } : {}) });
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
registerSettingsRoutes(app, { settingsStore, ...(opts.nat ? { nat: opts.nat } : {}), ...(opts.update ? { update: opts.update } : {}) });
|
|
56
|
+
// Trust-circle + NAT surfaces exist only when the host runs its own personal
|
|
57
|
+
// cadre. In donor-only mode they're left unmounted, so `/auth/*` and `/nat/*`
|
|
58
|
+
// fall through to the static not-found handler and 404 (see static.ts).
|
|
59
|
+
if (opts.trustCircle) {
|
|
60
|
+
registerTrustCircleRoutes(app, { handlers: createTrustCircleHandlers(opts.trustCircle), events });
|
|
61
|
+
}
|
|
62
|
+
if (opts.nat) {
|
|
63
|
+
registerNatRoutes(app, { handlers: createNatHandlers(opts.nat), events });
|
|
64
|
+
}
|
|
55
65
|
if (opts.update) {
|
|
56
66
|
registerUpdateRoutes(app, { handlers: createUpdateHandlers(opts.update), events });
|
|
57
67
|
}
|
|
68
|
+
if (opts.grants) {
|
|
69
|
+
registerGrantsAdminRoutes(app, { handlers: createGrantAdminHandlers(opts.grants) });
|
|
70
|
+
// Grantee-facing provisioning surface needs both the donation service and a
|
|
71
|
+
// grant validator (the GrantService doubles as the validator).
|
|
72
|
+
if (opts.donations) {
|
|
73
|
+
registerGrantsRoutes(app, { donations: opts.donations, grants: opts.grants });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
58
76
|
// Static mount registers as the not-found handler — declared last so the
|
|
59
77
|
// API paths above win.
|
|
60
78
|
registerStaticMount(app, { ...(opts.staticRoot !== undefined ? { rootDir: opts.staticRoot } : {}) });
|
|
@@ -85,17 +103,20 @@ export function createLocalUiServer(opts) {
|
|
|
85
103
|
});
|
|
86
104
|
}));
|
|
87
105
|
// One-shot connectivity publish — the SPA will get a sane initial
|
|
88
|
-
// signal even if no settings have changed yet this session.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
// signal even if no settings have changed yet this session. Skipped in
|
|
107
|
+
// donor-only mode, where there is no NatService.
|
|
108
|
+
if (opts.nat) {
|
|
109
|
+
try {
|
|
110
|
+
const snap = opts.nat.getStatus();
|
|
111
|
+
events.publish({
|
|
112
|
+
type: 'connectivity-changed',
|
|
113
|
+
portMode: snap.portMode,
|
|
114
|
+
directReachability: snap.directReachability,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// NatService.start may not have completed; harmless to skip.
|
|
119
|
+
}
|
|
99
120
|
}
|
|
100
121
|
// Update observer: poll UpdateService.getState() every 60 s and emit
|
|
101
122
|
// `update-available` when `available.version` changes. Cheap, avoids
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
import type { FastifyInstance } from 'fastify';
|
|
13
|
+
import type { GrantAdminHandlers } from '../../donation/types.js';
|
|
14
|
+
export interface GrantsAdminRoutesOptions {
|
|
15
|
+
handlers: GrantAdminHandlers;
|
|
16
|
+
}
|
|
17
|
+
export declare function registerGrantsAdminRoutes(app: FastifyInstance, opts: GrantsAdminRoutesOptions): void;
|
|
@@ -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,9 +6,11 @@
|
|
|
6
6
|
* `onStateChange` (forwarded by `createLocalUiServer.start`) — the route
|
|
7
7
|
* handlers don't re-publish.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
12
14
|
*/
|
|
13
15
|
import type { FastifyInstance } from 'fastify';
|
|
14
16
|
import type { HostProcessOrchestrator } from '../../orchestrator/index.js';
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* `onStateChange` (forwarded by `createLocalUiServer.start`) — the route
|
|
7
7
|
* handlers don't re-publish.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
12
14
|
*/
|
|
13
15
|
import { existsSync, openSync, readSync, closeSync, statSync } from 'node:fs';
|
|
14
16
|
import { defaultLogPath } from '../../orchestrator/log-rotator.js';
|
|
@@ -64,32 +66,45 @@ export function registerNodesRoutes(app, opts) {
|
|
|
64
66
|
// forwards that to the bus. Don't double-publish here.
|
|
65
67
|
return { ok: true };
|
|
66
68
|
});
|
|
67
|
-
// start / restart: the
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
// follow-up ticket. Until then these endpoints stub out cleanly so the
|
|
72
|
-
// SPA gets a predictable error rather than a route-not-found 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.
|
|
73
73
|
app.post('/api/nodes/:id/start', async (request, reply) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
74
|
+
const { id } = request.params;
|
|
75
|
+
if (orchestrator.isOwnerNode(id)) {
|
|
76
|
+
if (!orchestrator.hasOwnerConfig()) {
|
|
77
|
+
return notImplemented(reply, `start ${id}: owner node has no saved spawn config.`);
|
|
78
|
+
}
|
|
79
|
+
const node = await orchestrator.ensureOwnerNode();
|
|
80
|
+
return { ok: true, data: { node } };
|
|
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;
|
|
86
|
+
if (orchestrator.isOwnerNode(id)) {
|
|
87
|
+
if (!orchestrator.hasOwnerConfig()) {
|
|
88
|
+
return notImplemented(reply, `restart ${id}: owner node has no saved spawn config.`);
|
|
89
|
+
}
|
|
90
|
+
const node = await orchestrator.restartOwnerNode();
|
|
91
|
+
return { ok: true, data: { node } };
|
|
92
|
+
}
|
|
93
|
+
return startRestartFallback(reply, orchestrator, id, 'restart');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** 404 for unknown ids; 501 not_implemented for known non-owner nodes. */
|
|
97
|
+
function startRestartFallback(reply, orchestrator, id, verb) {
|
|
98
|
+
if (!orchestrator.getNode(id)) {
|
|
99
|
+
return reply.code(404).send({
|
|
85
100
|
ok: false,
|
|
86
|
-
error: {
|
|
87
|
-
code: 'not_implemented',
|
|
88
|
-
message: `restart ${request.params.id}: cadre-host v1 has no auto-spawn path. ` +
|
|
89
|
-
`Restart the cadre-host service to bring up persisted nodes.`,
|
|
90
|
-
},
|
|
101
|
+
error: { code: 'not_found', message: `Unknown node: ${id}` },
|
|
91
102
|
});
|
|
92
|
-
}
|
|
103
|
+
}
|
|
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.`);
|
|
105
|
+
}
|
|
106
|
+
function notImplemented(reply, message) {
|
|
107
|
+
return reply.code(501).send({ ok: false, error: { code: 'not_implemented', message } });
|
|
93
108
|
}
|
|
94
109
|
/**
|
|
95
110
|
* Read the last `n` lines from a log file by walking the tail in chunks.
|