@phosra/gatekeeper 0.1.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 (42) hide show
  1. package/README.md +83 -0
  2. package/dist/capabilities-cache.d.ts +25 -0
  3. package/dist/capabilities-cache.d.ts.map +1 -0
  4. package/dist/capabilities-cache.js +45 -0
  5. package/dist/capabilities-cache.js.map +1 -0
  6. package/dist/crosswalk.d.ts +11 -0
  7. package/dist/crosswalk.d.ts.map +1 -0
  8. package/dist/crosswalk.js +32 -0
  9. package/dist/crosswalk.js.map +1 -0
  10. package/dist/fleet-backcompat.test.d.ts +2 -0
  11. package/dist/fleet-backcompat.test.d.ts.map +1 -0
  12. package/dist/fleet-backcompat.test.js +38 -0
  13. package/dist/fleet-backcompat.test.js.map +1 -0
  14. package/dist/gatekeeper-version.test.d.ts +2 -0
  15. package/dist/gatekeeper-version.test.d.ts.map +1 -0
  16. package/dist/gatekeeper-version.test.js +12 -0
  17. package/dist/gatekeeper-version.test.js.map +1 -0
  18. package/dist/gatekeeper.d.ts +38 -0
  19. package/dist/gatekeeper.d.ts.map +1 -0
  20. package/dist/gatekeeper.js +466 -0
  21. package/dist/gatekeeper.js.map +1 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +5 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/ocss-http-client.d.ts +21 -0
  27. package/dist/ocss-http-client.d.ts.map +1 -0
  28. package/dist/ocss-http-client.js +45 -0
  29. package/dist/ocss-http-client.js.map +1 -0
  30. package/dist/protocol.d.ts +10 -0
  31. package/dist/protocol.d.ts.map +1 -0
  32. package/dist/protocol.js +10 -0
  33. package/dist/protocol.js.map +1 -0
  34. package/dist/trust-list-cache.d.ts +23 -0
  35. package/dist/trust-list-cache.d.ts.map +1 -0
  36. package/dist/trust-list-cache.js +35 -0
  37. package/dist/trust-list-cache.js.map +1 -0
  38. package/dist/types.d.ts +216 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +104 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +30 -0
