@camstack/system 1.2.432 → 1.2.434

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 (105) hide show
  1. package/dist/{tls-CpcGkeJV.mjs → agent-https-B8lKYVtU.mjs} +123 -617
  2. package/dist/{tls-C5IYg3V5.js → agent-https-BZIwS4xF.js} +157 -765
  3. package/dist/auth/device-ca.d.ts +0 -10
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  8. package/dist/builtins/alerts/alerts.addon.js +1 -1
  9. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  10. package/dist/builtins/autotrack/index.js +1 -1
  11. package/dist/builtins/autotrack/index.mjs +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  13. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  14. package/dist/builtins/calendars/calendars.addon.js +1 -1
  15. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  16. package/dist/builtins/camera-grid/index.js +1 -1
  17. package/dist/builtins/camera-grid/index.mjs +1 -1
  18. package/dist/builtins/composer/composer.addon.js +2 -2
  19. package/dist/builtins/composer/composer.addon.mjs +2 -2
  20. package/dist/builtins/console-logging/index.js +1 -1
  21. package/dist/builtins/console-logging/index.mjs +1 -1
  22. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  23. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  24. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  25. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  26. package/dist/builtins/hub-forwarder/index.js +1 -1
  27. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  28. package/dist/builtins/keychain/index.js +3 -3
  29. package/dist/builtins/keychain/index.mjs +3 -3
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  31. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.js +9 -4
  33. package/dist/builtins/local-auth/local-auth.addon.mjs +9 -4
  34. package/dist/builtins/local-network/address-book.d.ts +13 -0
  35. package/dist/builtins/local-network/agent-mdns.d.ts +51 -0
  36. package/dist/builtins/local-network/local-network.addon.js +74 -53
  37. package/dist/builtins/local-network/local-network.addon.mjs +56 -36
  38. package/dist/builtins/loki-logging/index.js +1 -1
  39. package/dist/builtins/loki-logging/index.mjs +1 -1
  40. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  41. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  42. package/dist/builtins/platform-probe/index.js +1 -1
  43. package/dist/builtins/platform-probe/index.mjs +1 -1
  44. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  45. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  46. package/dist/builtins/snapshot/index.js +1 -1
  47. package/dist/builtins/snapshot/index.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  49. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  50. package/dist/builtins/sqlite-storage/index.js +1 -1
  51. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  52. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -3
  53. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -3
  54. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  55. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  56. package/dist/builtins/system-config/system-config.addon.js +1388 -31
  57. package/dist/builtins/system-config/system-config.addon.mjs +1362 -28
  58. package/dist/builtins/winston-logging/index.js +1 -1
  59. package/dist/builtins/winston-logging/index.mjs +1 -1
  60. package/dist/{composition-sources-CFlUdpO7.js → composition-sources-B7R1oQnY.js} +1 -1
  61. package/dist/{composition-sources-DpbL38z3.mjs → composition-sources-CqrDyHM1.mjs} +1 -1
  62. package/dist/{constants-B89vjrSm.mjs → constants-CrMPWOug.mjs} +2 -2
  63. package/dist/{constants-DfP8V3eX.js → constants-CuOfMdYe.js} +2 -2
  64. package/dist/{device-ca-4IY98a_-.mjs → device-ca-CDjGQQPd.mjs} +17 -6
  65. package/dist/{device-ca-BCyAPmIj.js → device-ca-D21ZP00q.js} +17 -6
  66. package/dist/{dist-BCKOeWu_.js → dist-CCH3XPqO.js} +86 -6
  67. package/dist/{dist-Bt8GTRHB.mjs → dist-CYcBqnXk.mjs} +69 -7
  68. package/dist/file-host-worker.js +1 -1
  69. package/dist/file-host-worker.mjs +1 -1
  70. package/dist/hub-mdns-ciao-CBLvq2oj.js +821 -0
  71. package/dist/hub-mdns-ciao-DJRwtlA8.mjs +648 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.js +176 -67
  74. package/dist/index.mjs +96 -13
  75. package/dist/kernel/index.d.ts +5 -4
  76. package/dist/kernel/moleculer/agent-mesh-authenticator.d.ts +12 -1
  77. package/dist/kernel/moleculer/agent-ui-unlock.d.ts +40 -0
  78. package/dist/kernel/moleculer/mesh-authenticator.d.ts +6 -0
  79. package/dist/kernel/moleculer/mesh-mtls/constants.d.ts +1 -1
  80. package/dist/kernel/moleculer/mesh-mtls/index.d.ts +3 -2
  81. package/dist/kernel/moleculer/mesh-mtls/mesh-ca.d.ts +4 -9
  82. package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +2 -2
  83. package/dist/kernel/moleculer/mesh-mtls/mesh-join-token.d.ts +3 -2
  84. package/dist/kernel/moleculer/mesh-mtls/mesh-peer-transport.d.ts +7 -0
  85. package/dist/kernel/moleculer/mesh-mtls/pairing/agent-pairing.d.ts +204 -0
  86. package/dist/kernel/moleculer/mesh-mtls/pairing/hub-pairing.d.ts +115 -0
  87. package/dist/kernel/moleculer/mesh-mtls/pairing/index.d.ts +3 -0
  88. package/dist/kernel/moleculer/mesh-mtls/pairing/pairing-protocol.d.ts +107 -0
  89. package/dist/kernel/moleculer/mesh-mtls/tls-tcp-transporter.d.ts +18 -0
  90. package/dist/{lib-B_R6sPC3.mjs → lib-BeGzVQcd.mjs} +1 -1
  91. package/dist/{lib-DOJ5yyWA.js → lib-NNxHSOIE.js} +1 -1
  92. package/dist/{restore-archive-Bh34W8J8.mjs → restore-archive-5ml7uYMf.mjs} +1 -1
  93. package/dist/{restore-archive-DhHH7VQp.js → restore-archive-Do0QwAik.js} +1 -1
  94. package/dist/{retired-settings-keys-u3Wwkdrq.js → retired-settings-keys-CEiCaI_j.js} +1 -1
  95. package/dist/{retired-settings-keys-CmcEitC2.mjs → retired-settings-keys-CyDdyjLP.mjs} +1 -1
  96. package/dist/tls/agent-https-names.d.ts +2 -0
  97. package/dist/tls/cert-backdate.d.ts +10 -0
  98. package/dist/tls/cert-issue.d.ts +0 -20
  99. package/dist/{wal-maintenance-uQGuStFW.mjs → wal-maintenance-DgLLPK14.mjs} +0 -0
  100. package/dist/{wal-maintenance-ChzCPUi2.js → wal-maintenance-LZYd8iT-.js} +0 -0
  101. package/dist/{x509.es-CAjSsrkk.mjs → x509.es-CDvj67p1.mjs} +14 -1
  102. package/dist/{x509.es-Dzl0BbmG.js → x509.es-r7Ht1thA.js} +19 -0
  103. package/package.json +1 -1
  104. package/dist/agent-https-names-CpUSuP4w.js +0 -120
  105. package/dist/agent-https-names-uetr4OQl.mjs +0 -91
