@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.
Files changed (198) hide show
  1. package/dist/auth/index.d.ts +2 -0
  2. package/dist/auth/index.js +1 -0
  3. package/dist/auth/index.js.map +1 -1
  4. package/dist/auth/self-label.d.ts +36 -0
  5. package/dist/auth/self-label.js +57 -0
  6. package/dist/auth/self-label.js.map +1 -0
  7. package/dist/auth/trust-circle.d.ts +36 -9
  8. package/dist/auth/trust-circle.js +40 -20
  9. package/dist/auth/trust-circle.js.map +1 -1
  10. package/dist/auth/types.d.ts +2 -2
  11. package/dist/bin/host.js +291 -54
  12. package/dist/bin/host.js.map +1 -1
  13. package/dist/donation/__tests__/fake-orchestrator.d.ts +103 -0
  14. package/dist/donation/__tests__/fake-orchestrator.js +180 -0
  15. package/dist/donation/__tests__/fake-orchestrator.js.map +1 -0
  16. package/dist/donation/donation-service.d.ts +361 -0
  17. package/dist/donation/donation-service.js +763 -0
  18. package/dist/donation/donation-service.js.map +1 -0
  19. package/dist/donation/donation-store.d.ts +49 -0
  20. package/dist/donation/donation-store.js +148 -0
  21. package/dist/donation/donation-store.js.map +1 -0
  22. package/dist/donation/donation-supervisor.d.ts +167 -0
  23. package/dist/donation/donation-supervisor.js +341 -0
  24. package/dist/donation/donation-supervisor.js.map +1 -0
  25. package/dist/donation/grant-service.d.ts +71 -0
  26. package/dist/donation/grant-service.js +163 -0
  27. package/dist/donation/grant-service.js.map +1 -0
  28. package/dist/donation/grant-store.d.ts +38 -0
  29. package/dist/donation/grant-store.js +135 -0
  30. package/dist/donation/grant-store.js.map +1 -0
  31. package/dist/donation/index.d.ts +20 -0
  32. package/dist/donation/index.js +17 -0
  33. package/dist/donation/index.js.map +1 -0
  34. package/dist/donation/types.d.ts +201 -0
  35. package/dist/donation/types.js +40 -0
  36. package/dist/donation/types.js.map +1 -0
  37. package/dist/index.d.ts +9 -5
  38. package/dist/index.js +7 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/installer/config.d.ts +26 -0
  41. package/dist/installer/config.js +13 -0
  42. package/dist/installer/config.js.map +1 -1
  43. package/dist/installer/index.d.ts +2 -0
  44. package/dist/installer/index.js +3 -0
  45. package/dist/installer/index.js.map +1 -1
  46. package/dist/installer/wizard.d.ts +4 -0
  47. package/dist/installer/wizard.js +5 -1
  48. package/dist/installer/wizard.js.map +1 -1
  49. package/dist/nat/nat-service.d.ts +5 -5
  50. package/dist/nat/nat-service.js +9 -9
  51. package/dist/nat/nat-service.js.map +1 -1
  52. package/dist/nat/types.d.ts +1 -1
  53. package/dist/orchestrator/host-process-orchestrator.d.ts +144 -32
  54. package/dist/orchestrator/host-process-orchestrator.js +397 -128
  55. package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
  56. package/dist/orchestrator/index.d.ts +3 -3
  57. package/dist/orchestrator/index.js +1 -1
  58. package/dist/orchestrator/index.js.map +1 -1
  59. package/dist/orchestrator/node-identity.d.ts +26 -0
  60. package/dist/orchestrator/node-identity.js +36 -0
  61. package/dist/orchestrator/node-identity.js.map +1 -0
  62. package/dist/orchestrator/port-allocator.d.ts +14 -0
  63. package/dist/orchestrator/port-allocator.js +45 -0
  64. package/dist/orchestrator/port-allocator.js.map +1 -1
  65. package/dist/orchestrator/state-store.d.ts +5 -5
  66. package/dist/orchestrator/state-store.js +2 -2
  67. package/dist/orchestrator/state-store.js.map +1 -1
  68. package/dist/orchestrator/types.d.ts +8 -8
  69. package/dist/owner/index.d.ts +11 -0
  70. package/dist/owner/index.js +11 -0
  71. package/dist/owner/index.js.map +1 -0
  72. package/dist/owner/owner-node-client.d.ts +110 -0
  73. package/dist/{authority/authority-node-client.js → owner/owner-node-client.js} +58 -17
  74. package/dist/owner/owner-node-client.js.map +1 -0
  75. package/dist/server/error-handler.d.ts +22 -1
  76. package/dist/server/error-handler.js +64 -1
  77. package/dist/server/error-handler.js.map +1 -1
  78. package/dist/server/events/types.d.ts +3 -0
  79. package/dist/server/index.d.ts +33 -2
  80. package/dist/server/index.js +43 -16
  81. package/dist/server/index.js.map +1 -1
  82. package/dist/server/routes/grants-admin.d.ts +17 -0
  83. package/dist/server/routes/grants-admin.js +32 -0
  84. package/dist/server/routes/grants-admin.js.map +1 -0
  85. package/dist/server/routes/grants.d.ts +28 -0
  86. package/dist/server/routes/grants.js +182 -0
  87. package/dist/server/routes/grants.js.map +1 -0
  88. package/dist/server/routes/nodes.d.ts +5 -4
  89. package/dist/server/routes/nodes.js +19 -18
  90. package/dist/server/routes/nodes.js.map +1 -1
  91. package/dist/server/routes/settings.d.ts +6 -1
  92. package/dist/server/routes/settings.js +5 -2
  93. package/dist/server/routes/settings.js.map +1 -1
  94. package/dist/server/routes/status.d.ts +8 -4
  95. package/dist/server/routes/status.js +9 -4
  96. package/dist/server/routes/status.js.map +1 -1
  97. package/dist/server/routes/strands.d.ts +20 -0
  98. package/dist/server/routes/strands.js +38 -0
  99. package/dist/server/routes/strands.js.map +1 -0
  100. package/dist/strands/index.d.ts +7 -0
  101. package/dist/strands/index.js +6 -0
  102. package/dist/strands/index.js.map +1 -0
  103. package/dist/strands/strand-service.d.ts +50 -0
  104. package/dist/strands/strand-service.js +103 -0
  105. package/dist/strands/strand-service.js.map +1 -0
  106. package/dist/strands/types.d.ts +93 -0
  107. package/dist/strands/types.js +23 -0
  108. package/dist/strands/types.js.map +1 -0
  109. package/dist/ui/assets/index-BVoqLjyX.css +1 -0
  110. package/dist/ui/assets/index-C-iks41M.js +17 -0
  111. package/dist/ui/assets/index-C-iks41M.js.map +1 -0
  112. package/dist/ui/index.html +2 -2
  113. package/dist/update/release-key.d.ts +1 -1
  114. package/dist/update/release-key.js +8 -6
  115. package/dist/update/release-key.js.map +1 -1
  116. package/package.json +5 -5
  117. package/src/__tests__/build-targets.test.ts +26 -0
  118. package/src/__tests__/global-setup.ts +51 -0
  119. package/src/__tests__/{host-authority.smoke.test.ts → host-owner.smoke.test.ts} +7 -7
  120. package/src/__tests__/orchestrator-env-scrub.test.ts +140 -0
  121. package/src/__tests__/orchestrator-node-identity.test.ts +204 -0
  122. package/src/__tests__/{orchestrator-authority.test.ts → orchestrator-owner.test.ts} +33 -33
  123. package/src/__tests__/orchestrator-pin-keys.test.ts +153 -0
  124. package/src/__tests__/orchestrator-push.test.ts +10 -10
  125. package/src/__tests__/orchestrator.test.ts +387 -6
  126. package/src/auth/__tests__/self-label.test.ts +85 -0
  127. package/src/auth/__tests__/trust-circle-integration.test.ts +152 -147
  128. package/src/auth/__tests__/trust-circle.test.ts +55 -11
  129. package/src/auth/index.ts +2 -0
  130. package/src/auth/self-label.ts +74 -0
  131. package/src/auth/trust-circle.ts +54 -22
  132. package/src/auth/types.ts +2 -2
  133. package/src/bin/host.ts +1299 -1038
  134. package/src/donation/__tests__/donation-service.test.ts +1068 -0
  135. package/src/donation/__tests__/donation-supervisor.test.ts +510 -0
  136. package/src/donation/__tests__/fake-orchestrator.test.ts +168 -0
  137. package/src/donation/__tests__/fake-orchestrator.ts +225 -0
  138. package/src/donation/__tests__/grant-service.test.ts +213 -0
  139. package/src/donation/__tests__/grant-store.test.ts +113 -0
  140. package/src/donation/donation-service.ts +962 -0
  141. package/src/donation/donation-store.ts +171 -0
  142. package/src/donation/donation-supervisor.ts +388 -0
  143. package/src/donation/grant-service.ts +200 -0
  144. package/src/donation/grant-store.ts +152 -0
  145. package/src/donation/index.ts +64 -0
  146. package/src/donation/types.ts +243 -0
  147. package/src/index.ts +51 -5
  148. package/src/installer/__tests__/config.test.ts +27 -1
  149. package/src/installer/__tests__/installer.smoke.test.ts +31 -1
  150. package/src/installer/__tests__/wizard.test.ts +5 -1
  151. package/src/installer/config.ts +34 -0
  152. package/src/installer/index.ts +5 -0
  153. package/src/installer/wizard.ts +12 -1
  154. package/src/nat/__tests__/nat-service.test.ts +5 -5
  155. package/src/nat/nat-service.ts +10 -10
  156. package/src/nat/types.ts +1 -1
  157. package/src/orchestrator/__tests__/node-identity.test.ts +62 -0
  158. package/src/orchestrator/host-process-orchestrator.ts +401 -134
  159. package/src/orchestrator/index.ts +3 -3
  160. package/src/orchestrator/node-identity.ts +40 -0
  161. package/src/orchestrator/port-allocator.ts +48 -0
  162. package/src/orchestrator/state-store.ts +7 -7
  163. package/src/orchestrator/types.ts +8 -8
  164. package/src/{authority/__tests__/authority-node-client.test.ts → owner/__tests__/owner-node-client.test.ts} +45 -17
  165. package/src/owner/index.ts +12 -0
  166. package/src/{authority/authority-node-client.ts → owner/owner-node-client.ts} +83 -23
  167. package/src/server/__tests__/error-handler.test.ts +22 -0
  168. package/src/server/__tests__/grants-admin-route.test.ts +73 -0
  169. package/src/server/__tests__/grants-route.test.ts +263 -0
  170. package/src/server/__tests__/nodes-route.test.ts +25 -25
  171. package/src/server/__tests__/server.smoke.test.ts +75 -0
  172. package/src/server/__tests__/settings-route.test.ts +17 -0
  173. package/src/server/__tests__/status-route.test.ts +21 -0
  174. package/src/server/__tests__/strands-route.test.ts +233 -0
  175. package/src/server/error-handler.ts +67 -1
  176. package/src/server/events/types.ts +1 -0
  177. package/src/server/index.ts +75 -17
  178. package/src/server/routes/grants-admin.ts +41 -0
  179. package/src/server/routes/grants.ts +210 -0
  180. package/src/server/routes/nodes.ts +19 -18
  181. package/src/server/routes/settings.ts +11 -3
  182. package/src/server/routes/status.ts +18 -8
  183. package/src/server/routes/strands.ts +54 -0
  184. package/src/strands/__tests__/strand-service.test.ts +165 -0
  185. package/src/strands/index.ts +14 -0
  186. package/src/strands/strand-service.ts +135 -0
  187. package/src/strands/types.ts +102 -0
  188. package/src/update/__tests__/release-key.test.ts +16 -3
  189. package/src/update/release-key.ts +8 -6
  190. package/dist/authority/authority-node-client.d.ts +0 -77
  191. package/dist/authority/authority-node-client.js.map +0 -1
  192. package/dist/authority/index.d.ts +0 -11
  193. package/dist/authority/index.js +0 -11
  194. package/dist/authority/index.js.map +0 -1
  195. package/dist/ui/assets/index-BlMXQPSw.js +0 -14
  196. package/dist/ui/assets/index-BlMXQPSw.js.map +0 -1
  197. package/dist/ui/assets/index-BwU5mJGF.css +0 -1
  198. package/src/authority/index.ts +0 -12