@@ -0,0 +1,466 @@
1
+ import { createPrivateKey, createPublicKey, verify as nodeVerify, randomBytes, createHmac, timingSafeEqual } from "node:crypto";
2
+ import { compareToCeiling, ed25519Sign, validateResiduals, signReceipt, SUPPORTED_VERSIONS, negotiate, MAX_ORDINAL, b64urlRaw, b64urlRawDecode } from "@openchildsafety/ocss";
3
+ import { RuleRefRequired, NoRatingMappingError, UnmappedRatingValueError, AssuranceLevelError } from "./types.js";
4
+ import { contentAgeFor } from "./crosswalk.js";
5
+ import { OcssHttpClient } from "./ocss-http-client.js";
6
+ import { TrustListCache } from "./trust-list-cache.js";
7
+ import { CapabilitiesCache } from "./capabilities-cache.js";
8
+ import { RULE_ASSURANCE_FLOORS, ASSURANCE_RANK, HARD_AV_FLOOR_RULES, } from "./types.js";
9
+ // HARD_AV_FLOOR_RULES is imported from types.ts where it is DERIVED from
10
+ // RULE_ASSURANCE_FLOORS (the drift-checked map) — see types.ts for the
11
+ // derivation and the invariant comment. It was previously a hardcoded set
12
+ // here; the Fix 6 refactor ensures any future document_verified-floor rule
13
+ // added to RULE_ASSURANCE_FLOORS is automatically covered by the
14
+ // confirm-stage guardrail without a manual update to this file.
15
+ /** Direct Ed25519 verify of a raw 32-byte public key over a message. */
16
+ function ed25519VerifyRaw(pub, msg, sig) {
17
+ const key = createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: b64urlRaw(pub) }, format: "jwk" });
18
+ return nodeVerify(null, Buffer.from(msg), key, Buffer.from(sig));
19
+ }
20
+ /** Derive the base64url-raw Ed25519 public X from a 32-byte raw seed (node:crypto). */
21
+ function ed25519PublicXB64Url(seed) {
22
+ const pkcs8 = Buffer.concat([Buffer.from("302e020100300506032b657004220420", "hex"), Buffer.from(seed)]);
23
+ const priv = createPrivateKey({ key: pkcs8, format: "der", type: "pkcs8" });
24
+ return createPublicKey(priv).export({ format: "jwk" }).x;
25
+ }
26
+ /** Internal in-memory stores. */
27
+ class EnforcementProfileStore {
28
+ m = new Map();
29
+ get(endpointId) {
30
+ return this.m.get(endpointId);
31
+ }
32
+ put(endpointId, p) {
33
+ this.m.set(endpointId, p);
34
+ }
35
+ }
36
+ class ConsentStore {
37
+ active = new Set();
38
+ revoked = new Set();
39
+ markActive(endpointId) {
40
+ this.active.add(endpointId);
41
+ this.revoked.delete(endpointId);
42
+ }
43
+ markRevoked(endpointId) {
44
+ this.revoked.add(endpointId);
45
+ }
46
+ isActive(endpointId) {
47
+ return this.active.has(endpointId) && !this.revoked.has(endpointId);
48
+ }
49
+ }
50
+ /** The effective endpoint for no-arg accessors: the connected label wins over cfg. */
51
+ function defaultEid(rt) {
52
+ return rt.connectedEndpointId ?? rt.cfg.endpointId;
53
+ }
54
+ export function createGatekeeper(config) {
55
+ // §3.1 construction-time assertion: the reserved EC payload key (if any) MUST be a
56
+ // distinct identity from the Ed25519 signing key. (Guarded by presence — the key is
57
+ // OPTIONAL/reserved and unused in the read path.)
58
+ if (config.platformPayloadKeyJwk) {
59
+ const signX = ed25519PublicXB64Url(config.gatekeeperSigningKey.seed);
60
+ if (signX === config.platformPayloadKeyJwk.x) {
61
+ throw new Error("@phosra/gatekeeper: gatekeeperSigningKey and platformPayloadKeyJwk MUST be distinct identities");
62
+ }
63
+ }
64
+ const now = config.now ?? (() => Date.now());
65
+ const sk = config.gatekeeperSigningKey;
66
+ const signingKey = {
67
+ keyId: sk.keyID,
68
+ sign: (msg) => ed25519Sign(sk.seed, msg),
69
+ };
70
+ const http = new OcssHttpClient({
71
+ baseUrl: config.censusBaseUrl,
72
+ senderKey: sk,
73
+ fetchImpl: config.fetchImpl,
74
+ now,
75
+ });
76
+ const trustList = new TrustListCache({
77
+ fetchImpl: config.fetchImpl ?? fetch,
78
+ baseUrl: config.censusBaseUrl,
79
+ trustRootXB64Url: config.trustRootXB64Url,
80
+ now,
81
+ });
82
+ const caps = new CapabilitiesCache({
83
+ fetchImpl: config.fetchImpl ?? fetch,
84
+ baseUrl: config.censusBaseUrl,
85
+ trustRootXB64Url: config.trustRootXB64Url,
86
+ now,
87
+ });
88
+ const rt = {
89
+ cfg: config,
90
+ http,
91
+ trustList,
92
+ caps,
93
+ profileStore: new EnforcementProfileStore(),
94
+ consentStore: new ConsentStore(),
95
+ signingKey,
96
+ now,
97
+ processedConnectLabels: new Set(),
98
+ };
99
+ // P2 boots NO background timers (kept out of unit tests; the harness calls
100
+ // refreshProfile explicitly). tlRefreshIntervalMs/pollIntervalMs are reserved.
101
+ //
102
+ // _testInjectProfile is attached to the runtime object but NOT declared on the
103
+ // public Gatekeeper interface — test fixtures access it via a typed cast in
104
+ // test/fixtures.ts (GatekeeperRuntime = Gatekeeper & { _testInjectProfile }).
105
+ const gk = {
106
+ refreshTrustList: () => refreshTrustList(rt),
107
+ refreshProfile: (endpointId) => refreshProfile(rt, endpointId),
108
+ getCachedProfile: (endpointId) => rt.profileStore.get(endpointId ?? defaultEid(rt)),
109
+ isAllowed: (args) => isAllowed(rt, args),
110
+ check: (category, ctx) => isAllowed(rt, { category, signal: ctx }),
111
+ confirm: (envelopeRef, ruleRef, state, methodClass) => confirm(rt, envelopeRef, ruleRef, state, methodClass),
112
+ handleConnect: (req) => handleConnect(rt, req),
113
+ reportParentChange: (change) => reportParentChange(rt, change),
114
+ destroy: () => {
115
+ /* no timers in P2 */
116
+ },
117
+ _testInjectProfile: (profile) => {
118
+ rt.profileStore.put(profile.endpointId, profile);
119
+ rt.consentStore.markActive(profile.endpointId);
120
+ },
121
+ };
122
+ return gk;
123
+ }
124
+ // --- Task 4: profile read path ---
125
+ async function refreshTrustList(rt) {
126
+ await rt.trustList.refresh();
127
+ await rt.caps.refresh().catch(() => { }); // best-effort — advisory; never a hard dependency
128
+ }
129
+ async function refreshProfile(rt, endpointId) {
130
+ const eid = endpointId ?? defaultEid(rt);
131
+ await rt.trustList.ensure();
132
+ const res = await rt.http.get(`/api/v1/enforcement-profiles/${eid}`);
133
+ // any non-2xx (404 unknown/wrong-resolver, 401/403 unaccredited/revoked, 5xx transient) →
134
+ // fail-closed, cache nothing → the gate blocks (§9.3(b) + fail-closed). Do NOT throw on
135
+ // an HTTP status: a revoked/unaccredited platform must degrade to BLOCK, not crash the caller.
136
+ if (!res.ok)
137
+ return;
138
+ const signed = (await res.json());
139
+ // VERIFY the router signature directly (NOT verifyDocument — that is the root path):
140
+ const routerPub = rt.trustList.signingKey(signed.key_id);
141
+ const ok = ed25519VerifyRaw(routerPub, Buffer.from(signed.document, "utf-8"), b64urlRawDecode(signed.sig));
142
+ if (!ok)
143
+ throw new Error("@phosra/gatekeeper: enforcement profile signature invalid (fail-closed)");
144
+ const doc = JSON.parse(signed.document);
145
+ if (doc.document_type !== "enforcement_profile") {
146
+ throw new Error(`@phosra/gatekeeper: not an enforcement_profile (${doc.document_type})`);
147
+ }
148
+ // GREASE BLOCK (P0 Task 8): accept any echoed version the gatekeeper speaks;
149
+ // block an unknown/unparseable/GREASE version fail-closed (do NOT pass through).
150
+ if (negotiate([doc.ocss_version], SUPPORTED_VERSIONS) === null) {
151
+ throw new Error(`@phosra/gatekeeper: profile version ${doc.ocss_version} not in supported set ${SUPPORTED_VERSIONS.join(",")} (fail-closed)`);
152
+ }
153
+ // §profile residuals are N/A at this receive surface (DoH-only resolvers carry none);
154
+ // validateResiduals tolerates undefined/empty and throws only on a malformed residual.
155
+ validateResiduals(doc.residuals ?? undefined);
156
+ rt.profileStore.put(eid, {
157
+ endpointId: eid,
158
+ document_type: doc.document_type,
159
+ ocss_version: doc.ocss_version,
160
+ profile_ref: doc.profile_ref,
161
+ window: doc.window,
162
+ categories: doc.categories,
163
+ });
164
+ rt.consentStore.markActive(eid); // a router-verified in-window profile implies active consent at compile
165
+ }
166
+ // --- filled in Task 5 ---
167
+ /** Parse the census `params` field, which is a JSON object on the wire but tolerate a string. */
168
+ function parseParams(p) {
169
+ if (p == null)
170
+ return {};
171
+ return typeof p === "string" ? JSON.parse(p) : p;
172
+ }
173
+ function failClosedVerdict(rt, category) {
174
+ return { decision: "block", failMode: "block", ruleSlug: category, ruleRef: null, confirm: async () => undefined };
175
+ }
176
+ function isAllowed(rt, args) {
177
+ const eid = args.endpointId ?? defaultEid(rt);
178
+ const prof = rt.profileStore.get(eid);
179
+ if (!prof)
180
+ return failClosedVerdict(rt, args.category); // missing profile
181
+ if (!rt.consentStore.isActive(eid))
182
+ return failClosedVerdict(rt, args.category); // no active consent
183
+ const _na = Date.parse(prof.window.not_after);
184
+ const _nb = Date.parse(prof.window.not_before);
185
+ if (!Number.isFinite(_na) || _na < rt.now() || !Number.isFinite(_nb) || _nb > rt.now())
186
+ return failClosedVerdict(rt, args.category); // expired / not yet open / malformed bound
187
+ const cat = prof.categories.find((c) => c.category === args.category);
188
+ if (!cat)
189
+ return failClosedVerdict(rt, args.category); // absent category
190
+ // ASSURANCE-LEVEL GUARDRAIL (spec-faithfulness, §7.3):
191
+ // If the caller supplies signal.assurance_level, verify it meets the rule's
192
+ // §7.3 floor INDEPENDENTLY of the compiled profile decision. A signature
193
+ // proves provenance, not age truth — a parental_declared signal MUST NOT
194
+ // satisfy a document_verified-floor rule (UT/TX AV statutes, adult_site_av_required,
195
+ // hard_id_verification_escalation). Fail-closed: unknown assurance strings rank -1.
196
+ //
197
+ // This is a defense-in-depth check; the census compiler already applies floors
198
+ // at compile time. The guardrail catches races (profile compiled before the
199
+ // signal's assurance level was known) and mis-use by platform integrators who
200
+ // call isAllowed with a weaker signal than the rule requires.
201
+ const signalAssurance = args.signal?.assurance_level;
202
+ if (typeof signalAssurance === "string") {
203
+ const ruleFloor = RULE_ASSURANCE_FLOORS[args.category];
204
+ if (ruleFloor !== undefined) {
205
+ const signalRank = ASSURANCE_RANK[signalAssurance] ?? -1;
206
+ if (signalRank < ruleFloor) {
207
+ // Assurance insufficient: fail-closed regardless of profile decision.
208
+ // The failMode from the profile still applies (open profiles stay open).
209
+ const failMode = cat.fail_mode === "open" ? "allow" : "block";
210
+ return {
211
+ decision: "block",
212
+ failMode,
213
+ ruleSlug: cat.rule_slug ?? args.category,
214
+ ruleRef: cat.rule_ref ?? null,
215
+ // M5: throw for "applied" (no false enforcement claim on a weak-assurance verdict).
216
+ // "degraded"/"refused" may still be submitted if a rule_ref is available (honest
217
+ // attestation that enforcement was attempted but the required assurance was not met).
218
+ confirm: (state) => {
219
+ if (state === "applied")
220
+ throw new AssuranceLevelError(args.category);
221
+ const ruleRef = cat.rule_ref ?? null;
222
+ const envelopeRef = prof.profile_ref ?? `endpoint:${eid}:${args.category}`;
223
+ return ruleRef ? confirm(rt, envelopeRef, ruleRef, state) : Promise.resolve(undefined);
224
+ },
225
+ };
226
+ }
227
+ }
228
+ }
229
+ const failMode = cat.fail_mode === "open" ? "allow" : "block";
230
+ let decision = cat.decision;
231
+ const mapping = rt.cfg.ratingMappings.find((m) => m.ocssCategory === args.category);
232
+ if (mapping) {
233
+ const nativeCode = args.signal?.[mapping.myField];
234
+ const contentAge = contentAgeFor(mapping.vocabulary, nativeCode, mapping.codeMap);
235
+ const params = parseParams(cat.params);
236
+ if (params.max_allowed == null || !Number.isFinite(params.max_allowed)) {
237
+ decision = "block"; // fail-closed: no signed ceiling
238
+ }
239
+ else {
240
+ const cmp = compareToCeiling(contentAge, params.max_allowed);
241
+ decision = cmp === "block" ? "block" : cat.decision; // warn/allow come from the profile
242
+ }
243
+ }
244
+ // CHILD-SAFETY FIX (P1): the verb set {allow,warn,block} is no longer assumed. A profile verb
245
+ // the gatekeeper does not natively action (e.g. a future "throttle") is COERCED to its signed
246
+ // fallback from the root-verified capabilities doc; with no signed fallback OR no caps doc loaded
247
+ // it defaults to "block". This runs LAST so it also clamps a crosswalk result. Without it, an
248
+ // unknown verb rode into the Verdict verbatim and an integrator's `if (decision==="block")` never
249
+ // fired → fail-OPEN. Coercion makes that path fail-CLOSED. Known verbs are unchanged (back-compat).
250
+ //
251
+ // RUNTIME ENUM GUARD (P1 review finding 1): the signed fallback itself is ALSO runtime-validated.
252
+ // A caps doc carrying {verb:"throttle", fallback:"scalate"} (a typo or a future extension the
253
+ // verifier did not yet reject) must NOT allow "scalate" to ride into the Verdict. We explicitly
254
+ // check the fallback is in the closed enum — anything outside {allow,warn,block} → "block".
255
+ // This is the LAST LINE OF DEFENSE, source-agnostic: it closes the gate even if verify.ts and
256
+ // the Go builder both failed to catch the bad fallback upstream.
257
+ if (decision !== "allow" && decision !== "warn" && decision !== "block") {
258
+ const fb = rt.caps.doc()?.decision_verbs?.find((d) => d.verb === decision)?.fallback;
259
+ decision = (fb === "allow" || fb === "warn" || fb === "block") ? fb : "block";
260
+ }
261
+ const ruleRef = cat.rule_ref ?? null;
262
+ const envelopeRef = prof.profile_ref ?? `endpoint:${eid}:${args.category}`;
263
+ const category = args.category;
264
+ return {
265
+ decision,
266
+ failMode,
267
+ ruleSlug: cat.rule_slug ?? args.category,
268
+ ruleRef,
269
+ confirm: (state) => {
270
+ // §7.3 CONFIRM-STAGE ASSURANCE GUARDRAIL (defense-in-depth, second layer):
271
+ // The isAllowed stage already blocks on a weak signal.assurance_level.
272
+ // This layer catches the case where isAllowed returned a verdict (possibly
273
+ // "block") and the platform nonetheless calls confirm("applied") — asserting
274
+ // the rule FIRED even though the platform cannot have performed the hard AV
275
+ // (e.g. a parental-declared signal was used to decide, but the rule mandates
276
+ // document_verified). The receipt MUST NOT assert "applied" for a rule the
277
+ // platform did not actually enforce at the required assurance level.
278
+ if (state === "applied" && HARD_AV_FLOOR_RULES.has(category)) {
279
+ throw new AssuranceLevelError(category);
280
+ }
281
+ return ruleRef ? confirm(rt, envelopeRef, ruleRef, state) : Promise.resolve(undefined);
282
+ },
283
+ };
284
+ }
285
+ // --- filled in Task 6 ---
286
+ async function confirm(rt, envelopeRef, ruleRef, state, methodClass) {
287
+ // §8.3.8: rule_ref is required. A fail-closed verdict (ruleRef null) MUST NOT confirm —
288
+ // an orphan confirm pollutes the attribution rail (census keys idempotency on rule_ref).
289
+ if (!ruleRef)
290
+ throw new RuleRefRequired();
291
+ // INTERVENTION-FIRED SEMANTICS (§8.3.8 spec-faithfulness):
292
+ // enforcement_state == "applied" already means the intervention fired — the
293
+ // closed 3-value vocab is the spec's way of encoding this. A separate
294
+ // intervention_fired boolean would be redundant AND non-spec (the census
295
+ // strict-decodes EnforcementResultBody with DisallowUnknownFields and would
296
+ // reject it). What the spec requires is that the platform attest HOW the
297
+ // intervention fired via method_class — distinguishing "policy received" from
298
+ // "enforcement confirmed." This is enforced on the census side for "applied":
299
+ // method_class must be non-empty (census rejects blank method_class on applied).
300
+ const body = {
301
+ envelope_ref: envelopeRef,
302
+ rule_ref: ruleRef,
303
+ enforcement_state: state,
304
+ applied_at: new Date(rt.now()).toISOString(),
305
+ method_class: methodClass ?? "platform_gate",
306
+ };
307
+ // signReceipt: sig covers canon({body, spec, type}) only (D-9); id/key_id ride outside.
308
+ const receipt = signReceipt("enforcement_result", body, rt.signingKey, new Date(rt.now()), new Uint8Array(randomBytes(10)));
309
+ // receipt.body is a Uint8Array; JSON.stringify(Uint8Array) emits a numeric-keyed object
310
+ // {"0":123,...} (the "numeric-keys trap"). The census strict-decodes body as a verbatim
311
+ // json.RawMessage, so the bytes must match the signed canon. Decode once here for the
312
+ // wire object; return the in-memory receipt so verifyReceipt still works on the Uint8Array.
313
+ const wire = { ...receipt, body: JSON.parse(new TextDecoder().decode(receipt.body)) };
314
+ const res = await rt.http.post("/api/v1/enforcement-confirmations", wire);
315
+ // 201 = written; 200 = idempotent same receipt; 409 = already confirmed (census deduplicates
316
+ // on rule_ref — same (policy_id, category) produces the same rule_ref, so a rule confirmed
317
+ // in a prior session/run returns 409 on re-confirm). All three mean "enforcement is recorded".
318
+ if (res.status !== 201 && res.status !== 200 && res.status !== 409) {
319
+ throw new Error(`@phosra/gatekeeper: confirm POST failed (${res.status})`);
320
+ }
321
+ return receipt;
322
+ }
323
+ // --- Task 11: reportParentChange (P4 §3.2) ---
324
+ /** Canonical key-sorted JSON bytes over the proposal event fields (mirrors Go canon). */
325
+ function canonEvent(ev) {
326
+ const sorted = {};
327
+ for (const k of Object.keys(ev).sort())
328
+ sorted[k] = ev[k];
329
+ return new TextEncoder().encode(JSON.stringify(sorted));
330
+ }
331
+ async function reportParentChange(rt, change) {
332
+ const eid = change.endpointId ?? defaultEid(rt);
333
+ // C8: only declared total-ordered rating fields are reportable.
334
+ const mapping = rt.cfg.ratingMappings.find((m) => m.ocssCategory === change.ocssCategory);
335
+ if (!mapping)
336
+ throw new NoRatingMappingError(change.ocssCategory);
337
+ // Normalize through the SAME crosswalk isAllowed uses (C1/C8).
338
+ // The census does NOT normalize (no crosswalk on the census, C2) — send the integer.
339
+ const desiredMaxAllowed = contentAgeFor(mapping.vocabulary, change.nativeValue, mapping.codeMap);
340
+ // Fix 1 — unmapped-value safety guard: MAX_ORDINAL is the fail-closed sentinel meaning
341
+ // "unresolvable code". It is safe as a CONTENT-AGE (→ block), but MUST NOT be sent as a
342
+ // proposed CEILING (desired_max_allowed === MAX_ORDINAL = "allow everything"). Reject here
343
+ // instead of emitting a maximally-permissive proposal.
344
+ if (desiredMaxAllowed === MAX_ORDINAL) {
345
+ throw new UnmappedRatingValueError(mapping.vocabulary, change.nativeValue);
346
+ }
347
+ // Echo-suppression (A5/§5.11.1): EXACT equality against the cached profile ceiling
348
+ // (per-endpoint). Do NOT emit a proposal if the value echoes the last-received canonical.
349
+ const prof = rt.profileStore.get(eid);
350
+ const cat = prof?.categories.find((c) => c.category === change.ocssCategory);
351
+ const params = cat ? parseParams(cat.params) : {};
352
+ if (params.max_allowed != null &&
353
+ Number.isFinite(params.max_allowed) &&
354
+ desiredMaxAllowed === params.max_allowed) {
355
+ return { suppressed: true };
356
+ }
357
+ // Sign a self-contained phosra_platform_proposal (C6/R-4): Ed25519 sig over the
358
+ // canonical event bytes. Distinct from the RFC-9421 transport sig the POST adds.
359
+ // This is a Phosra-PRODUCT signed event — NOT an OCSS vocab.ts envelope_type,
360
+ // NOT enforcement_result=degraded, MUST NOT write a rule.
361
+ // Fix 2 — event_type is included in the signed ev so the kind is tamper-bound by the
362
+ // Ed25519 signature. "phosra_platform_proposal" is a Phosra-PRODUCT event — NOT an OCSS
363
+ // vocab.ts envelope_type. The distinction is explicit in the signed bytes.
364
+ const ev = {
365
+ change_scope: change.changeScope,
366
+ desired_max_allowed: desiredMaxAllowed,
367
+ endpoint_id: eid,
368
+ event_type: "phosra_platform_proposal",
369
+ ocss_category: change.ocssCategory,
370
+ signed_at: new Date(rt.now()).toISOString().replace(/\.\d{3}Z$/, "Z"),
371
+ };
372
+ const canonBytes = canonEvent(ev);
373
+ const sig = ed25519Sign(rt.cfg.gatekeeperSigningKey.seed, canonBytes);
374
+ const body = { ...ev, key_id: rt.cfg.gatekeeperSigningKey.keyID, sig: b64urlRaw(sig) };
375
+ // POST — rt.http.post adds the RFC-9421 transport signature over the wire body.
376
+ const res = await rt.http.post("/api/v1/proposals", body);
377
+ if (!res.ok) {
378
+ throw new Error(`reportParentChange: census POST /proposals failed ${res.status}`);
379
+ }
380
+ const filed = (await res.json());
381
+ return { proposalId: filed.body?.proposal_id ?? "", delta_kind: filed.body?.delta_kind };
382
+ }
383
+ /** Compute HMAC-SHA256(secret, message) → lowercase hex.
384
+ * Matches the server-side signing scheme in internal/service/webhook.go:155-157. */
385
+ function hmacSha256Hex(secret, message) {
386
+ return createHmac("sha256", secret).update(message).digest("hex");
387
+ }
388
+ /** P3 connect callback. Receives the writer-BFF-delivered endpoint_id_label (the census
389
+ * disclosed it ONCE to completeLink on the writer plane — GC1), persists it as the
390
+ * connected endpoint, and activates the PULL path. No decrypt (GC3); webhook PUSH deferred (GC7).
391
+ *
392
+ * Security: when cfg.connectSecret is set, the `X-Phosra-Signature` header MUST carry a
393
+ * valid HMAC-SHA256(secret, rawBody) hex value. Missing or wrong signature → HTTP 401
394
+ * (fail-closed). Algorithm mirrors internal/service/webhook.go:155-157.
395
+ *
396
+ * Idempotency: a re-delivered connect callback for an already-processed label returns 200
397
+ * immediately without re-running refreshProfile (safe to replay; nonce-in-label is the key).
398
+ *
399
+ * NOT-PROVEN (C7 deferral): body.state is recorded but NOT validated against a platform-side
400
+ * pending session record here — consumer-platform session-binding validation is P4+. */
401
+ async function handleConnect(rt, req) {
402
+ // Read the raw body text first so we can HMAC over the exact bytes received.
403
+ let rawBody;
404
+ try {
405
+ rawBody = await req.text();
406
+ }
407
+ catch {
408
+ return new Response(JSON.stringify({ error: "malformed body" }), { status: 400, headers: { "content-type": "application/json" } });
409
+ }
410
+ // HMAC-SHA256 verification (fail-closed when connectSecret is configured).
411
+ const connectSecret = rt.cfg.connectSecret;
412
+ if (connectSecret) {
413
+ const sigHeader = req.headers.get("x-phosra-signature");
414
+ if (!sigHeader) {
415
+ return new Response(JSON.stringify({ error: "missing X-Phosra-Signature" }), { status: 401, headers: { "content-type": "application/json" } });
416
+ }
417
+ const expected = hmacSha256Hex(connectSecret, rawBody);
418
+ // Constant-time compare (same length: 64 hex chars for SHA-256). A length mismatch means
419
+ // the header value is definitely wrong, so reject without timing-sensitive comparison.
420
+ if (sigHeader.length !== expected.length ||
421
+ !timingSafeEqual(Buffer.from(sigHeader, "utf8"), Buffer.from(expected, "utf8"))) {
422
+ return new Response(JSON.stringify({ error: "invalid X-Phosra-Signature" }), { status: 401, headers: { "content-type": "application/json" } });
423
+ }
424
+ }
425
+ // Parse JSON body.
426
+ let body;
427
+ try {
428
+ body = JSON.parse(rawBody);
429
+ }
430
+ catch {
431
+ return new Response(JSON.stringify({ error: "malformed body" }), { status: 400, headers: { "content-type": "application/json" } });
432
+ }
433
+ const label = body.endpoint_id_label;
434
+ if (!label) {
435
+ return new Response(JSON.stringify({ error: "endpoint_id_label required" }), { status: 400, headers: { "content-type": "application/json" } });
436
+ }
437
+ // Idempotency guard: a re-delivered connect callback for an already-processed label
438
+ // returns 200 immediately (binding already established; re-running refreshProfile is
439
+ // unnecessary and could cause thundering-herd on retries). A nonce embedded in the
440
+ // label by the census (GC1) makes distinct connect invocations distinct labels.
441
+ if (rt.processedConnectLabels.has(label)) {
442
+ return new Response(JSON.stringify({ connected: true, endpointId: label }), {
443
+ status: 200,
444
+ headers: { "content-type": "application/json" },
445
+ });
446
+ }
447
+ // body.state intentionally unvalidated here (C7 deferral — see GC6/NOT-PROVEN).
448
+ rt.connectedEndpointId = label;
449
+ rt.processedConnectLabels.add(label);
450
+ try {
451
+ await refreshProfile(rt, label); // PULL: fetch + router-verify + cache + markActive (best-effort)
452
+ }
453
+ catch {
454
+ // refreshProfile is best-effort here: a profile may legitimately not exist yet
455
+ // (no rule written to this endpoint) and will populate on the first PULL after
456
+ // rules are written. The binding is established once the label is persisted —
457
+ // that is the "connected" (Plaid-linked) semantic. → status 200 always.
458
+ }
459
+ // "connected" = the binding/label was persisted, NOT "a profile is immediately cached".
460
+ // Profile priming above is best-effort; callers verify profile presence via getCachedProfile.
461
+ return new Response(JSON.stringify({ connected: true, endpointId: label }), {
462
+ status: 200,
463
+ headers: { "content-type": "application/json" },
464
+ });
465
+ }
466
+ //# sourceMappingURL=gatekeeper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gatekeeper.js","sourceRoot":"","sources":["../src/gatekeeper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { createGatekeeper } from "./gatekeeper.js";
2
+ export { contentAgeFor } from "./crosswalk.js";
3
+ export { RuleRefRequired, NoRatingMappingError, UnmappedRatingValueError, AssuranceLevelError, type RatingMapping, type GatekeeperConfig, type Verdict, type GatekeeperCategory, type VerifiedProfile, type Gatekeeper, type ReportParentChangeArgs, type ReportParentChangeResult, } from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAC9B,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // @phosra/gatekeeper public API barrel.
2
+ export { createGatekeeper } from "./gatekeeper.js";
3
+ export { contentAgeFor } from "./crosswalk.js";
4
+ export { RuleRefRequired, NoRatingMappingError, UnmappedRatingValueError, AssuranceLevelError, } from "./types.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GASpB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type SenderKey } from "@openchildsafety/ocss";
2
+ export interface OcssHttpClientConfig {
3
+ baseUrl: string;
4
+ senderKey: SenderKey;
5
+ fetchImpl?: typeof fetch;
6
+ now?: () => number;
7
+ }
8
+ /**
9
+ * OcssHttpClient is the generic RFC-9421 census transport for @phosra/gatekeeper.
10
+ * Signs the §9.3(b) profile GET and the §8.3.8 confirm POST as the gatekeeper key.
11
+ * No @ocss/sim-common, no @phosra/link — its own boundary.
12
+ */
13
+ export declare class OcssHttpClient {
14
+ private cfg;
15
+ private fetchImpl;
16
+ private now;
17
+ constructor(cfg: OcssHttpClientConfig);
18
+ post(path: string, body: unknown): Promise<Response>;
19
+ get(path: string): Promise<Response>;
20
+ }
21
+ //# sourceMappingURL=ocss-http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocss-http-client.d.ts","sourceRoot":"","sources":["../src/ocss-http-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEjF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,SAAS,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,GAAG,CAAc;gBACb,GAAG,EAAE,oBAAoB;IAM/B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkBpD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAW3C"}
@@ -0,0 +1,45 @@
1
+ import { signRequest, SPEC_VERSION } from "@openchildsafety/ocss";
2
+ /**
3
+ * OcssHttpClient is the generic RFC-9421 census transport for @phosra/gatekeeper.
4
+ * Signs the §9.3(b) profile GET and the §8.3.8 confirm POST as the gatekeeper key.
5
+ * No @ocss/sim-common, no @phosra/link — its own boundary.
6
+ */
7
+ export class OcssHttpClient {
8
+ cfg;
9
+ fetchImpl;
10
+ now;
11
+ constructor(cfg) {
12
+ this.cfg = cfg;
13
+ this.fetchImpl = cfg.fetchImpl ?? fetch;
14
+ this.now = cfg.now ?? (() => Date.now());
15
+ }
16
+ async post(path, body) {
17
+ const targetURI = this.cfg.baseUrl + path;
18
+ const bodyText = JSON.stringify(body);
19
+ const headers = signRequest({
20
+ method: "POST",
21
+ targetURI,
22
+ body: new TextEncoder().encode(bodyText),
23
+ keyID: this.cfg.senderKey.keyID,
24
+ seed: this.cfg.senderKey.seed,
25
+ created: Math.floor(this.now() / 1000),
26
+ });
27
+ headers["Content-Type"] = "application/json";
28
+ if (headers["OCSS-Spec-Version"] !== SPEC_VERSION) {
29
+ throw new Error(`@phosra/gatekeeper: spec version drift ${headers["OCSS-Spec-Version"]} != ${SPEC_VERSION}`);
30
+ }
31
+ return this.fetchImpl(targetURI, { method: "POST", headers, body: bodyText });
32
+ }
33
+ async get(path) {
34
+ const targetURI = this.cfg.baseUrl + path;
35
+ const headers = signRequest({
36
+ method: "GET",
37
+ targetURI,
38
+ keyID: this.cfg.senderKey.keyID,
39
+ seed: this.cfg.senderKey.seed,
40
+ created: Math.floor(this.now() / 1000),
41
+ });
42
+ return this.fetchImpl(targetURI, { method: "GET", headers });
43
+ }
44
+ }
45
+ //# sourceMappingURL=ocss-http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocss-http-client.js","sourceRoot":"","sources":["../src/ocss-http-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAkB,MAAM,uBAAuB,CAAA;AASjF;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACjB,GAAG,CAAsB;IACzB,SAAS,CAAc;IACvB,GAAG,CAAc;IACzB,YAAY,GAAyB;QACnC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,KAAK,CAAA;QACvC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAa;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAA;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC;YAC1B,MAAM,EAAE,MAAM;YACd,SAAS;YACT,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC;YACxC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK;YAC/B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI;YAC7B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;SACvC,CAAC,CAAA;QACF,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAA;QAC5C,IAAI,OAAO,CAAC,mBAAmB,CAAC,KAAK,YAAY,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,0CAA0C,OAAO,CAAC,mBAAmB,CAAC,OAAO,YAAY,EAAE,CAAC,CAAA;QAC9G,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAA;QACzC,MAAM,OAAO,GAAG,WAAW,CAAC;YAC1B,MAAM,EAAE,KAAK;YACb,SAAS;YACT,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK;YAC/B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI;YAC7B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;SACvC,CAAC,CAAA;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IAC9D,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @phosra/gatekeeper/protocol — byte-identical re-export of @openchildsafety/ocss.
3
+ *
4
+ * This is a pure re-export, not a capture vector: no re-wrapping,
5
+ * no interposition, no shadowing. Callers may import from either
6
+ * "@openchildsafety/ocss" or "@phosra/gatekeeper/protocol" and get the same exports
7
+ * with the same object identities — the platform-side half of the §12.3 cut-test.
8
+ */
9
+ export * from "@openchildsafety/ocss";
10
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @phosra/gatekeeper/protocol — byte-identical re-export of @openchildsafety/ocss.
3
+ *
4
+ * This is a pure re-export, not a capture vector: no re-wrapping,
5
+ * no interposition, no shadowing. Callers may import from either
6
+ * "@openchildsafety/ocss" or "@phosra/gatekeeper/protocol" and get the same exports
7
+ * with the same object identities — the platform-side half of the §12.3 cut-test.
8
+ */
9
+ export * from "@openchildsafety/ocss";
10
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,uBAAuB,CAAA"}