@@ -0,0 +1,204 @@
1
+ import { type AgentPairingState } from "./pairing-protocol.js";
2
+ /** What the agent persists when a pairing completes. */
3
+ export interface InstalledPairing {
4
+ readonly meshTls: {
5
+ readonly certPem: string;
6
+ readonly keyPem: string;
7
+ readonly caCertPem: string;
8
+ };
9
+ /** Host the broker dials (no port: the TLS port is the cluster default). */
10
+ readonly hubAddress: string;
11
+ readonly hubUrl: string;
12
+ }
13
+ /** What the agent's own machine is told, and nobody else (console, 0600 file, local window). */
14
+ export interface LocalPairingSecret {
15
+ readonly pairingId: string;
16
+ readonly pin: string;
17
+ /** The six digits, once the hub revealed its nonce; `null` before that. */
18
+ readonly code: string | null;
19
+ readonly hub: {
20
+ readonly nodeId: string;
21
+ readonly url: string;
22
+ readonly caFingerprint: string;
23
+ };
24
+ readonly expiresAt: number;
25
+ }
26
+ export interface AgentPairingDeps {
27
+ readonly nodeId: string;
28
+ readonly version: string | null;
29
+ /** True when `agent.json` already holds mesh material. */
30
+ readonly isPaired: () => boolean;
31
+ /** Persist the material and reconnect. A throw cancels the pairing. */
32
+ readonly install: (pairing: InstalledPairing) => Promise<void>;
33
+ readonly now?: () => number;
34
+ readonly log?: (message: string, meta?: Record<string, unknown>) => void;
35
+ /** The pairing state may have changed (pending <-> unpaired): re-announce it. */
36
+ readonly onStateChange?: () => void;
37
+ /**
38
+ * A pairing needs the operator's eyes on THIS machine: print the PIN and the code to the console,
39
+ * write them to a 0600 file, show them in the local window. Called at the offer (code `null`)
40
+ * and again when the code is known.
41
+ */
42
+ readonly onLocalSecret?: (secret: LocalPairingSecret) => void;
43
+ /** The pairing ended: remove what `onLocalSecret` wrote. */
44
+ readonly onLocalSecretCleared?: (pairingId: string) => void;
45
+ /**
46
+ * The LOCAL RESET CODE: print it to the console and a 0600 file. It clears the offer lockouts and
47
+ * the pending offers (a stranger who filled the slots with throwaway CAs would otherwise block
48
+ * pairing). Called at construction and again each time it is used.
49
+ */
50
+ readonly onResetCode?: (code: string) => void;
51
+ }
52
+ export type PairingFailure = {
53
+ readonly ok: false;
54
+ readonly status: number;
55
+ readonly error: string;
56
+ readonly message: string;
57
+ /** Set on a rationed offer: how long until it may be tried again. */
58
+ readonly retryAfterMs?: number;
59
+ };
60
+ export type PairingResult<T> = ({
61
+ readonly ok: true;
62
+ } & T) | PairingFailure;
63
+ /** What an unauthenticated `hello` says. */
64
+ export interface AgentHello {
65
+ /** The pairing protocol version the agent speaks; the hub compares it. */
66
+ readonly protocol: number;
67
+ readonly nodeId: string;
68
+ readonly version: string | null;
69
+ readonly state: AgentPairingState;
70
+ readonly pendingExpiresAt: number | null;
71
+ }
72
+ export interface OfferRequest {
73
+ readonly protocol: number;
74
+ readonly hubNodeId: string;
75
+ readonly hubCaCertPem: string;
76
+ readonly commitment: string;
77
+ readonly hubUrl: string;
78
+ readonly hubMeshAddress: string;
79
+ /** The offer, signed with the hub CA's KEY: a public CA certificate alone cannot make one. */
80
+ readonly signature: string;
81
+ }
82
+ export interface OfferAnswer {
83
+ readonly pairingId: string;
84
+ readonly csrPem: string;
85
+ readonly agentNonce: string;
86
+ readonly expiresAt: number;
87
+ }
88
+ export type PairingOutcome = "completed" | "rejected" | "expired" | "code-mismatch" | "failed";
89
+ /** Who is asking, as the HTTP layer decided it (genuine loopback, no forwarding header). */
90
+ export interface PairingRequestContext {
91
+ readonly local: boolean;
92
+ /** The caller's address, for per-source throttling of wrong PINs / reset codes. */
93
+ readonly source?: string;
94
+ }
95
+ /** One pending pairing as the NETWORK may see it: who asks, never a secret. */
96
+ export interface PendingPairingPublic {
97
+ readonly hub: {
98
+ readonly nodeId: string;
99
+ readonly url: string;
100
+ readonly caFingerprint: string;
101
+ };
102
+ readonly expiresAt: number;
103
+ }
104
+ /** What anyone on the network may read from `status`. */
105
+ export interface AgentPairingPublicView {
106
+ readonly state: AgentPairingState;
107
+ readonly pending: readonly PendingPairingPublic[];
108
+ }
109
+ /** The local view: the same, plus what only the agent's own machine may see. */
110
+ export interface AgentPairingLocalView extends AgentPairingPublicView {
111
+ readonly pending: readonly (PendingPairingPublic & {
112
+ readonly pairingId: string;
113
+ readonly pin: string;
114
+ readonly code: string | null;
115
+ readonly localConfirmed: boolean;
116
+ })[];
117
+ }
118
+ /** What the hub learns by polling with the pairing id it was given. */
119
+ export interface AgentPairingPoll {
120
+ /** `pending` while the pairing lives, `idle` once it ended. */
121
+ readonly state: "pending" | "idle";
122
+ readonly localConfirmed: boolean;
123
+ readonly last: {
124
+ readonly pairingId: string;
125
+ readonly outcome: PairingOutcome;
126
+ } | null;
127
+ }
128
+ /** `host:6443` / `[::1]:6443` / `host` → the host, port dropped. */
129
+ export declare function hubHostFromMeshAddress(address: string): string;
130
+ export declare class AgentPairing {
131
+ private readonly deps;
132
+ private readonly sessions;
133
+ /** Outcome of each recent pairing BY id (bounded): ending others never overwrites an answer. */
134
+ private readonly outcomes;
135
+ /** When pairings that never reached local confirmation ended. */
136
+ private abandoned;
137
+ private lockedUntil;
138
+ private readonly perCa;
139
+ private readonly pinBySource;
140
+ private globalPinFailures;
141
+ private resetCode;
142
+ /** Commitments already offered (signed offers are single use) until their signature is stale. */
143
+ private readonly seenCommitments;
144
+ private readonly now;
145
+ constructor(deps: AgentPairingDeps);
146
+ hello(): AgentHello;
147
+ state(): AgentPairingState;
148
+ /** Who is asking to pair, and until when. No code, no pairing id, no PIN. */
149
+ publicView(): AgentPairingPublicView;
150
+ /** The view for the agent's own machine: also the pairing ids, the codes and the PINs. */
151
+ localView(): AgentPairingLocalView;
152
+ offer(body: unknown): Promise<PairingResult<{
153
+ answer: OfferAnswer;
154
+ }>>;
155
+ /** The hub reveals its nonce; the agent checks it against the commitment and derives the code. */
156
+ reveal(body: unknown): PairingResult<object>;
157
+ /** The hub polls with the pairing id: did the agent's operator confirm, did it end? */
158
+ poll(body: unknown): PairingResult<{
159
+ poll: AgentPairingPoll;
160
+ }>;
161
+ /**
162
+ * The operator typed the code the HUB shows. From the agent's own machine that is enough; from
163
+ * anywhere else the one-time PIN of THIS pairing (read on the agent's console) must come with
164
+ * it. A wrong code counts against the pairing (the third cancels it); a wrong PIN counts against
165
+ * the agent (five lock confirming for five minutes).
166
+ */
167
+ confirm(body: unknown, ctx: PairingRequestContext): PairingResult<object>;
168
+ reject(body: unknown, ctx: PairingRequestContext): PairingResult<object>;
169
+ /** The hub withdraws its own pairing (authorized by the pairing id it was given). */
170
+ cancel(body: unknown): PairingResult<object>;
171
+ complete(body: unknown): Promise<PairingResult<object>>;
172
+ /** The agent forgot its hub (local action): the pairing slate is clean. */
173
+ noteUnpaired(): void;
174
+ /** Remaining lock for a source, or `null`. Loopback never calls this. */
175
+ private sourceLock;
176
+ /** A wrong PIN or reset code from `source`: five in five minutes lock THAT source, doubling. */
177
+ private noteWrongSecret;
178
+ /**
179
+ * The operator clears the lockouts, the budget and every pending offer. From this machine
180
+ * (genuine loopback) or with the LOCAL RESET CODE printed at boot; the code is renewed on use.
181
+ */
182
+ reset(body: unknown, ctx: PairingRequestContext): PairingResult<object>;
183
+ /** Verify the CA-key signature of an offer and spend its commitment. */
184
+ private checkOfferSignature;
185
+ /**
186
+ * All slots taken: a signed offer makes room by dropping the oldest pairing whose hub never showed
187
+ * activity (no reveal, no poll) and that no operator confirmed. `false` when there is none.
188
+ */
189
+ private makeRoom;
190
+ /** Offers that never reached local confirmation are rationed - globally and per hub CA. */
191
+ private rationOffer;
192
+ /** A pairing ended without the local operator confirming it: it counts against the budget. */
193
+ private noteAbandoned;
194
+ /** The pairing this local request means, else the refusal. */
195
+ private localTarget;
196
+ private announceLocally;
197
+ private checkCompletion;
198
+ /** The live session `body.pairingId` names, else the refusal. */
199
+ private pending;
200
+ private expireIfDue;
201
+ private outcomeOf;
202
+ private rememberOutcome;
203
+ private end;
204
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The hub's side of pairing (D857): starts a pairing with an agent it found
3
+ * (mDNS) or was given an address for, computes the same six digits the agent
4
+ * shows, and signs the agent's certificate ONLY after the operator confirmed
5
+ * them on the hub AND the agent's own operator confirmed them there.
6
+ *
7
+ * Either confirmation may come first. The hub learns the agent's by polling the
8
+ * agent's pairing status (`advance`), never by being told: nothing the agent
9
+ * says is trusted beyond "my operator confirmed", and even that only matters
10
+ * together with the hub's own confirmation.
11
+ *
12
+ * Transport-agnostic: `AgentPairingTransport` is the HTTP client in production
13
+ * and an in-memory shim (or a man in the middle) in specs.
14
+ */
15
+ import { type MeshCaSigner } from "../mesh-ca.js";
16
+ import type { AgentHello, AgentPairingPoll, OfferAnswer, OfferRequest } from "./agent-pairing.js";
17
+ /** Where an agent's API is. */
18
+ export interface AgentEndpoint {
19
+ readonly host: string;
20
+ readonly port: number;
21
+ }
22
+ /** A refusal the agent sent, kept with its machine-readable code. */
23
+ export declare class PairingHttpError extends Error {
24
+ readonly status: number;
25
+ readonly code: string;
26
+ constructor(status: number, code: string, message: string);
27
+ }
28
+ /** The agent's pairing HTTP surface, as the hub calls it. Throws {@link PairingHttpError}. */
29
+ export interface AgentPairingTransport {
30
+ hello(endpoint: AgentEndpoint): Promise<AgentHello>;
31
+ offer(endpoint: AgentEndpoint, body: OfferRequest): Promise<OfferAnswer>;
32
+ reveal(endpoint: AgentEndpoint, body: {
33
+ pairingId: string;
34
+ hubNonce: string;
35
+ }): Promise<void>;
36
+ /** Did the agent's operator confirm? Authorized by the pairing id. */
37
+ poll(endpoint: AgentEndpoint, pairingId: string): Promise<AgentPairingPoll>;
38
+ complete(endpoint: AgentEndpoint, body: {
39
+ pairingId: string;
40
+ leafPem: string;
41
+ caCertPem: string;
42
+ proof: string;
43
+ }): Promise<void>;
44
+ reject(endpoint: AgentEndpoint, pairingId: string): Promise<void>;
45
+ }
46
+ export type HubPairingState = "awaiting-confirmation" | "completing" | "paired" | "rejected" | "expired" | "failed";
47
+ export interface HubPairingView {
48
+ readonly pairingId: string;
49
+ readonly nodeId: string;
50
+ readonly endpoint: AgentEndpoint;
51
+ /** The six digits the agent's page shows. The operator types them on the hub to confirm. */
52
+ readonly code: string;
53
+ readonly state: HubPairingState;
54
+ readonly hubConfirmed: boolean;
55
+ readonly agentConfirmed: boolean;
56
+ readonly expiresAt: number;
57
+ readonly failure: string | null;
58
+ }
59
+ export interface HubPairingDeps {
60
+ readonly transport: AgentPairingTransport;
61
+ /** The mesh CA, `null` while it is not ready. */
62
+ readonly ca: () => MeshCaSigner | null;
63
+ readonly hubNodeId: string;
64
+ readonly hubUrl: () => Promise<string>;
65
+ readonly hubMeshAddress: () => Promise<string>;
66
+ /** True when `nodeId` is connected to the mesh with a verified certificate. */
67
+ readonly isNodeConnected: (nodeId: string) => boolean;
68
+ /** Node ids the hub already knows (certificates issued, nodes seen), for the case-insensitive collision check. */
69
+ readonly knownNodeIds?: () => readonly string[];
70
+ /** Record a leaf this hub just issued (the certificate registry). */
71
+ readonly recordIssued: (nodeId: string, certPem: string) => Promise<void>;
72
+ /**
73
+ * Put the leaves of `nodeId` on the CRL: a node id is held by one device. With `keepCertPem`, every
74
+ * leaf EXCEPT that one - the previous leaves go only after the new pairing completed.
75
+ */
76
+ readonly revokeNode: (nodeId: string, reason: string, keepCertPem?: string) => Promise<void>;
77
+ /** Revoke ONE leaf (it was never presented): used when the agent provably did not install it. */
78
+ /** True when the node's live mesh connection presents exactly this leaf (registry convergence). */
79
+ readonly isLeafPresented?: (nodeId: string, certPem: string) => boolean;
80
+ readonly revokeLeaf?: (nodeId: string, certPem: string, reason: string) => Promise<void>;
81
+ readonly now?: () => number;
82
+ readonly log?: (message: string, meta?: Record<string, unknown>) => void;
83
+ }
84
+ export declare class HubPairing {
85
+ private readonly deps;
86
+ private readonly sessions;
87
+ private readonly now;
88
+ constructor(deps: HubPairingDeps);
89
+ /** Offer the hub's CA to the agent at `endpoint` and derive the code. Throws an `Error` with the reason. */
90
+ begin(endpoint: AgentEndpoint): Promise<HubPairingView>;
91
+ /** The operator typed the code the AGENT shows. A wrong one counts; the third cancels the pairing. */
92
+ confirm(pairingId: string, code: string): Promise<HubPairingView>;
93
+ reject(pairingId: string): Promise<HubPairingView>;
94
+ get(pairingId: string): HubPairingView | undefined;
95
+ list(): readonly HubPairingView[];
96
+ /** Active pairings, for the poller. */
97
+ activeIds(): readonly string[];
98
+ /**
99
+ * Move a pairing on: notice an expiry, a cancellation or the agent's
100
+ * confirmation, and complete when both sides confirmed. Never throws.
101
+ */
102
+ advance(pairingId: string): Promise<HubPairingView>;
103
+ private complete;
104
+ /** The new leaf is installed: older leaves of the node go, the new one stays. */
105
+ private installed;
106
+ /** After a failed completion call: did the agent install the certificate after all? */
107
+ private installOutcome;
108
+ private assertNodeIdUsable;
109
+ private expireIfDue;
110
+ private finish;
111
+ private cancel;
112
+ private require;
113
+ private remember;
114
+ private view;
115
+ }
@@ -0,0 +1,3 @@
1
+ export { AgentPairing, hubHostFromMeshAddress, type AgentHello, type AgentPairingDeps, type AgentPairingLocalView, type AgentPairingPoll, type AgentPairingPublicView, type LocalPairingSecret, type PairingRequestContext, type PendingPairingPublic, type InstalledPairing, type OfferAnswer, type OfferRequest, type PairingFailure, type PairingOutcome, type PairingResult } from "./agent-pairing.js";
2
+ export { HubPairing, PairingHttpError, type AgentEndpoint, type AgentPairingTransport, type HubPairingDeps, type HubPairingState, type HubPairingView } from "./hub-pairing.js";
3
+ export { computePairingCommitment, computePairingSas, describeCsr, keyFingerprintShort, PAIRING_CODE_DIGITS, isValidAgentNodeId, PAIRING_MAX_CODE_ATTEMPTS, PAIRING_MAX_SESSIONS, PAIRING_PIN_DIGITS, PAIRING_PATHS, PAIRING_PROTOCOL, PAIRING_TTL_MS, signPairingProof, verifyPairingProof, type AgentPairingState, type DescribedCsr, type PairingProofClaims, type PairingSasInput } from "./pairing-protocol.js";
@@ -0,0 +1,107 @@
1
+ import { isValidAgentNodeId } from "../mesh-ca.js";
2
+ export { isValidAgentNodeId };
3
+ export declare const PAIRING_PROTOCOL: 1;
4
+ export declare const PAIRING_DOMAIN = "camstack-pairing/v1";
5
+ export declare const PAIRING_PROOF_TYP = "camstack-pairing-complete+jwt";
6
+ export declare const PAIRING_PROOF_USE = "pairing-complete";
7
+ export declare const PAIRING_OFFER_TYP = "camstack-pairing-offer+jwt";
8
+ export declare const PAIRING_OFFER_USE = "pairing-offer";
9
+ /** An offer signature older than this (or from further ahead than 60 s) is refused. */
10
+ export declare const PAIRING_OFFER_MAX_AGE_MS: number;
11
+ /** A pairing that is not confirmed on both sides within this window is cancelled on both. */
12
+ export declare const PAIRING_TTL_MS: number;
13
+ /** Wrong codes typed before the pairing is cancelled. */
14
+ export declare const PAIRING_MAX_CODE_ATTEMPTS = 3;
15
+ export declare const PAIRING_CODE_DIGITS = 6;
16
+ /** Pending pairings an agent holds at once, one per hub CA (offer squatting, D857). */
17
+ export declare const PAIRING_MAX_SESSIONS = 4;
18
+ /** The local PIN: single use, shown only on the agent's own machine. */
19
+ export declare const PAIRING_PIN_DIGITS = 8;
20
+ /** A pairing proof is valid this long from `iat`, never longer. */
21
+ export declare const PAIRING_PROOF_MAX_LIFETIME_MS: number;
22
+ /** Agent endpoints, all under the agent API (`:4444`). */
23
+ export declare const PAIRING_PATHS: {
24
+ readonly hello: "/api/agent/pairing/hello";
25
+ readonly offer: "/api/agent/pairing/offer";
26
+ readonly reveal: "/api/agent/pairing/reveal";
27
+ /** Public: who asks to pair - never a code, a pairing id or a PIN. */
28
+ readonly status: "/api/agent/pairing/status";
29
+ /** The hub's poll, authorized by the pairing id: did the agent's operator confirm? */
30
+ readonly poll: "/api/agent/pairing/poll";
31
+ readonly complete: "/api/agent/pairing/complete";
32
+ /** The agent's operator: the hub's code + the local PIN (or genuine loopback). */
33
+ readonly confirm: "/api/agent/pairing/confirm";
34
+ readonly reject: "/api/agent/pairing/reject";
35
+ /** The hub withdraws its pairing, authorized by the pairing id. */
36
+ readonly cancel: "/api/agent/pairing/cancel";
37
+ /** The agent's operator clears lockouts and pending offers (loopback, or the local reset code). */
38
+ readonly reset: "/api/agent/pairing/reset";
39
+ readonly forget: "/api/agent/pairing/forget";
40
+ };
41
+ export type AgentPairingState = "unpaired" | "pending" | "paired";
42
+ /** A fresh single-use local PIN: digits only, uniformly random. */
43
+ export declare function randomPin(): string;
44
+ export declare function randomHex(bytes: number): string;
45
+ export declare function newPairingId(): string;
46
+ /** The hub's commitment to its nonce, bound to the CA certificate it offers. */
47
+ export declare function computePairingCommitment(caCertPem: string, hubNonce: string): string;
48
+ export declare function commitmentsEqual(expected: string, presented: unknown): boolean;
49
+ export interface PairingSasInput {
50
+ readonly caCertPem: string;
51
+ /** SPKI DER of the agent's mesh public key, base64. */
52
+ readonly agentPublicKeySpki: string;
53
+ readonly hubNonce: string;
54
+ readonly agentNonce: string;
55
+ }
56
+ /** The six digits both screens show. */
57
+ export declare function computePairingSas(input: PairingSasInput): string;
58
+ export declare function constantTimeEqual(expected: string, presented: unknown): boolean;
59
+ /** What the hub learns from an agent's CSR, after checking the CSR's own signature. */
60
+ export interface DescribedCsr {
61
+ readonly commonName: string;
62
+ /** SPKI DER, base64 — what the SAS binds. */
63
+ readonly publicKeySpki: string;
64
+ }
65
+ export declare function describeCsr(csrPem: string): Promise<DescribedCsr>;
66
+ /** The transcript the CA signs when the hub completes a pairing. */
67
+ export interface PairingProofClaims {
68
+ readonly v: typeof PAIRING_PROTOCOL;
69
+ readonly use: typeof PAIRING_PROOF_USE;
70
+ readonly pairingId: string;
71
+ readonly nodeId: string;
72
+ readonly hubNonce: string;
73
+ readonly agentNonce: string;
74
+ /** SHA-256 hex of the leaf DER. */
75
+ readonly leafFingerprint: string;
76
+ readonly caFingerprint: string;
77
+ readonly hubMeshAddress: string;
78
+ readonly hubUrl: string;
79
+ /** Epoch ms. */
80
+ readonly iat: number;
81
+ /** Epoch ms; at most {@link PAIRING_PROOF_MAX_LIFETIME_MS} after `iat`. */
82
+ readonly exp: number;
83
+ }
84
+ export declare function leafFingerprintCompact(certPem: string): string;
85
+ export declare function signPairingProof(caKeyPem: string, claims: PairingProofClaims): string;
86
+ /**
87
+ * The claims of `proof` when it was signed by `caCertPem`'s key, else `null`.
88
+ * Shape, expiry and the binding to a session are the caller's to check.
89
+ */
90
+ export declare function verifyPairingProof(caCertPem: string, proof: string): PairingProofClaims | null;
91
+ /** A short public fingerprint of a public key, for the mDNS TXT record. */
92
+ export declare function keyFingerprintShort(publicKeySpkiB64: string): string;
93
+ /** What a hub signs with its CA key to make an offer: nobody holding only the public CA can. */
94
+ export interface PairingOfferClaims {
95
+ readonly use: typeof PAIRING_OFFER_USE;
96
+ /** The agent the offer is for - an offer cannot be replayed to another agent. */
97
+ readonly agentNodeId: string;
98
+ readonly hubNodeId: string;
99
+ readonly caFingerprint: string;
100
+ readonly commitment: string;
101
+ readonly hubUrl: string;
102
+ readonly hubMeshAddress: string;
103
+ readonly iat: number;
104
+ }
105
+ export declare function signPairingOffer(caKeyPem: string, claims: PairingOfferClaims): string;
106
+ /** The claims of `token` when `caCertPem`'s key signed it as a pairing offer, else `null`. */
107
+ export declare function verifyPairingOffer(caCertPem: string, token: string): PairingOfferClaims | null;
@@ -113,6 +113,12 @@ export interface FilteredGossip {
113
113
  * remove the payload is returned unchanged.
114
114
  */
115
115
  export declare function keepOnlyGossipNodes(payload: unknown, keep: ReadonlySet<string>): FilteredGossip;
116
+ /** A mesh peer whose certificate identity was verified at the handshake (D857). */
117
+ export interface VerifiedMeshPeer {
118
+ /** The leaf CN, which is the node id. */
119
+ readonly nodeId: string;
120
+ readonly serial: string;
121
+ }
116
122
  /** What a peer's leaf looks like, recorded at handshake. */
117
123
  interface PeerLeaf {
118
124
  readonly serial: string;
@@ -177,6 +183,18 @@ export declare class TlsTcpTransporter extends TcpTransporterBase {
177
183
  */
178
184
  peerTransport(nodeId: string): "tls" | "clear";
179
185
  /**
186
+ * The certificate identity of `nodeId` (D857): its mutual-TLS handshake chained to the
187
+ * mesh CA this transporter was built with, its leaf CN is `nodeId`, and the leaf is not
188
+ * revoked now. This is the ONLY proof of "who is calling" the mesh gates accept:
189
+ * every inbound packet on a TLS socket is dropped unless its `sender` equals the leaf CN
190
+ * (see `onIncomingMessage`), so a node id in a delivered packet that is verified here was
191
+ * claimed by the holder of that node's private key.
192
+ *
193
+ * `undefined` for a node that reached us over clear text, never completed a handshake,
194
+ * or whose serial is on the CRL.
195
+ */
196
+ verifiedPeer(nodeId: string): VerifiedMeshPeer | undefined;
197
+ /**
180
198
  * The node disconnected: its next connection decides its transport again —
181
199
  * unless it already re-handshook (the disconnect can be noticed after the
182
200
  * restarted node is back), in which case its live TLS socket keeps it `tls`.
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, r as __exportAll, t as __commonJSMin } from "./chunk-CNf5ZN-e.mjs";
2
- import { h as tslib_es6_exports, m as init_tslib_es6 } from "./x509.es-CAjSsrkk.mjs";
2
+ import { h as tslib_es6_exports, m as init_tslib_es6 } from "./x509.es-CDvj67p1.mjs";
3
3
  import { t as require_src } from "./src-hG4grMU4.mjs";
4
4
  //#region ../../node_modules/source-map/lib/base64.js
5
5
  var require_base64 = /* @__PURE__ */ __commonJSMin(((exports) => {
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_x509_es = require("./x509.es-Dzl0BbmG.js");
2
+ const require_x509_es = require("./x509.es-r7Ht1thA.js");
3
3
  const require_src$1 = require("./src-CnAqG6EF.js");
4
4
  //#region ../../node_modules/source-map/lib/base64.js
5
5
  var require_base64 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
@@ -1,4 +1,4 @@
1
- import { g as DATABASE_SNAPSHOT_SUFFIX } from "./dist-Bt8GTRHB.mjs";
1
+ import { v as DATABASE_SNAPSHOT_SUFFIX } from "./dist-CYcBqnXk.mjs";
2
2
  import { createCipheriv, createDecipheriv, createHash, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-BCKOeWu_.js");
2
+ const require_dist = require("./dist-CCH3XPqO.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let buffer = require("buffer");
5
5
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BCKOeWu_.js");
1
+ const require_dist = require("./dist-CCH3XPqO.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- import { Dr as asJsonObject } from "./dist-Bt8GTRHB.mjs";
1
+ import { Ar as asJsonObject } from "./dist-CYcBqnXk.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -17,6 +17,8 @@ export declare class AgentHttpsNames {
17
17
  private readonly byNode;
18
18
  constructor(store: SettingsStoreClient);
19
19
  load(): Promise<void>;
20
+ /** The names certified to `nodeId` (its id and the address of its live mesh socket), or `null`. */
21
+ namesOf(nodeId: string): readonly string[] | null;
20
22
  /** Every name certified to a node other than `nodeId`. */
21
23
  takenByOthers(nodeId: string): ReadonlySet<string>;
22
24
  /** Free every name certified to a node that was forgotten. */
@@ -0,0 +1,10 @@
1
+ /**
2
+ * How far a freshly issued certificate's `notBefore` is moved into the past.
3
+ *
4
+ * TLS refuses a certificate whose `notBefore` is in the verifier's future, and X.509 dates have
5
+ * one-second resolution, so a certificate dated "now" fails the handshake on any peer whose clock
6
+ * runs even slightly behind the issuer's. Expiry is always computed from the real `now`; only the
7
+ * start of validity moves.
8
+ */
9
+ export declare const CERT_NOT_BEFORE_BACKDATE_MS: number;
10
+ export declare function backdatedNotBefore(now: Date): Date;
@@ -1,23 +1,3 @@
1
- /**
2
- * Certificate issuance. Isolated so `cert-manager.ts` reads as policy and
3
- * this file reads as mechanics.
4
- *
5
- * **In-process, by decision (D551).** This used to shell out to `openssl`
6
- * three times. The hub's own TLS identity is not allowed to be a function
7
- * of which openssl happens to sit on the host: this fleet alone runs four
8
- * of them, and one — Ubuntu 20.04's 1.1.1f — silently ADDED the ambient
9
- * `/etc/ssl/openssl.cnf` `[req] x509_extensions = v3_ca` block on top of
10
- * our `-addext` flags, producing a root with two `basicConstraints` and
11
- * two `subjectKeyIdentifier` extensions. RFC 5280 §4.2 allows each at
12
- * most once, so OpenSSL 3 (which is what `node:crypto` is) marked that
13
- * root invalid: `X509Certificate.ca` read `false` on our own CA and
14
- * `checkIssued()` refused the pair. Under D227 that means "not issued by
15
- * the local CA", i.e. rotate the root — on every boot, forever, voiding
16
- * every trust an operator had granted by hand.
17
- *
18
- * Keys come from WebCrypto (`node:crypto`'s own implementation) and the
19
- * DER is assembled by `@peculiar/x509`. Nothing here reads the host.
20
- */
21
1
  import type { webcrypto as WebCrypto } from "node:crypto";
22
2
  import { X509Certificate as PeculiarCertificate } from "@peculiar/x509";
23
3
  import type { CertIdentity } from "./cert-identity.js";
@@ -1,4 +1,17 @@
1
1
  import { n as __esmMin, r as __exportAll, t as __commonJSMin } from "./chunk-CNf5ZN-e.mjs";
2
+ //#region src/tls/cert-backdate.ts
3
+ /**
4
+ * How far a freshly issued certificate's `notBefore` is moved into the past.
5
+ *
6
+ * TLS refuses a certificate whose `notBefore` is in the verifier's future, and X.509 dates have
7
+ * one-second resolution, so a certificate dated "now" fails the handshake on any peer whose clock
8
+ * runs even slightly behind the issuer's. Expiry is always computed from the real `now`; only the
9
+ * start of validity moves.
10
+ */
11
+ var CERT_NOT_BEFORE_BACKDATE_MS = 5 * 6e4;
12
+ function backdatedNotBefore(now) {
13
+ return /* @__PURE__ */ new Date(now.getTime() - CERT_NOT_BEFORE_BACKDATE_MS);
14
+ }
2
15
  /*! *****************************************************************************
3
16
  Copyright (C) Microsoft. All rights reserved.
4
17
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use
@@ -11900,4 +11913,4 @@ AsnEcSignatureFormatter.namedCurveSize.set("K-256", 32);
11900
11913
  AsnEcSignatureFormatter.namedCurveSize.set("P-384", 48);
11901
11914
  AsnEcSignatureFormatter.namedCurveSize.set("P-521", 66);
11902
11915
  //#endregion
11903
- export { KeyUsagesExtension as a, Pkcs10CertificateRequestGenerator as c, X509Certificate as d, X509CertificateGenerator as f, tslib_es6_exports as h, KeyUsageFlags as i, SubjectAlternativeNameExtension as l, init_tslib_es6 as m, BasicConstraintsExtension as n, PemConverter as o, cryptoProvider as p, ExtendedKeyUsageExtension as r, Pkcs10CertificateRequest as s, AuthorityKeyIdentifierExtension as t, SubjectKeyIdentifierExtension as u };
11916
+ export { KeyUsagesExtension as a, Pkcs10CertificateRequestGenerator as c, X509Certificate as d, X509CertificateGenerator as f, backdatedNotBefore as g, tslib_es6_exports as h, KeyUsageFlags as i, SubjectAlternativeNameExtension as l, init_tslib_es6 as m, BasicConstraintsExtension as n, PemConverter as o, cryptoProvider as p, ExtendedKeyUsageExtension as r, Pkcs10CertificateRequest as s, AuthorityKeyIdentifierExtension as t, SubjectKeyIdentifierExtension as u };
@@ -1,4 +1,17 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
+ //#region src/tls/cert-backdate.ts
3
+ /**
4
+ * How far a freshly issued certificate's `notBefore` is moved into the past.
5
+ *
6
+ * TLS refuses a certificate whose `notBefore` is in the verifier's future, and X.509 dates have
7
+ * one-second resolution, so a certificate dated "now" fails the handshake on any peer whose clock
8
+ * runs even slightly behind the issuer's. Expiry is always computed from the real `now`; only the
9
+ * start of validity moves.
10
+ */
11
+ var CERT_NOT_BEFORE_BACKDATE_MS = 5 * 6e4;
12
+ function backdatedNotBefore(now) {
13
+ return /* @__PURE__ */ new Date(now.getTime() - CERT_NOT_BEFORE_BACKDATE_MS);
14
+ }
2
15
  /*! *****************************************************************************
3
16
  Copyright (C) Microsoft. All rights reserved.
4
17
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use
@@ -11972,6 +11985,12 @@ Object.defineProperty(exports, "X509CertificateGenerator", {
11972
11985
  return X509CertificateGenerator;
11973
11986
  }
11974
11987
  });
11988
+ Object.defineProperty(exports, "backdatedNotBefore", {
11989
+ enumerable: true,
11990
+ get: function() {
11991
+ return backdatedNotBefore;
11992
+ }
11993
+ });
11975
11994
  Object.defineProperty(exports, "cryptoProvider", {
11976
11995
  enumerable: true,
11977
11996
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.432",
3
+ "version": "1.2.434",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",