@@ -0,0 +1,135 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import debug from 'debug';
4
+ import { GrantError } from './types.js';
5
+ const log = debug('cadre:host:grant-store');
6
+ const FILE_VERSION = 1;
7
+ /**
8
+ * Atomic JSON store for `grants.json`. Modelled on `auth/trust-circle-store.ts`:
9
+ * a single file, written to `<path>.tmp` then renamed, with an in-memory cache
10
+ * keyed by token.
11
+ *
12
+ * Unlike the trust-circle pending rows, grants are **not** self-reaping: a
13
+ * grant may legitimately outlive many nodes, so expiry is evaluated at
14
+ * validate time (in `GrantService`) and the admin removes stale grants
15
+ * explicitly. This store keeps no clock.
16
+ *
17
+ * Concurrency assumption: only one cadre-host process owns a given rootDir
18
+ * (the orchestrator already enforces this). No file-locking primitives.
19
+ */
20
+ export class GrantStore {
21
+ constructor(rootDir) {
22
+ this.cache = null;
23
+ mkdirSync(rootDir, { recursive: true });
24
+ this.path = join(rootDir, 'grants.json');
25
+ }
26
+ filePath() {
27
+ return this.path;
28
+ }
29
+ /**
30
+ * Load (with caching). A missing file returns an empty store (fresh
31
+ * install). A present-but-malformed file throws — silently wiping it would
32
+ * revoke every grantee at once.
33
+ */
34
+ load() {
35
+ if (this.cache)
36
+ return this.cache;
37
+ if (!existsSync(this.path)) {
38
+ this.cache = emptyFile();
39
+ return this.cache;
40
+ }
41
+ let raw;
42
+ try {
43
+ raw = readFileSync(this.path, 'utf8');
44
+ }
45
+ catch (err) {
46
+ throw new GrantError('storage_error', `failed to read grants file at ${this.path}: ${err.message}`);
47
+ }
48
+ let parsed;
49
+ try {
50
+ parsed = JSON.parse(raw);
51
+ }
52
+ catch (err) {
53
+ throw new GrantError('storage_error', `grants file at ${this.path} is not valid JSON: ${err.message}`);
54
+ }
55
+ if (!isGrantFile(parsed)) {
56
+ throw new GrantError('storage_error', `grants file at ${this.path} has unexpected shape`);
57
+ }
58
+ this.cache = parsed;
59
+ return this.cache;
60
+ }
61
+ /** Persist the current state atomically (write then rename). */
62
+ save(state) {
63
+ // NOTE: rewrites the whole grants.json on every mutation. Fine at household
64
+ // scale (a handful of grantees); if grant counts ever grow large, switch to
65
+ // an append/compact log or per-token files.
66
+ mkdirSync(dirname(this.path), { recursive: true });
67
+ const payload = JSON.stringify({ ...state, version: FILE_VERSION }, null, 2);
68
+ const tmp = `${this.path}.tmp`;
69
+ try {
70
+ writeFileSync(tmp, payload, { encoding: 'utf8' });
71
+ renameSync(tmp, this.path);
72
+ }
73
+ catch (err) {
74
+ // Surface as storage_error (→ 500) like load(), not a bare Error (→
75
+ // "internal"); a write failure on issue/revoke should read consistently.
76
+ throw new GrantError('storage_error', `failed to write grants file at ${this.path}: ${err.message}`);
77
+ }
78
+ this.cache = { ...state, version: FILE_VERSION };
79
+ log('saved grants (%d) to %s', Object.keys(state.grants).length, this.path);
80
+ }
81
+ add(grant) {
82
+ const state = this.load();
83
+ const { token, ...rest } = grant;
84
+ state.grants[token] = rest;
85
+ this.save(state);
86
+ }
87
+ get(token) {
88
+ const state = this.load();
89
+ const row = state.grants[token];
90
+ if (!row)
91
+ return undefined;
92
+ return { token, ...row };
93
+ }
94
+ list() {
95
+ const state = this.load();
96
+ return Object.entries(state.grants).map(([token, row]) => ({ token, ...row }));
97
+ }
98
+ remove(token) {
99
+ const state = this.load();
100
+ if (!(token in state.grants))
101
+ return false;
102
+ delete state.grants[token];
103
+ this.save(state);
104
+ return true;
105
+ }
106
+ /**
107
+ * Mark a grant revoked at the given ISO instant. Idempotent: an
108
+ * already-revoked grant keeps its original `revokedAt`. Returns false when
109
+ * the token is unknown.
110
+ */
111
+ markRevoked(token, at) {
112
+ const state = this.load();
113
+ const row = state.grants[token];
114
+ if (!row)
115
+ return false;
116
+ if (!row.revokedAt)
117
+ row.revokedAt = at;
118
+ this.save(state);
119
+ return true;
120
+ }
121
+ }
122
+ function emptyFile() {
123
+ return { version: FILE_VERSION, grants: {} };
124
+ }
125
+ function isGrantFile(v) {
126
+ if (!v || typeof v !== 'object')
127
+ return false;
128
+ const obj = v;
129
+ if (obj.version !== FILE_VERSION)
130
+ return false;
131
+ if (!obj.grants || typeof obj.grants !== 'object')
132
+ return false;
133
+ return true;
134
+ }
135
+ //# sourceMappingURL=grant-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-store.js","sourceRoot":"","sources":["../../src/donation/grant-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Donation grant-token layer for cadre-host. See ./types.ts, ./grant-store.ts,
3
+ * and ./grant-service.ts.
4
+ *
5
+ * This layer gates *who may ask this host to donate a node*. The lifecycle
6
+ * service that consumes a validated grant to actually provision a node lands in
7
+ * the `2-donation-service` ticket, which imports the `GrantValidator` interface
8
+ * defined here.
9
+ */
10
+ export { GrantStore } from './grant-store.js';
11
+ export { GrantService, createGrantAdminHandlers, DEFAULT_MAX_NODES, } from './grant-service.js';
12
+ export type { GrantServiceOptions } from './grant-service.js';
13
+ export { GrantError, DonationError } from './types.js';
14
+ export type { Grant, GrantDenyReason, GrantValidation, GrantValidator, GrantFile, GrantAdminHandlers, GrantErrorCode, } from './types.js';
15
+ export { DonationStore } from './donation-store.js';
16
+ export { DonationService, DONATION_AWAITING_SEED_TTL_MS, DONATION_PROVISIONING_TTL_MS, DONATION_REAP_SWEEP_MS, } from './donation-service.js';
17
+ export type { DonationOrchestrator, DonationProvisionRequest, DonationSeedResult, DonationPeerInfo, DonationServiceOptions, RespawnResult, } from './donation-service.js';
18
+ export { DonationSupervisor, DONATION_RESPAWN_BACKOFF_BASE_MS, DONATION_RESPAWN_BACKOFF_MAX_MS, DONATION_RESPAWN_MAX_ATTEMPTS, DONATION_RESPAWN_HEALTHY_MS, DONATION_RESPAWN_SWEEP_MS, } from './donation-supervisor.js';
19
+ export type { DonationSupervisorOptions, SupervisedOrchestrator, } from './donation-supervisor.js';
20
+ export type { Donation, DonationView, DonationStatus, DonationFile, DonationErrorCode, } from './types.js';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Donation grant-token layer for cadre-host. See ./types.ts, ./grant-store.ts,
3
+ * and ./grant-service.ts.
4
+ *
5
+ * This layer gates *who may ask this host to donate a node*. The lifecycle
6
+ * service that consumes a validated grant to actually provision a node lands in
7
+ * the `2-donation-service` ticket, which imports the `GrantValidator` interface
8
+ * defined here.
9
+ */
10
+ export { GrantStore } from './grant-store.js';
11
+ export { GrantService, createGrantAdminHandlers, DEFAULT_MAX_NODES, } from './grant-service.js';
12
+ export { GrantError, DonationError } from './types.js';
13
+ /* ──────────────── donation lifecycle (2-donation-service) ──────────────── */
14
+ export { DonationStore } from './donation-store.js';
15
+ export { DonationService, DONATION_AWAITING_SEED_TTL_MS, DONATION_PROVISIONING_TTL_MS, DONATION_REAP_SWEEP_MS, } from './donation-service.js';
16
+ export { DonationSupervisor, DONATION_RESPAWN_BACKOFF_BASE_MS, DONATION_RESPAWN_BACKOFF_MAX_MS, DONATION_RESPAWN_MAX_ATTEMPTS, DONATION_RESPAWN_HEALTHY_MS, DONATION_RESPAWN_SWEEP_MS, } from './donation-supervisor.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/donation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWvD,+EAA+E;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,eAAe,EACf,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,+BAA+B,EAC/B,6BAA6B,EAC7B,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Donation grant-token types for cadre-host.
3
+ *
4
+ * A **grant token** is a high-entropy base64url secret the host admin issues,
5
+ * out-of-band (QR / copy-paste), to one *grantee* — a friend or family member
6
+ * whose cadre authority (their phone) may then ask this host to spawn donated
7
+ * nodes. The grantee presents the token as `Authorization: Bearer <grant-token>`
8
+ * on every donation request (the grantee-facing surface lands in the
9
+ * `2-donation-service` ticket).
10
+ *
11
+ * This module owns the grant's identity, expiry, and per-grantee quota. The
12
+ * *live-node tally* it checks the quota against is owned by the donation
13
+ * service, not by the grant layer — see `GrantValidator.validateForProvision`.
14
+ *
15
+ * **Grant ≠ trust-circle membership.** A trust-circle invite (`auth/`)
16
+ * authorises a device to join *the host's own* cadre. A donation grant
17
+ * authorises an external cadre authority to ask the host to spawn a node that
18
+ * joins *the grantee's* cadre. The two token flows are deliberately separate
19
+ * modules; this one reuses the *shape* of the trust-circle store (atomic
20
+ * write-then-rename JSON, token-keyed rows) but with grant — not invite —
21
+ * semantics: a grant is long-lived and reusable up to a quota, never
22
+ * one-time-redeemed.
23
+ */
24
+ /** A grant the host admin issued to one grantee (friend/family). */
25
+ export interface Grant {
26
+ /** base64url secret; also the bearer credential. The store key. */
27
+ token: string;
28
+ /** Human label chosen by the admin, e.g. "Alice's cadre". Display-only. */
29
+ label: string;
30
+ /** Max concurrently-live donated nodes this grant may hold. */
31
+ maxNodes: number;
32
+ /** ISO timestamp when the grant was issued. */
33
+ createdAt: string;
34
+ /** ISO expiry; absent = no expiry. */
35
+ expiresAt?: string;
36
+ /** Set when the admin revokes; a revoked grant validates as denied. */
37
+ revokedAt?: string;
38
+ }
39
+ /** Why a presented grant token was denied. */
40
+ export type GrantDenyReason = 'unknown_token' | 'expired' | 'revoked' | 'quota_exceeded';
41
+ /** Result of validating a presented bearer token. */
42
+ export interface GrantValidation {
43
+ ok: boolean;
44
+ /** The matched grant, when the token names one (present even on some denials). */
45
+ grant?: Grant;
46
+ /** Set when `ok` is false. */
47
+ reason?: GrantDenyReason;
48
+ }
49
+ /**
50
+ * The slice `2-donation-service` depends on. Keeps that ticket decoupled from
51
+ * the store implementation: it holds a `GrantValidator`, not a `GrantStore`.
52
+ *
53
+ * Both methods are pure functions of the store state (and, for provision, the
54
+ * caller-supplied count) — they never throw for a bad token, they return a
55
+ * denial. Issuance/revocation (which mutate) live on `GrantService`.
56
+ */
57
+ export interface GrantValidator {
58
+ /**
59
+ * Validate a presented bearer token for a *new* provision request.
60
+ *
61
+ * `liveNodeCount(token)` is supplied by the donation service — the grant
62
+ * layer owns identity/expiry/revocation; the donation service owns the
63
+ * authoritative live-node tally (it holds the grant→node records). The count
64
+ * is only consulted once identity/expiry/revocation have passed, so an
65
+ * unknown or dead token never invokes it.
66
+ */
67
+ validateForProvision(token: string, liveNodeCount: (token: string) => number): GrantValidation;
68
+ /**
69
+ * Validate for a non-provisioning op (peer/seed/terminate): identity + not
70
+ * expired/revoked. No quota check.
71
+ */
72
+ validate(token: string): GrantValidation;
73
+ }
74
+ /** On-disk shape of `grants.json`. */
75
+ export interface GrantFile {
76
+ version: 1;
77
+ /** keyed by token. */
78
+ grants: Record<string, Omit<Grant, 'token'>>;
79
+ }
80
+ /**
81
+ * Typed handlers exposed to the loopback management server for the **admin**
82
+ * grant surface (`/grants-admin`). Distinct from the grantee-facing `/grants`
83
+ * provisioning surface (`2-donation-service`): the admin surface is loopback,
84
+ * no bearer — same-machine admin, matching cadre-host's local-UI "no login"
85
+ * posture.
86
+ *
87
+ * Handlers throw a `GrantError` whose `.code` the server maps to an HTTP
88
+ * status (see `server/error-handler.ts`).
89
+ */
90
+ export interface GrantAdminHandlers {
91
+ postGrant(body: {
92
+ label: string;
93
+ maxNodes?: number;
94
+ ttlMs?: number;
95
+ }): Promise<{
96
+ grant: Grant;
97
+ }>;
98
+ listGrants(): Promise<{
99
+ grants: Grant[];
100
+ }>;
101
+ deleteGrant(token: string): Promise<void>;
102
+ }
103
+ /**
104
+ * Lifecycle of one donated node:
105
+ * provisioning → the child process is being spawned.
106
+ * awaiting_seed → spawned + reachable; waiting for the requester to present
107
+ * its phone-signed seed (`PUT /grants/:id/seed`).
108
+ * seeded → the seed was applied; the node has joined the requester's cadre.
109
+ * error → provisioning failed (orchestrator resources reclaimed).
110
+ * terminated → stopped + removed (kept in the store for audit).
111
+ *
112
+ * A donation is "live" (counts against a grant's `maxNodes` quota) while it is
113
+ * `provisioning`, `awaiting_seed`, or `seeded` — see `DonationStore.liveNodeCount`.
114
+ */
115
+ export type DonationStatus = 'provisioning' | 'awaiting_seed' | 'seeded' | 'error' | 'terminated';
116
+ /**
117
+ * One donated node, tracked host-side and persisted to `donations.json`.
118
+ *
119
+ * The host contributes capacity only: `partyId` is the REQUESTER's cadre (a
120
+ * foreign party), and `seedToken` gates the node's own `POST /seed` — it is
121
+ * minted host-side, persisted here (so a host restart in the request→seed gap
122
+ * can still present the seed), and NEVER returned to the grantee.
123
+ *
124
+ * The record also carries every input the original spawn consumed
125
+ * (`partyId` + `profile` + `bootstrapNodes` + `ownerKeys`), so a crashed node can
126
+ * be re-spawned from the record alone — see `DonationService.respawn`. Records
127
+ * written before those two fields existed have neither; such a record is NOT
128
+ * respawnable and is skipped (never crashes a sweep).
129
+ */
130
+ export interface Donation {
131
+ /** "grn_<nanoid>" — also the donated node's orchestrator containerId. */
132
+ id: string;
133
+ /** The grant token that authorized this donation (the quota key). */
134
+ grantToken: string;
135
+ /** The REQUESTER's cadre (a foreign party this host merely hosts a node for). */
136
+ partyId: string;
137
+ /**
138
+ * Requester control-network bootstrap multiaddrs — replayed on respawn.
139
+ * Absent on records written before respawn support; those are not respawnable.
140
+ */
141
+ bootstrapNodes?: string[];
142
+ /**
143
+ * Requester owner public key(s), base64url — replayed as the node's pinned
144
+ * seed-trust anchors (`CADRE_OWNER_KEYS`) on respawn. Absent on records written
145
+ * before respawn support; those are not respawnable.
146
+ */
147
+ ownerKeys?: string[];
148
+ /**
149
+ * Respawn bookkeeping, owned by the supervisor's backoff. Absent until the
150
+ * first respawn attempt. Not secret — it rides along in `DonationView` so the
151
+ * borrower's `GET /grants/:id` shows the counters for free.
152
+ */
153
+ respawn?: {
154
+ attempts: number;
155
+ lastAttemptAt: string;
156
+ };
157
+ /** Node profile. Defaults to `storage` so the node participates and is dialable. */
158
+ profile: 'storage' | 'transaction';
159
+ status: DonationStatus;
160
+ /** Orchestrator handle (opaque `pid:token`), set once the child is spawned. */
161
+ dockerId?: string;
162
+ /**
163
+ * Reserved: a donation's live peer identity is read fresh from the node's
164
+ * `/status` on each `getPeer` call and is NOT persisted here — this field is
165
+ * currently never written. Kept for a future cache if `/status` fan-out ever
166
+ * shows up as hot.
167
+ */
168
+ peerId?: string;
169
+ /** The node's loopback `POST /seed` URL. Host-internal — stripped from the wire view. */
170
+ seedEndpoint?: string;
171
+ /** Host↔node bearer gating `POST /seed`. Persisted; NEVER leaves the host. */
172
+ seedToken?: string;
173
+ createdAt: string;
174
+ updatedAt: string;
175
+ /** Failure detail when `status === 'error'`. */
176
+ error?: string;
177
+ }
178
+ /**
179
+ * Redacted wire shape returned to the grantee: the host-only secrets
180
+ * (`seedToken`) and host-internal loopback URL (`seedEndpoint`) are stripped.
181
+ */
182
+ export type DonationView = Omit<Donation, 'seedToken' | 'seedEndpoint'>;
183
+ /** On-disk shape of `donations.json`. Keyed by donation id. */
184
+ export interface DonationFile {
185
+ version: 1;
186
+ donations: Record<string, Omit<Donation, 'id'>>;
187
+ }
188
+ /** Error codes thrown by the donation service / grantee-facing routes. */
189
+ export type DonationErrorCode = 'unauthorized' | 'forbidden' | 'quota_exceeded' | 'invalid_request' | 'not_found' | 'invalid_state' | 'seed_failed' | 'peer_unavailable' | 'orchestrator_error' | 'storage_error';
190
+ /** Typed donation error carrying a stable `code` for HTTP mapping. */
191
+ export declare class DonationError extends Error {
192
+ readonly code: DonationErrorCode;
193
+ constructor(code: DonationErrorCode, message: string);
194
+ }
195
+ /** Error codes thrown by GrantStore / GrantService. */
196
+ export type GrantErrorCode = 'not_found' | 'invalid_label' | 'invalid_max_nodes' | 'invalid_ttl' | 'storage_error';
197
+ /** Typed error carrying a stable `code` for HTTP mapping. */
198
+ export declare class GrantError extends Error {
199
+ readonly code: GrantErrorCode;
200
+ constructor(code: GrantErrorCode, message: string);
201
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Donation grant-token types for cadre-host.
3
+ *
4
+ * A **grant token** is a high-entropy base64url secret the host admin issues,
5
+ * out-of-band (QR / copy-paste), to one *grantee* — a friend or family member
6
+ * whose cadre authority (their phone) may then ask this host to spawn donated
7
+ * nodes. The grantee presents the token as `Authorization: Bearer <grant-token>`
8
+ * on every donation request (the grantee-facing surface lands in the
9
+ * `2-donation-service` ticket).
10
+ *
11
+ * This module owns the grant's identity, expiry, and per-grantee quota. The
12
+ * *live-node tally* it checks the quota against is owned by the donation
13
+ * service, not by the grant layer — see `GrantValidator.validateForProvision`.
14
+ *
15
+ * **Grant ≠ trust-circle membership.** A trust-circle invite (`auth/`)
16
+ * authorises a device to join *the host's own* cadre. A donation grant
17
+ * authorises an external cadre authority to ask the host to spawn a node that
18
+ * joins *the grantee's* cadre. The two token flows are deliberately separate
19
+ * modules; this one reuses the *shape* of the trust-circle store (atomic
20
+ * write-then-rename JSON, token-keyed rows) but with grant — not invite —
21
+ * semantics: a grant is long-lived and reusable up to a quota, never
22
+ * one-time-redeemed.
23
+ */
24
+ /** Typed donation error carrying a stable `code` for HTTP mapping. */
25
+ export class DonationError extends Error {
26
+ constructor(code, message) {
27
+ super(message);
28
+ this.name = 'DonationError';
29
+ this.code = code;
30
+ }
31
+ }
32
+ /** Typed error carrying a stable `code` for HTTP mapping. */
33
+ export class GrantError extends Error {
34
+ constructor(code, message) {
35
+ super(message);
36
+ this.name = 'GrantError';
37
+ this.code = code;
38
+ }
39
+ }
40
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/donation/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAgMH,sEAAsE;AACtE,MAAM,OAAO,aAAc,SAAQ,KAAK;IAGtC,YAAY,IAAuB,EAAE,OAAe;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAUD,6DAA6D;AAC7D,MAAM,OAAO,UAAW,SAAQ,KAAK;IAGnC,YAAY,IAAoB,EAAE,OAAe;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -18,12 +18,16 @@
18
18
  * - cadre-host-local-ui
19
19
  */
20
20
  export type { Orchestrator, OrchestratorCreateRequest, OrchestratorCreateResult, OrchestratorStats, ContainerStatus, ContainerResources, } from '@serfab/cadre-provider';
21
- export { HostProcessOrchestrator, AUTHORITY_CONTAINER_ID } from './orchestrator/index.js';
22
- export type { HostProcessConfig, PersistedHandle, ManagedNodeInfo, NodeStateListener, NodePorts, AuthorityAdminEndpoint, AuthoritySpawnConfig, } from './orchestrator/index.js';
23
- export { AuthorityNodeClient, AuthorityNodeUnavailableError } from './authority/index.js';
24
- export type { AuthorityNodeClientOptions } from './authority/index.js';
25
- export { TrustCircleService, TrustCircleStore, TrustCircleError, createTrustCircleHandlers, parseDuration, DEFAULT_INVITE_TTL_MS, } from './auth/index.js';
21
+ export { HostProcessOrchestrator, OWNER_CONTAINER_ID } from './orchestrator/index.js';
22
+ export type { HostProcessConfig, PersistedHandle, ManagedNodeInfo, NodeStateListener, NodePorts, OwnerAdminEndpoint, OwnerSpawnConfig, } from './orchestrator/index.js';
23
+ export { OwnerNodeClient, OwnerNodeUnavailableError } from './owner/index.js';
24
+ export type { OwnerNodeClientOptions } from './owner/index.js';
25
+ export { TrustCircleService, TrustCircleStore, TrustCircleError, createTrustCircleHandlers, parseDuration, ensureSelfLabel, DEFAULT_INVITE_TTL_MS, } from './auth/index.js';
26
26
  export type { TrustCircleServiceOptions, CadreNodeLike, TrustCircleMember, PendingInvite, TrustCircleFile, TrustCircleSnapshot, TrustCircleHandlers, TrustCircleErrorCode, } from './auth/index.js';
27
+ export { StrandService, StrandError, createStrandHandlers } from './strands/index.js';
28
+ export type { StrandServiceOptions, CadreNodeLike as StrandCadreNodeLike, StrandSummary, StrandListSnapshot, StrandRemovalResult, StrandHandlers, StrandErrorCode, } from './strands/index.js';
29
+ export { GrantStore, GrantService, createGrantAdminHandlers, GrantError, DEFAULT_MAX_NODES, DonationStore, DonationService, DonationError, } from './donation/index.js';
30
+ export type { Grant, GrantDenyReason, GrantValidation, GrantValidator, GrantFile, GrantAdminHandlers, GrantErrorCode, GrantServiceOptions, Donation, DonationView, DonationStatus, DonationFile, DonationErrorCode, DonationProvisionRequest, DonationSeedResult, DonationPeerInfo, DonationServiceOptions, } from './donation/index.js';
27
31
  export { Installer, readHostConfig, updateHostConfig, writeHostConfig } from './installer/index.js';
28
32
  export type { InstallOptions, InstallResult, UninstallOptions, InstallerOptions, HostConfigFile, PushSettings, WizardAnswers, WizardDefaults, ServiceHost, ServiceHostContext, ServiceHostStatus, } from './installer/index.js';
29
33
  export { resolvePushCredentials, setFcmSecret, setApnsSecret, clearPushSecret, pushAccount, pushStatus, } from './push/index.js';
package/dist/index.js CHANGED
@@ -17,9 +17,13 @@
17
17
  * - cadre-host-installer
18
18
  * - cadre-host-local-ui
19
19
  */
20
- export { HostProcessOrchestrator, AUTHORITY_CONTAINER_ID } from './orchestrator/index.js';
21
- export { AuthorityNodeClient, AuthorityNodeUnavailableError } from './authority/index.js';
22
- export { TrustCircleService, TrustCircleStore, TrustCircleError, createTrustCircleHandlers, parseDuration, DEFAULT_INVITE_TTL_MS, } from './auth/index.js';
20
+ export { HostProcessOrchestrator, OWNER_CONTAINER_ID } from './orchestrator/index.js';
21
+ export { OwnerNodeClient, OwnerNodeUnavailableError } from './owner/index.js';
22
+ export { TrustCircleService, TrustCircleStore, TrustCircleError, createTrustCircleHandlers, parseDuration, ensureSelfLabel, DEFAULT_INVITE_TTL_MS, } from './auth/index.js';
23
+ /* ──────────────── strand management ──────────────── */
24
+ export { StrandService, StrandError, createStrandHandlers } from './strands/index.js';
25
+ /* ──────────────── donation grant tokens ──────────────── */
26
+ export { GrantStore, GrantService, createGrantAdminHandlers, GrantError, DEFAULT_MAX_NODES, DonationStore, DonationService, DonationError, } from './donation/index.js';
23
27
  export { Installer, readHostConfig, updateHostConfig, writeHostConfig } from './installer/index.js';
24
28
  /* ──────────────── push credentials (FCM/APNs) ──────────────── */
25
29
  export { resolvePushCredentials, setFcmSecret, setApnsSecret, clearPushSecret, pushAccount, pushStatus, } from './push/index.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAW1F,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAG1F,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,aAAa,EACb,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAYzB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAepG,mEAAmE;AAEnE,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,yBAAyB,EACzB,cAAc,EACd,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAe3B,OAAO,EACL,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,GACd,MAAM,gBAAgB,CAAC;AA+BxB,+DAA+D;AAE/D,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAWtF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,aAAa,EACb,eAAe,EACf,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAYzB,yDAAyD;AAEzD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAWtF,6DAA6D;AAE7D,OAAO,EACL,UAAU,EACV,YAAY,EACZ,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,qBAAqB,CAAC;AAqB7B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAepG,mEAAmE;AAEnE,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,yBAAyB,EACzB,cAAc,EACd,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAe3B,OAAO,EACL,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,GACd,MAAM,gBAAgB,CAAC;AA+BxB,+DAA+D;AAE/D,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
@@ -16,6 +16,20 @@ export interface UpdatesConfig {
16
16
  /** Optional override for the manifest URL (env var still wins). */
17
17
  manifestUrl?: string;
18
18
  }
19
+ /**
20
+ * Whether this host also runs its own personal cadre (the opt-in "founder"
21
+ * persona), on top of the always-on node-donor role.
22
+ *
23
+ * Default (absent or `enabled: false`) is donor-only: `cadre-host start` brings
24
+ * up the donation surface but spawns **no** owner node, so the trust-circle and
25
+ * NAT surfaces stay inactive. Set `enabled: true` (install-time choice) to also
26
+ * spawn the host's own owner node and enable `/auth` + `/nat`. See
27
+ * docs/cadre-host.md § Two roles: donor and founder.
28
+ */
29
+ export interface OwnCadreConfig {
30
+ /** When true, `start` spawns the host-owned owner node + trust-circle + NAT. */
31
+ enabled: boolean;
32
+ }
19
33
  /**
20
34
  * Non-secret push (FCM/APNs) settings. The actual private keys (and the FCM
21
35
  * service-account / APNs key identifiers) live in the secret store — see
@@ -55,6 +69,12 @@ export interface HostConfigFile {
55
69
  installerVersion: string;
56
70
  /** Update-flow settings; defaults: { autoApply: false }. */
57
71
  updates: UpdatesConfig;
72
+ /**
73
+ * Host-own-cadre (founder persona) opt-in. Optional — absent is treated as
74
+ * `{ enabled: false }` (donor-only). Written by the installer; read via
75
+ * `hostOwnsCadre()`. Not editable through /api/settings (install-time only).
76
+ */
77
+ ownCadre?: OwnCadreConfig;
58
78
  /**
59
79
  * Non-secret push settings (bundle id, sandbox/prod toggle, cooldown/debounce).
60
80
  * Optional — absent when push is not configured. Private keys never live here;
@@ -62,6 +82,12 @@ export interface HostConfigFile {
62
82
  */
63
83
  push?: PushSettings;
64
84
  }
