instar 1.3.514 → 1.3.516
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +172 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +11 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/HybridLogicalClock.d.ts +200 -0
- package/dist/core/HybridLogicalClock.d.ts.map +1 -0
- package/dist/core/HybridLogicalClock.js +288 -0
- package/dist/core/HybridLogicalClock.js.map +1 -0
- package/dist/core/MeshRpc.d.ts +6 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +8 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/PoolLinkAssertion.d.ts +164 -0
- package/dist/core/PoolLinkAssertion.d.ts.map +1 -0
- package/dist/core/PoolLinkAssertion.js +184 -0
- package/dist/core/PoolLinkAssertion.js.map +1 -0
- package/dist/core/PoolLinkJtiStore.d.ts +48 -0
- package/dist/core/PoolLinkJtiStore.d.ts.map +1 -0
- package/dist/core/PoolLinkJtiStore.js +149 -0
- package/dist/core/PoolLinkJtiStore.js.map +1 -0
- package/dist/core/PoolViewProxy.d.ts +168 -0
- package/dist/core/PoolViewProxy.d.ts.map +1 -0
- package/dist/core/PoolViewProxy.js +208 -0
- package/dist/core/PoolViewProxy.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +19 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +84 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +45 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +38 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +109 -1
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.515.md +64 -0
- package/upgrades/1.3.516.md +55 -0
- package/upgrades/side-effects/multi-machine-seamlessness-ws44-pool-links.md +146 -0
- package/upgrades/side-effects/replicated-store-foundation-hlc-primitive.md +98 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshRpc.d.ts","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"MeshRpc.d.ts","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"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"}
|
package/dist/core/MeshRpc.js
CHANGED
|
@@ -106,6 +106,7 @@ export function checkCommandRBAC(command, sender, deps) {
|
|
|
106
106
|
case 'topic-profile-pull':
|
|
107
107
|
case 'commitments-sync':
|
|
108
108
|
case 'preferences-sync':
|
|
109
|
+
case 'pool-view-fetch':
|
|
109
110
|
case 'secret-share':
|
|
110
111
|
// Read/observe class (or e2e-encrypted) — any registered peer (already
|
|
111
112
|
// proven a registered peer by verifyEnvelope). journal-sync joins this
|
|
@@ -120,6 +121,13 @@ export function checkCommandRBAC(command, sender, deps) {
|
|
|
120
121
|
// per-topic profile entries — disclosure-only for registered
|
|
121
122
|
// same-operator peers; the receiver revalidates every field (§10.2)
|
|
122
123
|
// before anything persists, so the verb carries reach, not authority.
|
|
124
|
+
// pool-view-fetch joins it (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4): the
|
|
125
|
+
// verb is reachable by any registered peer, but its HANDLER is the real
|
|
126
|
+
// gate — it serves a private view body ONLY when the carried user-auth
|
|
127
|
+
// ASSERTION verifies (audience-bound to this holder + this view + this
|
|
128
|
+
// method, single-use, signed by the authenticated sender) AND the
|
|
129
|
+
// holder's own per-view authorization passes. The probeOnly shape
|
|
130
|
+
// discloses only existence (not the body) to a registered peer.
|
|
123
131
|
return { ok: true, reason: 'ok' };
|
|
124
132
|
default:
|
|
125
133
|
return { ok: false, reason: 'claim-unauthorized' };
|
package/dist/core/MeshRpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshRpc.js","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"MeshRpc.js","sourceRoot":"","sources":["../../src/core/MeshRpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoolLinkAssertion — the cross-machine user-authentication assertion for WS4.4
|
|
3
|
+
* (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 "Links that survive machine
|
|
4
|
+
* boundaries"). SECURITY-SENSITIVE.
|
|
5
|
+
*
|
|
6
|
+
* When a tunnel-fronting machine proxies a `/view/:id` (or dashboard asset)
|
|
7
|
+
* request to the machine that actually HOLDS the content, it must tell the
|
|
8
|
+
* holder "I, the fronting machine, validated the END USER's credential (their
|
|
9
|
+
* PIN session / view token)" — WITHOUT forwarding the raw credential. Each
|
|
10
|
+
* machine's PIN secret and authToken NEVER cross the boundary (spec §WS4.4 b/e):
|
|
11
|
+
* the holder makes its own authorization decision, but it needs to trust that
|
|
12
|
+
* the user really authenticated at the edge.
|
|
13
|
+
*
|
|
14
|
+
* This module is PURE LOGIC — the canonical assertion bytes, the construction,
|
|
15
|
+
* and the verification gate. Crypto (Ed25519 sign/verify, reusing
|
|
16
|
+
* MachineIdentity.sign/verify), the single-use jti store, the peer-key lookup,
|
|
17
|
+
* and the clock are injected as seams so the dangerous parts (audience-binding,
|
|
18
|
+
* replay, expiry) are unit-testable with in-memory fakes. The HTTP transport +
|
|
19
|
+
* production wiring sit on top and call these functions.
|
|
20
|
+
*
|
|
21
|
+
* Three independent properties protect every assertion (spec §WS4.4 e):
|
|
22
|
+
* (1) AUDIENCE-BOUND — the signed bytes name the EXPECTED holder fingerprint,
|
|
23
|
+
* the SPECIFIC view id, and the HTTP method. A capture cannot be replayed
|
|
24
|
+
* against another resource, another holder, or another verb.
|
|
25
|
+
* (2) SIGNED by the fronting machine's mesh key — the holder verifies the
|
|
26
|
+
* signature against the EXPECTED fronting machine's REGISTERED public key,
|
|
27
|
+
* so only a registered peer can mint a valid assertion.
|
|
28
|
+
* (3) SINGLE-USE within a short TTL — the jti is recorded by the holder on
|
|
29
|
+
* first accept; a replay inside the window is rejected, and the TTL bounds
|
|
30
|
+
* the window beyond which the assertion is stale regardless.
|
|
31
|
+
*
|
|
32
|
+
* The fronting machine is a DUMB RELAY: it validates the user credential locally
|
|
33
|
+
* (its own concern), mints THIS assertion, and forwards it. It substitutes NO
|
|
34
|
+
* machine/mesh credential for the user credential at the holder — the assertion
|
|
35
|
+
* only attests "a user authenticated at the edge for this exact resource", and
|
|
36
|
+
* the holder still applies its own per-view authorization on top.
|
|
37
|
+
*/
|
|
38
|
+
export type MachineFingerprint = string;
|
|
39
|
+
/**
|
|
40
|
+
* The audience-binding triple — the exact (holder, resource, method) this
|
|
41
|
+
* assertion is valid for. Any mismatch at the holder is a rejection.
|
|
42
|
+
*/
|
|
43
|
+
export interface PoolLinkAudience {
|
|
44
|
+
/** The machine fingerprint (machineId) of the EXPECTED holder of the view. */
|
|
45
|
+
holderFingerprint: MachineFingerprint;
|
|
46
|
+
/** The specific view id this assertion authorizes (no wildcard). */
|
|
47
|
+
viewId: string;
|
|
48
|
+
/** The HTTP method this assertion authorizes (e.g. 'GET'). Upper-cased. */
|
|
49
|
+
method: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The assertion payload. `iss` (the fronting machine) is included in the signed
|
|
53
|
+
* bytes AND used by the holder to pick which registered public key to verify
|
|
54
|
+
* against — so an assertion signed by machine X cannot claim to come from Y.
|
|
55
|
+
*/
|
|
56
|
+
export interface PoolLinkAssertion {
|
|
57
|
+
/** Issuer — the fronting machine fingerprint that signed this. */
|
|
58
|
+
iss: MachineFingerprint;
|
|
59
|
+
/** Audience — the (holder, view, method) this is bound to. */
|
|
60
|
+
aud: PoolLinkAudience;
|
|
61
|
+
/** Single-use id (replay control). Recorded by the holder on first accept. */
|
|
62
|
+
jti: string;
|
|
63
|
+
/** Issued-at, ms epoch. */
|
|
64
|
+
iat: number;
|
|
65
|
+
/** Expiry, ms epoch. The holder rejects at/after this. */
|
|
66
|
+
exp: number;
|
|
67
|
+
/**
|
|
68
|
+
* The kind of end-user credential the fronting machine validated. NOT the
|
|
69
|
+
* credential itself — the raw PIN / view token NEVER appears here (spec
|
|
70
|
+
* §WS4.4 b). Purely informational for the holder's audit trail.
|
|
71
|
+
*/
|
|
72
|
+
userAuth: 'pin-session' | 'view-sig' | 'view-pin';
|
|
73
|
+
/** Ed25519 signature (base64) over the canonical bytes. */
|
|
74
|
+
signature: string;
|
|
75
|
+
}
|
|
76
|
+
/** The canonical bytes the signature covers — field-ordered, audience included.
|
|
77
|
+
* Excludes `signature` itself. Stable ordering so sign/verify agree. */
|
|
78
|
+
export declare function canonicalizePoolLinkAssertion(a: Omit<PoolLinkAssertion, 'signature'>): string;
|
|
79
|
+
/** Default assertion lifetime (ms). Short by design — the proxy hop is sub-second. */
|
|
80
|
+
export declare const DEFAULT_POOL_LINK_TTL_MS = 30000;
|
|
81
|
+
/**
|
|
82
|
+
* Hard ceiling (ms) on the TTL SPAN (`exp - iat`) a holder will honor, regardless
|
|
83
|
+
* of what the issuer minted. A misbehaving (but registered) peer could otherwise
|
|
84
|
+
* mint a far-future `exp` and pin its jti in the holder's single-use store for
|
|
85
|
+
* that whole span — an unbounded-retention DoS. The holder rejects any span over
|
|
86
|
+
* this ceiling as `ttl-too-long` BEFORE the jti is ever recorded. 5 min is far
|
|
87
|
+
* above the sub-second proxy hop + the default 30s mint, so it never rejects a
|
|
88
|
+
* legitimate assertion. (P19 — bounded by construction.)
|
|
89
|
+
*/
|
|
90
|
+
export declare const DEFAULT_POOL_LINK_MAX_TTL_MS: number;
|
|
91
|
+
export interface MintPoolLinkAssertionDeps {
|
|
92
|
+
/** This (fronting) machine's fingerprint — becomes `iss`. */
|
|
93
|
+
selfFingerprint: MachineFingerprint;
|
|
94
|
+
/** Sign the canonical bytes with THIS machine's Ed25519 signing key. */
|
|
95
|
+
sign: (canonical: string) => string;
|
|
96
|
+
/** Mint a fresh, unguessable jti (crypto-random in prod; deterministic in tests). */
|
|
97
|
+
mintJti: () => string;
|
|
98
|
+
now: () => number;
|
|
99
|
+
/** Assertion lifetime (ms). Default DEFAULT_POOL_LINK_TTL_MS. */
|
|
100
|
+
ttlMs?: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build + sign a pool-link assertion naming THIS fronting machine as issuer and
|
|
104
|
+
* binding it to the (holder, view, method) audience. The method is upper-cased
|
|
105
|
+
* so the audience-binding is case-insensitive on the verb (HTTP methods are
|
|
106
|
+
* case-insensitive by spec, but we canonicalize to avoid an accidental mismatch).
|
|
107
|
+
*/
|
|
108
|
+
export declare function mintPoolLinkAssertion(audience: {
|
|
109
|
+
holderFingerprint: MachineFingerprint;
|
|
110
|
+
viewId: string;
|
|
111
|
+
method: string;
|
|
112
|
+
}, userAuth: PoolLinkAssertion['userAuth'], deps: MintPoolLinkAssertionDeps): PoolLinkAssertion;
|
|
113
|
+
export type PoolLinkVerifyReason = 'ok' | 'malformed' | 'wrong-holder' | 'wrong-view' | 'wrong-method' | 'unexpected-issuer' | 'unknown-issuer' | 'signature-invalid' | 'expired' | 'not-yet-valid' | 'ttl-too-long' | 'replayed';
|
|
114
|
+
export interface VerifyPoolLinkAssertionDeps {
|
|
115
|
+
/** THIS (holder) machine's fingerprint — `aud.holderFingerprint` MUST equal it. */
|
|
116
|
+
selfFingerprint: MachineFingerprint;
|
|
117
|
+
/**
|
|
118
|
+
* The fronting machine the holder EXPECTS this assertion to come from. The
|
|
119
|
+
* fronting machine identity is proven independently by the mesh transport
|
|
120
|
+
* (the machine-authed request carries the sender id); the holder passes that
|
|
121
|
+
* authenticated id here so `iss` must match it — a captured assertion cannot
|
|
122
|
+
* be replayed by a DIFFERENT machine. Pass null to skip (not recommended).
|
|
123
|
+
*/
|
|
124
|
+
expectedIssuer: MachineFingerprint | null;
|
|
125
|
+
/** Resolve `iss`'s REGISTERED Ed25519 public key (PEM), or null if unknown. */
|
|
126
|
+
resolveIssuerPublicKeyPem: (iss: MachineFingerprint) => string | null;
|
|
127
|
+
/** Verify the Ed25519 signature over `canonical` against `publicKeyPem`. */
|
|
128
|
+
verify: (canonical: string, signature: string, publicKeyPem: string) => boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Has this jti been seen (replay guard)? Single-use store, persisted on the
|
|
131
|
+
* holder. Returns true if already recorded.
|
|
132
|
+
*/
|
|
133
|
+
seenJti: (jti: string) => boolean;
|
|
134
|
+
now: () => number;
|
|
135
|
+
/** Tolerance for a slightly-future iat (clock skew), ms. Default 5000. */
|
|
136
|
+
clockSkewToleranceMs?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Hard ceiling on the accepted TTL span (`exp - iat`), ms. An assertion whose
|
|
139
|
+
* span exceeds this is rejected as `ttl-too-long` before the jti is recorded —
|
|
140
|
+
* so a misbehaving peer cannot pin a far-future jti in the holder's store.
|
|
141
|
+
* Default DEFAULT_POOL_LINK_MAX_TTL_MS (5 min).
|
|
142
|
+
*/
|
|
143
|
+
maxTtlMs?: number;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Verify an assertion at the HOLDER, evaluated IN ORDER (spec §WS4.4 e). PURE:
|
|
147
|
+
* it does NOT record the jti — the caller records it ONLY on a fully-accepted
|
|
148
|
+
* assertion (so a rejected one never burns a jti, and a replay is caught by the
|
|
149
|
+
* EXPECTED `requested` audience matching the stored jti). The `requested`
|
|
150
|
+
* argument is the audience derived from the ACTUAL incoming request (this
|
|
151
|
+
* machine's own fingerprint, the view id in the URL, the request method) — the
|
|
152
|
+
* assertion's `aud` must match it exactly, which is what makes the binding real:
|
|
153
|
+
* a captured assertion presented for a different view/method is rejected.
|
|
154
|
+
*/
|
|
155
|
+
export declare function verifyPoolLinkAssertion(assertion: PoolLinkAssertion, requested: {
|
|
156
|
+
viewId: string;
|
|
157
|
+
method: string;
|
|
158
|
+
}, deps: VerifyPoolLinkAssertionDeps): {
|
|
159
|
+
ok: boolean;
|
|
160
|
+
reason: PoolLinkVerifyReason;
|
|
161
|
+
};
|
|
162
|
+
/** HTTP status for each verify reason — auth failures 401/403, freshness/replay 409. */
|
|
163
|
+
export declare function statusForPoolLinkReason(reason: PoolLinkVerifyReason): number;
|
|
164
|
+
//# sourceMappingURL=PoolLinkAssertion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoolLinkAssertion.d.ts","sourceRoot":"","sources":["../../src/core/PoolLinkAssertion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoolLinkAssertion — the cross-machine user-authentication assertion for WS4.4
|
|
3
|
+
* (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 "Links that survive machine
|
|
4
|
+
* boundaries"). SECURITY-SENSITIVE.
|
|
5
|
+
*
|
|
6
|
+
* When a tunnel-fronting machine proxies a `/view/:id` (or dashboard asset)
|
|
7
|
+
* request to the machine that actually HOLDS the content, it must tell the
|
|
8
|
+
* holder "I, the fronting machine, validated the END USER's credential (their
|
|
9
|
+
* PIN session / view token)" — WITHOUT forwarding the raw credential. Each
|
|
10
|
+
* machine's PIN secret and authToken NEVER cross the boundary (spec §WS4.4 b/e):
|
|
11
|
+
* the holder makes its own authorization decision, but it needs to trust that
|
|
12
|
+
* the user really authenticated at the edge.
|
|
13
|
+
*
|
|
14
|
+
* This module is PURE LOGIC — the canonical assertion bytes, the construction,
|
|
15
|
+
* and the verification gate. Crypto (Ed25519 sign/verify, reusing
|
|
16
|
+
* MachineIdentity.sign/verify), the single-use jti store, the peer-key lookup,
|
|
17
|
+
* and the clock are injected as seams so the dangerous parts (audience-binding,
|
|
18
|
+
* replay, expiry) are unit-testable with in-memory fakes. The HTTP transport +
|
|
19
|
+
* production wiring sit on top and call these functions.
|
|
20
|
+
*
|
|
21
|
+
* Three independent properties protect every assertion (spec §WS4.4 e):
|
|
22
|
+
* (1) AUDIENCE-BOUND — the signed bytes name the EXPECTED holder fingerprint,
|
|
23
|
+
* the SPECIFIC view id, and the HTTP method. A capture cannot be replayed
|
|
24
|
+
* against another resource, another holder, or another verb.
|
|
25
|
+
* (2) SIGNED by the fronting machine's mesh key — the holder verifies the
|
|
26
|
+
* signature against the EXPECTED fronting machine's REGISTERED public key,
|
|
27
|
+
* so only a registered peer can mint a valid assertion.
|
|
28
|
+
* (3) SINGLE-USE within a short TTL — the jti is recorded by the holder on
|
|
29
|
+
* first accept; a replay inside the window is rejected, and the TTL bounds
|
|
30
|
+
* the window beyond which the assertion is stale regardless.
|
|
31
|
+
*
|
|
32
|
+
* The fronting machine is a DUMB RELAY: it validates the user credential locally
|
|
33
|
+
* (its own concern), mints THIS assertion, and forwards it. It substitutes NO
|
|
34
|
+
* machine/mesh credential for the user credential at the holder — the assertion
|
|
35
|
+
* only attests "a user authenticated at the edge for this exact resource", and
|
|
36
|
+
* the holder still applies its own per-view authorization on top.
|
|
37
|
+
*/
|
|
38
|
+
/** The canonical bytes the signature covers — field-ordered, audience included.
|
|
39
|
+
* Excludes `signature` itself. Stable ordering so sign/verify agree. */
|
|
40
|
+
export function canonicalizePoolLinkAssertion(a) {
|
|
41
|
+
return JSON.stringify([
|
|
42
|
+
a.iss,
|
|
43
|
+
a.aud.holderFingerprint,
|
|
44
|
+
a.aud.viewId,
|
|
45
|
+
a.aud.method,
|
|
46
|
+
a.jti,
|
|
47
|
+
a.iat,
|
|
48
|
+
a.exp,
|
|
49
|
+
a.userAuth,
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
/** Default assertion lifetime (ms). Short by design — the proxy hop is sub-second. */
|
|
53
|
+
export const DEFAULT_POOL_LINK_TTL_MS = 30_000;
|
|
54
|
+
/**
|
|
55
|
+
* Hard ceiling (ms) on the TTL SPAN (`exp - iat`) a holder will honor, regardless
|
|
56
|
+
* of what the issuer minted. A misbehaving (but registered) peer could otherwise
|
|
57
|
+
* mint a far-future `exp` and pin its jti in the holder's single-use store for
|
|
58
|
+
* that whole span — an unbounded-retention DoS. The holder rejects any span over
|
|
59
|
+
* this ceiling as `ttl-too-long` BEFORE the jti is ever recorded. 5 min is far
|
|
60
|
+
* above the sub-second proxy hop + the default 30s mint, so it never rejects a
|
|
61
|
+
* legitimate assertion. (P19 — bounded by construction.)
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_POOL_LINK_MAX_TTL_MS = 5 * 60_000;
|
|
64
|
+
/**
|
|
65
|
+
* Build + sign a pool-link assertion naming THIS fronting machine as issuer and
|
|
66
|
+
* binding it to the (holder, view, method) audience. The method is upper-cased
|
|
67
|
+
* so the audience-binding is case-insensitive on the verb (HTTP methods are
|
|
68
|
+
* case-insensitive by spec, but we canonicalize to avoid an accidental mismatch).
|
|
69
|
+
*/
|
|
70
|
+
export function mintPoolLinkAssertion(audience, userAuth, deps) {
|
|
71
|
+
const iat = deps.now();
|
|
72
|
+
const exp = iat + (deps.ttlMs ?? DEFAULT_POOL_LINK_TTL_MS);
|
|
73
|
+
const aud = {
|
|
74
|
+
holderFingerprint: audience.holderFingerprint,
|
|
75
|
+
viewId: audience.viewId,
|
|
76
|
+
method: audience.method.toUpperCase(),
|
|
77
|
+
};
|
|
78
|
+
const unsigned = {
|
|
79
|
+
iss: deps.selfFingerprint,
|
|
80
|
+
aud,
|
|
81
|
+
jti: deps.mintJti(),
|
|
82
|
+
iat,
|
|
83
|
+
exp,
|
|
84
|
+
userAuth,
|
|
85
|
+
};
|
|
86
|
+
return { ...unsigned, signature: deps.sign(canonicalizePoolLinkAssertion(unsigned)) };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Verify an assertion at the HOLDER, evaluated IN ORDER (spec §WS4.4 e). PURE:
|
|
90
|
+
* it does NOT record the jti — the caller records it ONLY on a fully-accepted
|
|
91
|
+
* assertion (so a rejected one never burns a jti, and a replay is caught by the
|
|
92
|
+
* EXPECTED `requested` audience matching the stored jti). The `requested`
|
|
93
|
+
* argument is the audience derived from the ACTUAL incoming request (this
|
|
94
|
+
* machine's own fingerprint, the view id in the URL, the request method) — the
|
|
95
|
+
* assertion's `aud` must match it exactly, which is what makes the binding real:
|
|
96
|
+
* a captured assertion presented for a different view/method is rejected.
|
|
97
|
+
*/
|
|
98
|
+
export function verifyPoolLinkAssertion(assertion, requested, deps) {
|
|
99
|
+
// (0) shape
|
|
100
|
+
if (!assertion ||
|
|
101
|
+
typeof assertion.iss !== 'string' ||
|
|
102
|
+
!assertion.aud ||
|
|
103
|
+
typeof assertion.aud.holderFingerprint !== 'string' ||
|
|
104
|
+
typeof assertion.aud.viewId !== 'string' ||
|
|
105
|
+
typeof assertion.aud.method !== 'string' ||
|
|
106
|
+
typeof assertion.jti !== 'string' ||
|
|
107
|
+
typeof assertion.iat !== 'number' ||
|
|
108
|
+
typeof assertion.exp !== 'number' ||
|
|
109
|
+
typeof assertion.signature !== 'string') {
|
|
110
|
+
return { ok: false, reason: 'malformed' };
|
|
111
|
+
}
|
|
112
|
+
// (1) audience-bound: holder fingerprint must be THIS machine.
|
|
113
|
+
if (assertion.aud.holderFingerprint !== deps.selfFingerprint) {
|
|
114
|
+
return { ok: false, reason: 'wrong-holder' };
|
|
115
|
+
}
|
|
116
|
+
// (2) audience-bound: the assertion's view must match the REQUESTED view,
|
|
117
|
+
// and both must match the incoming URL — no wildcard, no cross-resource.
|
|
118
|
+
if (assertion.aud.viewId !== requested.viewId) {
|
|
119
|
+
return { ok: false, reason: 'wrong-view' };
|
|
120
|
+
}
|
|
121
|
+
// (3) audience-bound: method match (case-insensitive via canonicalization).
|
|
122
|
+
if (assertion.aud.method !== requested.method.toUpperCase()) {
|
|
123
|
+
return { ok: false, reason: 'wrong-method' };
|
|
124
|
+
}
|
|
125
|
+
// (4) the authenticated transport sender must equal the claimed issuer — a
|
|
126
|
+
// captured assertion cannot be presented by a different machine.
|
|
127
|
+
if (deps.expectedIssuer !== null && assertion.iss !== deps.expectedIssuer) {
|
|
128
|
+
return { ok: false, reason: 'unexpected-issuer' };
|
|
129
|
+
}
|
|
130
|
+
// (5) signature valid for the claimed issuer's REGISTERED key.
|
|
131
|
+
const pem = deps.resolveIssuerPublicKeyPem(assertion.iss);
|
|
132
|
+
if (!pem)
|
|
133
|
+
return { ok: false, reason: 'unknown-issuer' };
|
|
134
|
+
const canonical = canonicalizePoolLinkAssertion({
|
|
135
|
+
iss: assertion.iss,
|
|
136
|
+
aud: assertion.aud,
|
|
137
|
+
jti: assertion.jti,
|
|
138
|
+
iat: assertion.iat,
|
|
139
|
+
exp: assertion.exp,
|
|
140
|
+
userAuth: assertion.userAuth,
|
|
141
|
+
});
|
|
142
|
+
if (!deps.verify(canonical, assertion.signature, pem)) {
|
|
143
|
+
return { ok: false, reason: 'signature-invalid' };
|
|
144
|
+
}
|
|
145
|
+
// (6) freshness: TTL-span ceiling + expiry + not-yet-valid (small skew tolerance).
|
|
146
|
+
const now = deps.now();
|
|
147
|
+
const skew = deps.clockSkewToleranceMs ?? 5000;
|
|
148
|
+
// (6a) span ceiling FIRST — reject an absurd exp regardless of the current
|
|
149
|
+
// clock, so a far-future-exp assertion can never reach the jti store and
|
|
150
|
+
// pin a record there (unbounded-retention DoS). P19 — bounded span.
|
|
151
|
+
const maxTtl = deps.maxTtlMs ?? DEFAULT_POOL_LINK_MAX_TTL_MS;
|
|
152
|
+
if (assertion.exp - assertion.iat > maxTtl)
|
|
153
|
+
return { ok: false, reason: 'ttl-too-long' };
|
|
154
|
+
if (now >= assertion.exp)
|
|
155
|
+
return { ok: false, reason: 'expired' };
|
|
156
|
+
if (assertion.iat - now > skew)
|
|
157
|
+
return { ok: false, reason: 'not-yet-valid' };
|
|
158
|
+
// (7) single-use: jti unseen within the window.
|
|
159
|
+
if (deps.seenJti(assertion.jti))
|
|
160
|
+
return { ok: false, reason: 'replayed' };
|
|
161
|
+
return { ok: true, reason: 'ok' };
|
|
162
|
+
}
|
|
163
|
+
/** HTTP status for each verify reason — auth failures 401/403, freshness/replay 409. */
|
|
164
|
+
export function statusForPoolLinkReason(reason) {
|
|
165
|
+
switch (reason) {
|
|
166
|
+
case 'malformed':
|
|
167
|
+
return 400;
|
|
168
|
+
case 'wrong-holder':
|
|
169
|
+
case 'wrong-view':
|
|
170
|
+
case 'wrong-method':
|
|
171
|
+
case 'unexpected-issuer':
|
|
172
|
+
case 'unknown-issuer':
|
|
173
|
+
case 'signature-invalid':
|
|
174
|
+
return 401;
|
|
175
|
+
case 'expired':
|
|
176
|
+
case 'not-yet-valid':
|
|
177
|
+
case 'ttl-too-long':
|
|
178
|
+
case 'replayed':
|
|
179
|
+
return 409;
|
|
180
|
+
default:
|
|
181
|
+
return 400;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=PoolLinkAssertion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoolLinkAssertion.js","sourceRoot":"","sources":["../../src/core/PoolLinkAssertion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface PoolLinkJtiStoreDeps {
|
|
2
|
+
/** Absolute path to the persistence file. */
|
|
3
|
+
filePath: string;
|
|
4
|
+
now: () => number;
|
|
5
|
+
/**
|
|
6
|
+
* How long a recorded jti is retained for replay rejection (ms). MUST be ≥ the
|
|
7
|
+
* assertion TTL so a captured assertion can never outlive its retention. Default 1h.
|
|
8
|
+
*/
|
|
9
|
+
retentionMs?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Hard ceiling on the number of recorded jtis. A registered-but-misbehaving
|
|
12
|
+
* peer could otherwise mint many distinct-jti assertions and grow this store
|
|
13
|
+
* unboundedly (memory + disk DoS). At the cap, the oldest-expiry records are
|
|
14
|
+
* evicted to make room (they are the closest to GC anyway) and a loss counter
|
|
15
|
+
* is bumped. Default 100_000. (P19 — bounded by construction.)
|
|
16
|
+
*/
|
|
17
|
+
maxEntries?: number;
|
|
18
|
+
logger?: (line: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare class PoolLinkJtiStore {
|
|
21
|
+
private readonly d;
|
|
22
|
+
private jtis;
|
|
23
|
+
private loaded;
|
|
24
|
+
/** Count of records evicted to stay under maxEntries — observability. */
|
|
25
|
+
private droppedForCap;
|
|
26
|
+
constructor(d: PoolLinkJtiStoreDeps);
|
|
27
|
+
private retention;
|
|
28
|
+
private maxEntries;
|
|
29
|
+
private ensureLoaded;
|
|
30
|
+
/** Has this jti already been seen (replay)? Read-only; does not record. */
|
|
31
|
+
seen(jti: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Record a jti as consumed, bound to the assertion's expiry. Idempotent: a
|
|
34
|
+
* second record of the same jti is a no-op (the first already blocks replay).
|
|
35
|
+
* Persists BEFORE returning so a crash mid-record cannot yield a second accept.
|
|
36
|
+
*/
|
|
37
|
+
record(jti: string, assertionExpMs: number): void;
|
|
38
|
+
/** Evict oldest-expiry records until below the cap (leaving room for one insert). */
|
|
39
|
+
private enforceCap;
|
|
40
|
+
/** Count of currently-recorded jtis — tests/observability. */
|
|
41
|
+
size(): number;
|
|
42
|
+
/** Count of records evicted to stay under the size cap — tests/observability. */
|
|
43
|
+
droppedForCapCount(): number;
|
|
44
|
+
/** Drop records whose expiry is older than the retention window. */
|
|
45
|
+
private gc;
|
|
46
|
+
private persist;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=PoolLinkJtiStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoolLinkJtiStore.d.ts","sourceRoot":"","sources":["../../src/core/PoolLinkJtiStore.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAKD,qBAAa,gBAAgB;IAMf,OAAO,CAAC,QAAQ,CAAC,CAAC;IAL9B,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,yEAAyE;IACzE,OAAO,CAAC,aAAa,CAAK;gBAEG,CAAC,EAAE,oBAAoB;IAEpD,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IA0BpB,2EAA2E;IAC3E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAS1B;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI;IAiBjD,qFAAqF;IACrF,OAAO,CAAC,UAAU;IAclB,8DAA8D;IAC9D,IAAI,IAAI,MAAM;IAKd,iFAAiF;IACjF,kBAAkB,IAAI,MAAM;IAI5B,oEAAoE;IACpE,OAAO,CAAC,EAAE;IAYV,OAAO,CAAC,OAAO;CAiBhB"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoolLinkJtiStore — the single-use replay defense for WS4.4 pool-link
|
|
3
|
+
* assertions (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 e). SECURITY-SENSITIVE.
|
|
4
|
+
*
|
|
5
|
+
* Lives on the HOLDER machine. When a fronting machine proxies a `/view/:id`
|
|
6
|
+
* request with a signed, audience-bound assertion, the holder verifies it
|
|
7
|
+
* (PoolLinkAssertion.verifyPoolLinkAssertion) and then RECORDS its jti here so
|
|
8
|
+
* the SAME assertion can never be accepted twice. The recorded set is PERSISTED
|
|
9
|
+
* so a captured assertion cannot be replayed even across a holder restart,
|
|
10
|
+
* within its (short) TTL window.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors StreamTicketStore's factoring: pure + injected (clock, fs path) so the
|
|
13
|
+
* record / replay-detect / expiry / replay-across-restart lifecycle is
|
|
14
|
+
* deterministically unit-testable.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
const DEFAULT_RETENTION_MS = 60 * 60 * 1000;
|
|
19
|
+
const DEFAULT_MAX_ENTRIES = 100_000;
|
|
20
|
+
export class PoolLinkJtiStore {
|
|
21
|
+
d;
|
|
22
|
+
jtis = new Map();
|
|
23
|
+
loaded = false;
|
|
24
|
+
/** Count of records evicted to stay under maxEntries — observability. */
|
|
25
|
+
droppedForCap = 0;
|
|
26
|
+
constructor(d) {
|
|
27
|
+
this.d = d;
|
|
28
|
+
}
|
|
29
|
+
retention() {
|
|
30
|
+
return this.d.retentionMs ?? DEFAULT_RETENTION_MS;
|
|
31
|
+
}
|
|
32
|
+
maxEntries() {
|
|
33
|
+
return this.d.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
34
|
+
}
|
|
35
|
+
ensureLoaded() {
|
|
36
|
+
if (this.loaded)
|
|
37
|
+
return;
|
|
38
|
+
this.loaded = true;
|
|
39
|
+
try {
|
|
40
|
+
if (fs.existsSync(this.d.filePath)) {
|
|
41
|
+
const body = JSON.parse(fs.readFileSync(this.d.filePath, 'utf-8'));
|
|
42
|
+
if (body && body.jtis) {
|
|
43
|
+
for (const [j, r] of Object.entries(body.jtis)) {
|
|
44
|
+
if (r && typeof r.expiresAtMs === 'number') {
|
|
45
|
+
this.jtis.set(j, { expiresAtMs: r.expiresAtMs });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
// @silent-fallback-ok: a corrupt store FAILS CLOSED on the replay window
|
|
53
|
+
// only (loses recorded jtis), never crashes the serve path. The widened
|
|
54
|
+
// window is bounded by the assertion TTL (a re-presented jti's assertion
|
|
55
|
+
// fails 'expired' anyway once its short exp passes). Mirrors
|
|
56
|
+
// StreamTicketStore; logged with context, not a DegradationReporter case.
|
|
57
|
+
this.d.logger?.(`[pool-link-jti] store unreadable, starting empty: ${e?.message ?? e}`);
|
|
58
|
+
this.jtis.clear();
|
|
59
|
+
}
|
|
60
|
+
this.gc();
|
|
61
|
+
}
|
|
62
|
+
/** Has this jti already been seen (replay)? Read-only; does not record. */
|
|
63
|
+
seen(jti) {
|
|
64
|
+
this.ensureLoaded();
|
|
65
|
+
const rec = this.jtis.get(jti);
|
|
66
|
+
if (!rec)
|
|
67
|
+
return false;
|
|
68
|
+
// A record past retention is GC-eligible but, while present, still blocks
|
|
69
|
+
// replay. (Its assertion is long-expired, so this is belt-and-suspenders.)
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Record a jti as consumed, bound to the assertion's expiry. Idempotent: a
|
|
74
|
+
* second record of the same jti is a no-op (the first already blocks replay).
|
|
75
|
+
* Persists BEFORE returning so a crash mid-record cannot yield a second accept.
|
|
76
|
+
*/
|
|
77
|
+
record(jti, assertionExpMs) {
|
|
78
|
+
this.ensureLoaded();
|
|
79
|
+
this.gc();
|
|
80
|
+
if (this.jtis.has(jti))
|
|
81
|
+
return;
|
|
82
|
+
// Clamp the retained expiry to now + retention. Defense-in-depth: even if a
|
|
83
|
+
// far-future `exp` slipped past the holder's TTL-span ceiling, gc() (which
|
|
84
|
+
// keys off expiresAtMs) reliably drops this record within the retention
|
|
85
|
+
// window instead of pinning it for the attacker-chosen span.
|
|
86
|
+
const maxExpiry = this.d.now() + this.retention();
|
|
87
|
+
const expiresAtMs = Math.min(assertionExpMs, maxExpiry);
|
|
88
|
+
// Enforce the size ceiling: at the cap, evict oldest-expiry records first
|
|
89
|
+
// (closest to GC anyway). Bounded by construction (P19).
|
|
90
|
+
this.enforceCap();
|
|
91
|
+
this.jtis.set(jti, { expiresAtMs });
|
|
92
|
+
this.persist();
|
|
93
|
+
}
|
|
94
|
+
/** Evict oldest-expiry records until below the cap (leaving room for one insert). */
|
|
95
|
+
enforceCap() {
|
|
96
|
+
const cap = this.maxEntries();
|
|
97
|
+
if (this.jtis.size < cap)
|
|
98
|
+
return;
|
|
99
|
+
const sorted = [...this.jtis.entries()].sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs);
|
|
100
|
+
const toDrop = this.jtis.size - cap + 1;
|
|
101
|
+
for (let i = 0; i < toDrop && i < sorted.length; i++) {
|
|
102
|
+
this.jtis.delete(sorted[i][0]);
|
|
103
|
+
this.droppedForCap++;
|
|
104
|
+
}
|
|
105
|
+
this.d.logger?.(`[pool-link-jti] size cap ${cap} reached — evicted ${toDrop} oldest record(s) (total dropped-for-cap: ${this.droppedForCap})`);
|
|
106
|
+
}
|
|
107
|
+
/** Count of currently-recorded jtis — tests/observability. */
|
|
108
|
+
size() {
|
|
109
|
+
this.ensureLoaded();
|
|
110
|
+
return this.jtis.size;
|
|
111
|
+
}
|
|
112
|
+
/** Count of records evicted to stay under the size cap — tests/observability. */
|
|
113
|
+
droppedForCapCount() {
|
|
114
|
+
return this.droppedForCap;
|
|
115
|
+
}
|
|
116
|
+
/** Drop records whose expiry is older than the retention window. */
|
|
117
|
+
gc() {
|
|
118
|
+
const cutoff = this.d.now() - this.retention();
|
|
119
|
+
let changed = false;
|
|
120
|
+
for (const [j, r] of this.jtis) {
|
|
121
|
+
if (r.expiresAtMs < cutoff) {
|
|
122
|
+
this.jtis.delete(j);
|
|
123
|
+
changed = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (changed)
|
|
127
|
+
this.persist();
|
|
128
|
+
}
|
|
129
|
+
persist() {
|
|
130
|
+
try {
|
|
131
|
+
const dir = path.dirname(this.d.filePath);
|
|
132
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
133
|
+
const jtis = {};
|
|
134
|
+
for (const [j, r] of this.jtis)
|
|
135
|
+
jtis[j] = r;
|
|
136
|
+
const body = { version: 1, jtis };
|
|
137
|
+
const tmp = `${this.d.filePath}.tmp-${process.pid}`;
|
|
138
|
+
fs.writeFileSync(tmp, JSON.stringify(body, null, 2));
|
|
139
|
+
fs.renameSync(tmp, this.d.filePath);
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
// @silent-fallback-ok: a persistence failure degrades replay-across-restart
|
|
143
|
+
// protection for in-flight jtis only (all TTL-bounded); must not crash the
|
|
144
|
+
// serve path. The in-memory set still blocks replay within this process.
|
|
145
|
+
this.d.logger?.(`[pool-link-jti] persist failed: ${e?.message ?? e}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=PoolLinkJtiStore.js.map
|