85
+ /**
86
+ * Whether this host runs its own personal cadre (founder persona). Absent
87
+ * `ownCadre` ⇒ donor-only (false). The single source of truth for the
88
+ * founder/donor gate in `bin/host.ts start`.
89
+ */
90
+ export declare function hostOwnsCadre(cfg: HostConfigFile): boolean;
65
91
  export declare function writeHostConfig(filePath: string, cfg: HostConfigFile): void;
66
92
  /** Apply a partial patch to the persisted config (read-modify-write). */
67
93
  export declare function updateHostConfig(filePath: string, patch: Partial<Omit<HostConfigFile, 'version'>>): HostConfigFile;
@@ -12,6 +12,14 @@
12
12
  */
13
13
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
14
14
  import { dirname } from 'node:path';
15
+ /**
16
+ * Whether this host runs its own personal cadre (founder persona). Absent
17
+ * `ownCadre` ⇒ donor-only (false). The single source of truth for the
18
+ * founder/donor gate in `bin/host.ts start`.
19
+ */
20
+ export function hostOwnsCadre(cfg) {
21
+ return cfg.ownCadre?.enabled === true;
22
+ }
15
23
  const CURRENT_VERSION = 2;
16
24
  export function writeHostConfig(filePath, cfg) {
17
25
  if (cfg.version !== CURRENT_VERSION) {
@@ -108,6 +116,11 @@ function isHostConfigShape(v) {
108
116
  return false;
109
117
  if (updates.manifestUrl !== undefined && typeof updates.manifestUrl !== 'string')
110
118
  return false;
119
+ if (o.ownCadre !== undefined) {
120
+ const own = o.ownCadre;
121
+ if (!own || typeof own !== 'object' || typeof own.enabled !== 'boolean')
122
+ return false;
123
+ }
111
124
  return true;
112
125
  }
113
126
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/installer/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/installer/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8EpC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAmB;IAC/C,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,GAAmB;IACnE,IAAI,GAAG,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,mDAAmD,GAAG,CAAC,OAAO,cAAc,eAAe,GAAG,CAAC,CAAC;IAClH,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAC9B,QAAgB,EAChB,KAA+C;IAE/C,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAmB;QAC3B,GAAG,OAAO;QACV,GAAG,KAAK;QACR,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;QACzD,OAAO,EAAE,CAAC;KACX,CAAC;IACF,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uCAAuC,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,uBAAuB,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACtB,uEAAuE;QACvE,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC1C,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,4BAA4B,GAAG,CAAC,OAAO,wBAAwB,eAAe,GAAG,CACjH,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,6BAA6B,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iFAAiF;AACjF,SAAS,SAAS,CAAC,GAA4B,EAAE,QAAgB;IAC/D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,gCAAgC,CAAC,CAAC;IACnF,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,GAAG,CAAC,SAAmB;QAClC,MAAM,EAAE,GAAG,CAAC,MAAgB;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAoB;QACpC,OAAO,EAAE,GAAG,CAAC,OAAiB;QAC9B,YAAY,EAAE,GAAG,CAAC,YAAsB;QACxC,WAAW,EAAE,GAAG,CAAC,WAAsB;QACvC,WAAW,EAAE,GAAG,CAAC,WAAqB;QACtC,gBAAgB,EAAE,GAAG,CAAC,gBAA0B;QAChD,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,CAA0B;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ;QAClC,OAAO,CAAC,CAAC,WAAW,KAAK,SAAS;QAClC,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ;QACjC,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ,CACvC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAU;IACnC,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,IACE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ;QAClC,OAAO,CAAC,CAAC,WAAW,KAAK,SAAS;QAClC,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ;QACjC,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ,EACtC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,CAAC,OAA8C,CAAC;IACjE,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC/F,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,CAAC,CAAC,QAAmC,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -26,6 +26,8 @@ export interface InstallOptions {
26
26
  libp2pPort?: number;
27
27
  /** Disable UPnP probing on first run. */
28
28
  noUpnp?: boolean;
29
+ /** Also run the host's own personal cadre here (founder persona). Default false. */
30
+ ownCadre?: boolean;
29
31
  /** Open browser at end (default true; suppressed by --no-browser or non-TTY). */
30
32
  openBrowser?: boolean;
31
33
  /** Skip the enrollment-invite step (no QR printed). */
@@ -56,6 +56,7 @@ export class Installer {
56
56
  installedAt: new Date().toISOString(),
57
57
  installerVersion: this.installerVersion,
58
58
  updates: { autoApply: false },
59
+ ownCadre: { enabled: answers.ownCadre },
59
60
  };
60
61
  const cfgPath = configPath(answers.dataDir);
61
62
  writeHostConfig(cfgPath, cfg);
@@ -129,6 +130,7 @@ export class Installer {
129
130
  libp2pPort: opts.libp2pPort ?? defaults.libp2pPort,
130
131
  upnpEnabled: opts.noUpnp ? false : defaults.upnpEnabled,
131
132
  configureDdns: false,
133
+ ownCadre: opts.ownCadre ?? defaults.ownCadre,
132
134
  };
133
135
  }
134
136
  async resolveAnswers(opts) {
@@ -146,6 +148,7 @@ export class Installer {
146
148
  uiPort: opts.uiPort ?? platformDefaults.uiPort,
147
149
  libp2pPort: opts.libp2pPort ?? platformDefaults.libp2pPort,
148
150
  upnpEnabled: opts.noUpnp ? false : platformDefaults.upnpEnabled,
151
+ ownCadre: opts.ownCadre ?? platformDefaults.ownCadre,
149
152
  };
150
153
  const wizard = opts.wizard ?? runWizard;
151
154
  return await wizard(defaults);