@real-life/trust-protocol 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 (58) hide show
  1. package/README.md +99 -0
  2. package/dist/core.d.ts +32 -0
  3. package/dist/core.d.ts.map +1 -0
  4. package/dist/core.js +312 -0
  5. package/dist/core.js.map +1 -0
  6. package/dist/crypto.d.ts +32 -0
  7. package/dist/crypto.d.ts.map +1 -0
  8. package/dist/crypto.js +114 -0
  9. package/dist/crypto.js.map +1 -0
  10. package/dist/delivery.d.ts +51 -0
  11. package/dist/delivery.d.ts.map +1 -0
  12. package/dist/delivery.js +170 -0
  13. package/dist/delivery.js.map +1 -0
  14. package/dist/encounter.d.ts +63 -0
  15. package/dist/encounter.d.ts.map +1 -0
  16. package/dist/encounter.js +56 -0
  17. package/dist/encounter.js.map +1 -0
  18. package/dist/identity.d.ts +25 -0
  19. package/dist/identity.d.ts.map +1 -0
  20. package/dist/identity.js +87 -0
  21. package/dist/identity.js.map +1 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +29 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/probe/continuity.d.ts +99 -0
  27. package/dist/probe/continuity.d.ts.map +1 -0
  28. package/dist/probe/continuity.js +303 -0
  29. package/dist/probe/continuity.js.map +1 -0
  30. package/dist/probe/deps.d.ts +61 -0
  31. package/dist/probe/deps.d.ts.map +1 -0
  32. package/dist/probe/deps.js +54 -0
  33. package/dist/probe/deps.js.map +1 -0
  34. package/dist/probe/introduce.d.ts +70 -0
  35. package/dist/probe/introduce.d.ts.map +1 -0
  36. package/dist/probe/introduce.js +295 -0
  37. package/dist/probe/introduce.js.map +1 -0
  38. package/dist/probe/membership.d.ts +90 -0
  39. package/dist/probe/membership.d.ts.map +1 -0
  40. package/dist/probe/membership.js +450 -0
  41. package/dist/probe/membership.js.map +1 -0
  42. package/dist/probe/trust.d.ts +116 -0
  43. package/dist/probe/trust.d.ts.map +1 -0
  44. package/dist/probe/trust.js +345 -0
  45. package/dist/probe/trust.js.map +1 -0
  46. package/dist/probe.d.ts +8 -0
  47. package/dist/probe.d.ts.map +1 -0
  48. package/dist/probe.js +41 -0
  49. package/dist/probe.js.map +1 -0
  50. package/dist/schemas.d.ts +2 -0
  51. package/dist/schemas.d.ts.map +1 -0
  52. package/dist/schemas.js +51 -0
  53. package/dist/schemas.js.map +1 -0
  54. package/dist/unicode15.d.ts +8 -0
  55. package/dist/unicode15.d.ts.map +1 -0
  56. package/dist/unicode15.js +55 -0
  57. package/dist/unicode15.js.map +1 -0
  58. package/package.json +55 -0
@@ -0,0 +1,87 @@
1
+ // identity — the Identity Layer: one root seed, every context derived.
2
+ //
3
+ // A person holds ONE secret. Everything else is derived from it through
4
+ // the ordinary rltp/anchor family: the community anchor that carries the
5
+ // stable social identity, a pair anchor per relationship, a member
6
+ // anchor per group, public personas. No two contexts link to each other
7
+ // unless their holder deliberately discloses the link.
8
+ //
9
+ // The label registry is CLOSED (Identity §6.1, unchanged since 0.13):
10
+ // exactly three label forms derive, and this API rejects every other
11
+ // string before any key derivation — fail closed, no repair. `self`,
12
+ // `recovery`, `carrier` and `device/…` are not labels; recovery and the
13
+ // carrier-relationship identity have their own fixed derivations outside
14
+ // this registry and are deliberately NOT part of this module yet.
15
+ //
16
+ // Fresh-always (Encounter §4.4): a ceremony mints a pair context whose
17
+ // label digests the NONCE BYTES, so nothing correlatable is displayed —
18
+ // not even to someone who scanned the same person yesterday.
19
+ import { toU } from './core.js';
20
+ import { hkdf, edFromSeed, xFromSeed, anchorOfEd, mkOfX, digestBytes } from './crypto.js';
21
+ import { allowed15, WHITE_SPACE_15 } from './unicode15.js';
22
+ // ── the closed registry (Identity §6.1) ─────────────────────────────────
23
+ // A digest component is the ONE canonical u rendering of a sha2-256
24
+ // multihash: 47 characters, no padding, no non-zero trailing bits, no z.
25
+ const digestComponent = (d) => d.length === 47 && d[0] === 'u' && toU(d) === d;
26
+ // §6.2 — the ordered persona-name pipeline. NFC is the one permitted
27
+ // normalization (applied, not rejected); every later check runs on the
28
+ // NFC result — and every Unicode property is evaluated against the
29
+ // PINNED 15.0 data in unicode15.ts, never the platform's tables (review
30
+ // 2, B-1: Node 22 ships Unicode 16 — a post-15.0 code point must not
31
+ // derive an anchor on one runtime and be rejected on another).
32
+ const personaName = (raw) => {
33
+ if (!raw.isWellFormed())
34
+ return null; // no lone surrogates
35
+ const name = raw.normalize('NFC'); // NFC of 15.0-assigned code points is version-stable by Unicode's guarantee
36
+ const cps = [...name];
37
+ if (cps.length < 1 || cps.length > 64)
38
+ return null;
39
+ if (new TextEncoder().encode(name).length > 256)
40
+ return null;
41
+ if (name.includes('/'))
42
+ return null; // labels have exactly the components the registry shows
43
+ for (const c of cps)
44
+ if (!allowed15(c.codePointAt(0)))
45
+ return null; // assigned in 15.0, no Cc/Cf
46
+ if (WHITE_SPACE_15.has(cps[0].codePointAt(0)) || WHITE_SPACE_15.has(cps[cps.length - 1].codePointAt(0)))
47
+ return null;
48
+ return name;
49
+ };
50
+ /**
51
+ * The canonical form of a registry label, or null for everything the
52
+ * closed registry rejects. Personas come back NFC-normalized — for them
53
+ * the canonical label may differ from the input; label equality IS
54
+ * anchor identity, so derivation always uses the canonical form.
55
+ */
56
+ export function canonicalLabel(label) {
57
+ if (typeof label !== 'string')
58
+ return null;
59
+ if (label.startsWith('group/'))
60
+ return digestComponent(label.slice(6)) ? label : null;
61
+ if (label.startsWith('pair/'))
62
+ return digestComponent(label.slice(5)) ? label : null;
63
+ if (label.startsWith('persona/')) {
64
+ const name = personaName(label.slice(8));
65
+ return name === null ? null : 'persona/' + name;
66
+ }
67
+ return null; // self, recovery, carrier, device/…, and every unknown form: fail closed
68
+ }
69
+ export async function labeledContext(rootIkm, label) {
70
+ const canonical = canonicalLabel(label);
71
+ if (canonical === null)
72
+ throw new Error(`not a label of the closed registry (Identity §6.1): ${JSON.stringify(label)}`);
73
+ const ed = await edFromSeed(await hkdf(rootIkm, 'rltp/anchor/ed/' + canonical));
74
+ const x = await xFromSeed(await hkdf(rootIkm, 'rltp/anchor/x/' + canonical));
75
+ return { label: canonical, ed, x, anchor: anchorOfEd(ed.pubRaw), keyAgreement: mkOfX(x.pubRaw) };
76
+ }
77
+ // fresh-always (Encounter §4.4): label = pair/<multihash over the NONCE
78
+ // BYTES>. The nonce is the holder's own, fresh, and exactly 32 bytes —
79
+ // a shorter one weakens the label to below its specified entropy (B-2).
80
+ export async function pairContext(rootIkm, nonce) {
81
+ if (!(nonce instanceof Uint8Array) || nonce.length !== 32)
82
+ throw new Error('pair nonce must be exactly 32 bytes (Identity §6.1)');
83
+ return labeledContext(rootIkm, 'pair/' + await digestBytes(nonce));
84
+ }
85
+ /** The community anchor: an ordinary group context over the community's genesis digest. */
86
+ export const communityContext = (rootIkm, genesisDigest) => labeledContext(rootIkm, 'group/' + genesisDigest);
87
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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","sourcesContent":["// identity — the Identity Layer: one root seed, every context derived.\n//\n// A person holds ONE secret. Everything else is derived from it through\n// the ordinary rltp/anchor family: the community anchor that carries the\n// stable social identity, a pair anchor per relationship, a member\n// anchor per group, public personas. No two contexts link to each other\n// unless their holder deliberately discloses the link.\n//\n// The label registry is CLOSED (Identity §6.1, unchanged since 0.13):\n// exactly three label forms derive, and this API rejects every other\n// string before any key derivation — fail closed, no repair. `self`,\n// `recovery`, `carrier` and `device/…` are not labels; recovery and the\n// carrier-relationship identity have their own fixed derivations outside\n// this registry and are deliberately NOT part of this module yet.\n//\n// Fresh-always (Encounter §4.4): a ceremony mints a pair context whose\n// label digests the NONCE BYTES, so nothing correlatable is displayed —\n// not even to someone who scanned the same person yesterday.\nimport { toU } from './core.js'\nimport { hkdf, edFromSeed, xFromSeed, anchorOfEd, mkOfX, digestBytes } from './crypto.js'\nimport type { KeyPair } from './crypto.js'\nimport { allowed15, WHITE_SPACE_15 } from './unicode15.js'\n\n/**\n * One derived identity context: its label, both key pairs, and the two\n * public forms others see — the did:key anchor and the multikey for key\n * agreement. Contexts never link to each other without a deliberate act.\n */\nexport interface Context {\n label: string\n ed: KeyPair\n x: KeyPair\n anchor: string\n keyAgreement: string\n}\n\n// ── the closed registry (Identity §6.1) ─────────────────────────────────\n// A digest component is the ONE canonical u rendering of a sha2-256\n// multihash: 47 characters, no padding, no non-zero trailing bits, no z.\nconst digestComponent = (d: string): boolean => d.length === 47 && d[0] === 'u' && toU(d) === d\n\n// §6.2 — the ordered persona-name pipeline. NFC is the one permitted\n// normalization (applied, not rejected); every later check runs on the\n// NFC result — and every Unicode property is evaluated against the\n// PINNED 15.0 data in unicode15.ts, never the platform's tables (review\n// 2, B-1: Node 22 ships Unicode 16 — a post-15.0 code point must not\n// derive an anchor on one runtime and be rejected on another).\nconst personaName = (raw: string): string | null => {\n if (!(raw as unknown as { isWellFormed(): boolean }).isWellFormed()) return null // no lone surrogates\n const name = raw.normalize('NFC') // NFC of 15.0-assigned code points is version-stable by Unicode's guarantee\n const cps = [...name]\n if (cps.length < 1 || cps.length > 64) return null\n if (new TextEncoder().encode(name).length > 256) return null\n if (name.includes('/')) return null // labels have exactly the components the registry shows\n for (const c of cps) if (!allowed15(c.codePointAt(0)!)) return null // assigned in 15.0, no Cc/Cf\n if (WHITE_SPACE_15.has(cps[0]!.codePointAt(0)!) || WHITE_SPACE_15.has(cps[cps.length - 1]!.codePointAt(0)!)) return null\n return name\n}\n\n/**\n * The canonical form of a registry label, or null for everything the\n * closed registry rejects. Personas come back NFC-normalized — for them\n * the canonical label may differ from the input; label equality IS\n * anchor identity, so derivation always uses the canonical form.\n */\nexport function canonicalLabel (label: string): string | null {\n if (typeof label !== 'string') return null\n if (label.startsWith('group/')) return digestComponent(label.slice(6)) ? label : null\n if (label.startsWith('pair/')) return digestComponent(label.slice(5)) ? label : null\n if (label.startsWith('persona/')) {\n const name = personaName(label.slice(8))\n return name === null ? null : 'persona/' + name\n }\n return null // self, recovery, carrier, device/…, and every unknown form: fail closed\n}\n\nexport async function labeledContext (rootIkm: Uint8Array, label: string): Promise<Context> {\n const canonical = canonicalLabel(label)\n if (canonical === null) throw new Error(`not a label of the closed registry (Identity §6.1): ${JSON.stringify(label)}`)\n const ed = await edFromSeed(await hkdf(rootIkm, 'rltp/anchor/ed/' + canonical))\n const x = await xFromSeed(await hkdf(rootIkm, 'rltp/anchor/x/' + canonical))\n return { label: canonical, ed, x, anchor: anchorOfEd(ed.pubRaw), keyAgreement: mkOfX(x.pubRaw) }\n}\n\n// fresh-always (Encounter §4.4): label = pair/<multihash over the NONCE\n// BYTES>. The nonce is the holder's own, fresh, and exactly 32 bytes —\n// a shorter one weakens the label to below its specified entropy (B-2).\nexport async function pairContext (rootIkm: Uint8Array, nonce: Uint8Array): Promise<Context> {\n if (!(nonce instanceof Uint8Array) || nonce.length !== 32) throw new Error('pair nonce must be exactly 32 bytes (Identity §6.1)')\n return labeledContext(rootIkm, 'pair/' + await digestBytes(nonce))\n}\n\n/** The community anchor: an ordinary group context over the community's genesis digest. */\nexport const communityContext = (rootIkm: Uint8Array, genesisDigest: string): Promise<Context> =>\n labeledContext(rootIkm, 'group/' + genesisDigest)\n"]}
@@ -0,0 +1,6 @@
1
+ export * from './core.js';
2
+ export * from './crypto.js';
3
+ export * from './identity.js';
4
+ export * from './encounter.js';
5
+ export * from './delivery.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwBA,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ // @real-life/trust-protocol — the cryptographic core of RLTP.
2
+ //
3
+ // What this entry point IS: the wire-level primitives of the converged
4
+ // layers, reproducing the shipped test vectors byte for byte — the
5
+ // canonical form, the derivation of identity contexts under the closed
6
+ // label registry, the encounter wire builders, the sealed envelope and
7
+ // the generic receive chain.
8
+ //
9
+ // What it is NOT (yet): the layers' state machines. Identity's recovery
10
+ // and carrier derivations, Encounter's ceremony record and acceptance
11
+ // machinery, Delivery's type-specific stages and residual bookkeeping
12
+ // live in the specification and its conformance fixtures, not here. A
13
+ // conformant implementation builds ON this module; importing it is not
14
+ // yet conformance. The README states per module which casting's wire
15
+ // forms it implements.
16
+ //
17
+ // core the form every artifact obeys
18
+ // crypto the primitives, and the Data Integrity proof
19
+ // identity context derivation under the closed registry (§6.1/§6.2)
20
+ // encounter the ceremony's wire forms: challenges, cards, credentials
21
+ // delivery the sealed envelope and the generic receive stages 1–4
22
+ //
23
+ // Specification, schemas and vectors: https://rltp.real-life.org
24
+ export * from './core.js';
25
+ export * from './crypto.js';
26
+ export * from './identity.js';
27
+ export * from './encounter.js';
28
+ export * from './delivery.js';
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,uEAAuE;AACvE,mEAAmE;AACnE,uEAAuE;AACvE,uEAAuE;AACvE,6BAA6B;AAC7B,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,uBAAuB;AACvB,EAAE;AACF,6CAA6C;AAC7C,4DAA4D;AAC5D,wEAAwE;AACxE,yEAAyE;AACzE,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AAEjE,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA","sourcesContent":["// @real-life/trust-protocol — the cryptographic core of RLTP.\n//\n// What this entry point IS: the wire-level primitives of the converged\n// layers, reproducing the shipped test vectors byte for byte — the\n// canonical form, the derivation of identity contexts under the closed\n// label registry, the encounter wire builders, the sealed envelope and\n// the generic receive chain.\n//\n// What it is NOT (yet): the layers' state machines. Identity's recovery\n// and carrier derivations, Encounter's ceremony record and acceptance\n// machinery, Delivery's type-specific stages and residual bookkeeping\n// live in the specification and its conformance fixtures, not here. A\n// conformant implementation builds ON this module; importing it is not\n// yet conformance. The README states per module which casting's wire\n// forms it implements.\n//\n// core the form every artifact obeys\n// crypto the primitives, and the Data Integrity proof\n// identity context derivation under the closed registry (§6.1/§6.2)\n// encounter the ceremony's wire forms: challenges, cards, credentials\n// delivery the sealed envelope and the generic receive stages 1–4\n//\n// Specification, schemas and vectors: https://rltp.real-life.org\n\nexport * from './core.js'\nexport * from './crypto.js'\nexport * from './identity.js'\nexport * from './encounter.js'\nexport * from './delivery.js'\n"]}
@@ -0,0 +1,99 @@
1
+ import * as C from './deps.js';
2
+ import type { Person } from './deps.js';
3
+ export declare const snapshotPriors: (p: Person) => {
4
+ own: any[];
5
+ counterpart: string[];
6
+ };
7
+ export declare function buildProbe(p: Person, newKey: string, ent?: any): Promise<{
8
+ to: any;
9
+ kind: string;
10
+ env: C.Envelope;
11
+ }[] | null>;
12
+ export declare function receiveContinuity(p: Person, env: any, when: number, ent?: any): Promise<{
13
+ handled: boolean;
14
+ partial: boolean;
15
+ error?: undefined;
16
+ matches?: undefined;
17
+ chained?: undefined;
18
+ name?: undefined;
19
+ outbound?: undefined;
20
+ outboundError?: undefined;
21
+ } | {
22
+ handled: boolean;
23
+ matches: number;
24
+ error?: undefined;
25
+ partial?: undefined;
26
+ chained?: undefined;
27
+ name?: undefined;
28
+ outbound?: undefined;
29
+ outboundError?: undefined;
30
+ } | {
31
+ handled: boolean;
32
+ matches: number;
33
+ chained: boolean;
34
+ name: any;
35
+ outbound: {
36
+ to: any;
37
+ kind: string;
38
+ env: C.Envelope;
39
+ }[];
40
+ outboundError: string | undefined;
41
+ error?: undefined;
42
+ partial?: undefined;
43
+ } | {
44
+ handled: boolean;
45
+ error: string;
46
+ chained?: undefined;
47
+ name?: undefined;
48
+ outbound?: undefined;
49
+ outboundError?: undefined;
50
+ } | {
51
+ handled: boolean;
52
+ chained: boolean;
53
+ name: any;
54
+ outbound: {
55
+ to: any;
56
+ kind: string;
57
+ env: C.Envelope;
58
+ }[];
59
+ outboundError: string;
60
+ error?: undefined;
61
+ } | {
62
+ handled: boolean;
63
+ chained: boolean;
64
+ name: any;
65
+ outbound: {
66
+ to: any;
67
+ kind: string;
68
+ env: C.Envelope;
69
+ }[];
70
+ error?: undefined;
71
+ outboundError?: undefined;
72
+ } | {
73
+ handled: boolean;
74
+ doc: any;
75
+ } | {
76
+ handled: boolean;
77
+ duplicate: boolean;
78
+ } | {
79
+ handled: boolean;
80
+ duplicate?: undefined;
81
+ }>;
82
+ /**
83
+ * The host's producing call for a mapping the receiver still owes
84
+ * (review 6, B-3): a transient seal failure during probe/mapping
85
+ * handling persists the obligation in contMappingDue; this call — not a
86
+ * redelivery, which the freshness rule forbids — builds and returns it.
87
+ */
88
+ export declare function flushContinuity(p: Person, newKey: string, when: number, ent?: any): Promise<{
89
+ outbound: never[];
90
+ pending: boolean;
91
+ } | {
92
+ outbound: {
93
+ to: any;
94
+ kind: string;
95
+ env: C.Envelope;
96
+ }[];
97
+ pending?: undefined;
98
+ }>;
99
+ //# sourceMappingURL=continuity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continuity.d.ts","sourceRoot":"","sources":["../../src/probe/continuity.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAiBvC,eAAO,MAAM,cAAc,GAAI,GAAG,MAAM;;;CAGtC,CAAA;AAGF,wBAAsB,UAAU,CAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,GAAQ;;;;YAkBzE;AAgDD,wBAAsB,iBAAiB,CAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,GAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOxF;AAsGD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,GAAQ;;;;;;;;;;GAkB5F"}
@@ -0,0 +1,303 @@
1
+ // continuity.mjs — rltp-visibility §6a im Simulator: continuity-probe@1 +
2
+ // continuity-mapping@1. „Kontinuität wird NACH der Zeremonie aufgelöst,
3
+ // nie in ihr" (Encounter 4.4): jedes Enactment UND jede Vorstellung prägt
4
+ // ein frisches Tupel; ob es ein Wiedersehen war, klärt die Probe über den
5
+ // frischen Kanal — automatisch, ohne Dialog, ohne Namen.
6
+ //
7
+ // Die Probe ist das dritte Mitglied EINER Familie (Stern, Mapping, Probe:
8
+ // geblendete Werte unter Beziehungs-Schlüsseln): die EIGENEN bisherigen
9
+ // pair-Anker reisen als HMAC unter k_p (DH des NEUEN Kanals), gepolstert
10
+ // auf Vielfache von 256, sortiert, sequenziert. Nur wer eine der
11
+ // Beziehungen wirklich hält, kann matchen; ein leerer Bestand sendet
12
+ // trotzdem (sonst leakte die Abwesenheit die Kardinalität „null").
13
+ //
14
+ // Die Maschine (6a.4): EIN Wähler (Record-Seite = lexikographisch
15
+ // kleinerer neuer pair-Anker), EINE endgültige Wahl (eingefroren,
16
+ // Abweichung = Äquivokation), EIN Auslöser (Record-Seite kettet atomar
17
+ // mit ihrer Wahl; die Gegenseite NUR auf deren verifiziertes Mapping —
18
+ // ihr eigenes ist ein Match-Report). Kette: neues Tupel wird Kopf, das
19
+ // alte deaktiviert; Evidenz akkumuliert AUF DER BEZIEHUNG (✓ bleibt ✓),
20
+ // für Dritte unsichtbar. Wer die Probe nicht beantworten kann, IST
21
+ // protokollisch eine neue Beziehung (Identity 9.3).
22
+ import { jcs } from '../core.js';
23
+ import * as C from './deps.js';
24
+ import { hmac } from './trust.js';
25
+ const say = (p, m) => p.log.push(m);
26
+ const CHUNK = 256;
27
+ // ── Schlüssel (Spec-Infostrings, richtungsgebunden) ─────────────────────
28
+ // k_p = HKDF(ECDH(newPairX_sender, newPairX_recipient),
29
+ // 'rltp/visibility/blind/probe/' + senderNewPair + '/' + recipientNewPair)
30
+ const kProbe = async (xPriv, theirKa, senderAnchor, recipientAnchor) => C.hkdf(await C.ecdh(xPriv, C.xRawOfMk(theirKa)), 'rltp/visibility/blind/probe/' + senderAnchor + '/' + recipientAnchor);
31
+ const kMac = async (xPriv, theirKa, info) => C.hkdf(await C.ecdh(xPriv, C.xRawOfMk(theirKa)), info);
32
+ // ── Schnappschuss-Helfer (Prior-Candidate-Set, Sicht des Halters) ───────
33
+ // wird von introduce.mjs BEI TUPEL-ERZEUGUNG aufgerufen: aktive Heads,
34
+ // das frische Tupel selbst ausgeschlossen; deaktivierte Glieder nie.
35
+ export const snapshotPriors = (p) => ({
36
+ own: [...p.contacts.values()].filter((c) => !c.deactivated && c.channel?.own).map((c) => c.channel.own.anchor),
37
+ counterpart: [...p.contacts.entries()].filter(([, c]) => !c.deactivated).map(([k]) => k),
38
+ });
39
+ // ── continuity-probe@1: bauen ───────────────────────────────────────────
40
+ export async function buildProbe(p, newKey, ent = {}) {
41
+ const t = p.contacts.get(newKey);
42
+ if (!t?.channel?.own)
43
+ return null;
44
+ t.probeSeqOut = (t.probeSeqOut ?? 0) + 1;
45
+ const kp = await kProbe(t.channel.own.x.priv, t.channel.counterpartKa, t.channel.own.anchor, newKey);
46
+ const entries = [];
47
+ for (const a of t.priorCands?.own ?? [])
48
+ entries.push(await hmac(kp, a));
49
+ // Polstern auf das nächste Vielfache von 256 (leerer Bestand → 1 Voll-Polster-Chunk)
50
+ const total = Math.max(CHUNK, Math.ceil(entries.length / CHUNK) * CHUNK);
51
+ while (entries.length < total)
52
+ entries.push(C.b64uOf(ent.pad ?? C.rand(32)));
53
+ entries.sort();
54
+ const outbound = [];
55
+ for (let i = 0; i * CHUNK < entries.length; i++) {
56
+ const body = { type: 'continuity-probe@1', probe: String(t.probeSeqOut), seq: String(i + 1), last: (i + 1) * CHUNK >= entries.length, blinded: entries.slice(i * CHUNK, (i + 1) * CHUNK) };
57
+ outbound.push({ to: t, kind: 'continuity-probe@1 (geblendet, gepolstert, versiegelt)', env: await C.seal({ ...body, mac: await hmac(kp, jcs(body)) }, t.channel.counterpartKa, ent) });
58
+ }
59
+ say(p, `Kontinuitäts-Probe an ${t.name}: ${t.priorCands?.own.length ?? 0} eigene Beziehungs-Anker, geblendet + gepolstert (${entries.length})`);
60
+ return outbound;
61
+ }
62
+ // ── continuity-mapping@1: bauen ─────────────────────────────────────────
63
+ // prior = EIGENER alter pair-Anker · next = eigener neuer · to = neuer der
64
+ // Gegenseite; mac1 unter dem ALTEN Beziehungs-Schlüssel, mac2 unter dem
65
+ // NEUEN — nur derselbe Schlüssel-Inhaber kann beide (für die Adressatin
66
+ // fälschbar: Klasse V, abstreitbar).
67
+ async function buildMapping(p, newKey, oldKey, when, ent = {}) {
68
+ const t = p.contacts.get(newKey), old = p.contacts.get(oldKey);
69
+ const body = { type: 'continuity-mapping@1', prior: old.channel.own.anchor, next: t.channel.own.anchor, to: newKey, revision: '1', issuedAt: C.iso(when) };
70
+ const msg = jcs(body);
71
+ const doc = { ...body,
72
+ mac1: await hmac(await kMac(old.channel.own.x.priv, old.channel.counterpartKa, 'rltp/visibility/mac/cont1'), msg),
73
+ mac2: await hmac(await kMac(t.channel.own.x.priv, t.channel.counterpartKa, 'rltp/visibility/mac/cont2'), msg) };
74
+ return { to: t, kind: 'continuity-mapping@1 (mac1 alt · mac2 neu, versiegelt)', env: await C.seal(doc, t.channel.counterpartKa, ent) };
75
+ }
76
+ // ── die Kette: neues Tupel wird Kopf, das alte deaktiviert ──────────────
77
+ // Tupel-Ebene bleibt (Stern-Salts, Probe-Sequenzen); KETTEN-Ebene wandert:
78
+ // Kontakt-Gedächtnis, Evidenz (✓ akkumuliert), Offenlegungen, Gruppen.
79
+ function chainTuple(p, newKey, oldKey) {
80
+ const head = p.contacts.get(newKey), old = p.contacts.get(oldKey);
81
+ if (!head || !old || old.deactivated)
82
+ return false; // idempotent
83
+ old.deactivated = true;
84
+ old.chainedInto = newKey;
85
+ head.chain = [...(old.chain ?? []), oldKey];
86
+ if (old.state === '✓' || head.state === '✓') {
87
+ head.state = '✓';
88
+ head.provenance = 'ceremony';
89
+ }
90
+ else if (old.state === '⇄' || head.state === '⇄')
91
+ head.state = '⇄';
92
+ head.since = [old.since, head.since].filter(Boolean).sort()[0];
93
+ head.name = old.name; // lokales Kontakt-Gedächtnis
94
+ head.selfAnchor ??= old.selfAnchor;
95
+ head.mapping ??= old.mapping;
96
+ head.trustGiven ??= old.trustGiven;
97
+ head.trustReceived ??= old.trustReceived;
98
+ head.trustPaused ??= old.trustPaused;
99
+ head.sentMapping ??= old.sentMapping;
100
+ head.starReceived ??= old.starReceived;
101
+ head.starInfo ??= old.starInfo;
102
+ head.sharedGroups = [...new Set([...(old.sharedGroups ?? []), ...(head.sharedGroups ?? [])])];
103
+ say(p, `Kontinuität: ${head.name} wiedererkannt — neues Tupel an die Beziehung gekettet (${head.state}, ${head.chain.length + 1} Enactments)`);
104
+ return true;
105
+ }
106
+ // ── Empfangs-Dispatch ───────────────────────────────────────────────────
107
+ const iAmRecord = (t, newKey) => t.channel.own.anchor < newKey; // lexikographisch kleinerer NEUER Anker
108
+ const tupleOf = (p, rkid) => {
109
+ const ctx = p.contexts.get(rkid);
110
+ if (!ctx)
111
+ return null;
112
+ const e = [...p.contacts.entries()].find(([, c]) => c.channel?.own?.anchor === ctx.anchor);
113
+ return e ? { key: e[0], t: e[1], ctx } : null;
114
+ };
115
+ export async function receiveContinuity(p, env, when, ent = {}) {
116
+ const opened = await C.openEnvelope(p, env); // Stufen 1–4, Cache-Lesung
117
+ if (opened.duplicate)
118
+ return { handled: true, duplicate: true };
119
+ if (opened.error)
120
+ return { handled: false };
121
+ const r = await receiveContinuityInner(p, env, opened.doc, when, ent);
122
+ if (r?.handled && !r.error)
123
+ C.effectDone(p, opened.digest);
124
+ return r;
125
+ }
126
+ async function receiveContinuityInner(p, env, doc, when, ent = {}) {
127
+ const hit = tupleOf(p, env.rkid);
128
+ if (!hit)
129
+ return { handled: false };
130
+ if (doc?.type === 'continuity-probe@1')
131
+ return handleProbe(p, hit, doc, when, ent);
132
+ if (doc?.type === 'continuity-mapping@1')
133
+ return handleMapping(p, hit, doc, when, ent);
134
+ return { handled: false, doc };
135
+ }
136
+ async function handleProbe(p, { key, t }, doc, when, ent) {
137
+ // form BEFORE fields (M-3): mac, sequence and chunk list as used below
138
+ if (!C.shaped(doc, { mac: 'string', probe: 'string', seq: 'string', blinded: 'array' }) || typeof doc.last !== 'boolean'
139
+ || !C.intStr(doc.probe) || !C.intStr(doc.seq) // NaN in probeSeqIn wäre Replay-Zustandskorruption (Review 9, P-B1)
140
+ || doc.blinded.length !== 256 // die Chunkform ist FEST: genau 256 Einträge (Review 10, P-B1-Rest)
141
+ || !doc.blinded.every((b) => typeof b === 'string'))
142
+ return { handled: true, error: 'malformed probe' };
143
+ // Senderseitiger k_p, aus Empfängersicht nachgerechnet (Richtung!)
144
+ const kp = await kProbe(t.channel.own.x.priv, t.channel.counterpartKa, key, t.channel.own.anchor);
145
+ const { mac, ...body } = doc;
146
+ if ((await hmac(kp, jcs(body))) !== mac)
147
+ return { handled: true, error: 'probe mac' };
148
+ if (BigInt(doc.probe) <= BigInt(t.probeSeqIn ?? '0'))
149
+ return { handled: true, error: 'probe replay (nicht streng größer)' };
150
+ // Chunk-Sammlung pro (Tupel, probe); höhere probe verwirft die offene
151
+ if (t.probeAsm?.probe !== doc.probe)
152
+ t.probeAsm = { probe: doc.probe, blinded: [], next: 1 };
153
+ if (Number(doc.seq) !== t.probeAsm.next)
154
+ return { handled: true, error: 'malformed probe (chunk order)' };
155
+ t.probeAsm.next += 1;
156
+ t.probeAsm.blinded.push(...doc.blinded);
157
+ if (!doc.last)
158
+ return { handled: true, partial: true };
159
+ // finaler Chunk: union lesen, dann vollzieht sich der EFFEKT (consume
160
+ // + Wahl + Kette) VOR dem fehlbaren Mapping-Bau — bewusst, denn die
161
+ // Frische-Regel verbietet Chunk-Replays als Retry-Weg; ein Bau-Fehler
162
+ // wird als getypte Pflicht persistiert und über flushContinuity()
163
+ // eingelöst (Reviews 5–8)
164
+ const union = t.probeAsm.blinded;
165
+ const consume = () => { t.probeAsm = null; t.probeSeqIn = doc.probe; }; // als STRING — BigInt-Vergleich, 18 Stellen exakt
166
+ // Schnitt gegen die GEGENSEITEN-Anker des eigenen Prior-Candidate-Sets
167
+ const matches = [];
168
+ for (const cp of t.priorCands?.counterpart ?? []) {
169
+ if (union.includes(await hmac(kp, cp)))
170
+ matches.push(cp);
171
+ }
172
+ if (!matches.length) {
173
+ consume();
174
+ say(p, `Probe von ${t.name}: kein Match — ehrlich eine neue Beziehung`);
175
+ return { handled: true, matches: 0 };
176
+ }
177
+ const outbound = [];
178
+ let chained = false;
179
+ let outboundError;
180
+ if (iAmRecord(t, key)) {
181
+ // Record-Seite: einmal wählen, endgültig; Kette atomar mit der Wahl
182
+ if (!t.contChosen) {
183
+ const choice = matches[0]; // Wahl frei — hier: erster Match (reichste Historie wäre App-Sache)
184
+ consume();
185
+ t.contChosen = choice;
186
+ chained = chainTuple(p, key, choice);
187
+ try {
188
+ outbound.push(await buildMapping(p, key, choice, when, ent));
189
+ }
190
+ catch (err) {
191
+ t.contMappingDue = { prior: choice, kind: 'choice' };
192
+ outboundError = String(err?.message ?? err);
193
+ }
194
+ }
195
+ else
196
+ consume();
197
+ }
198
+ else {
199
+ // Nicht-Record: Match-Report (löst NIE eine Kette aus)
200
+ if (!t.contReported) {
201
+ consume();
202
+ t.contReported = matches[0];
203
+ try {
204
+ outbound.push(await buildMapping(p, key, matches[0], when, ent));
205
+ }
206
+ catch (err) {
207
+ t.contMappingDue = { prior: matches[0], kind: 'report' };
208
+ outboundError = String(err?.message ?? err);
209
+ }
210
+ }
211
+ else
212
+ consume();
213
+ }
214
+ return { handled: true, matches: matches.length, chained, name: chained ? t.name : undefined, outbound, outboundError };
215
+ }
216
+ async function handleMapping(p, { key, t }, doc, when, ent) {
217
+ if (!C.shaped(doc, { mac1: 'string', mac2: 'string', prior: 'string', next: 'string', to: 'string' }))
218
+ return { handled: true, error: 'malformed mapping' };
219
+ const { mac1, mac2, ...body } = doc;
220
+ const fail = (why) => { say(p, `Kontinuitäts-Mapping verworfen: ${why}`); return { handled: true, error: why }; };
221
+ if (body.to !== t.channel.own.anchor)
222
+ return fail('to ≠ eigener neuer Anker');
223
+ if (body.next !== key)
224
+ return fail('next ≠ neuer Anker der Gegenseite');
225
+ // prior MUSS im Schnappschuss liegen (nie das frische Tupel, nie tiefere Kettenglieder)
226
+ if (!(t.priorCands?.counterpart ?? []).includes(body.prior))
227
+ return fail('prior nicht im Prior-Candidate-Set');
228
+ const old = p.contacts.get(body.prior);
229
+ if (!old?.channel?.own)
230
+ return fail('alte Beziehung ohne eigenen Kontext');
231
+ const msg = jcs(body);
232
+ if ((await hmac(await kMac(old.channel.own.x.priv, old.channel.counterpartKa, 'rltp/visibility/mac/cont1'), msg)) !== mac1)
233
+ return fail('mac1 (alter Beziehungs-Schlüssel)');
234
+ if ((await hmac(await kMac(t.channel.own.x.priv, t.channel.counterpartKa, 'rltp/visibility/mac/cont2'), msg)) !== mac2)
235
+ return fail('mac2 (neuer Beziehungs-Schlüssel)');
236
+ const senderIsRecord = key < t.channel.own.anchor;
237
+ const outbound = [];
238
+ if (senderIsRecord) {
239
+ // Record-Freeze: abweichendes prior derselben (next,to) = Äquivokation
240
+ if (t.contRecordPrior && t.contRecordPrior !== body.prior)
241
+ return fail('Äquivokation — Record-Wahl ist eingefroren');
242
+ // fehlbare Arbeit VOR der Mutation (B-2): erst das Alignment-Mapping
243
+ // bauen, dann Freeze + Kette in einem synchronen Zug
244
+ t.contRecordPrior = body.prior;
245
+ chainTuple(p, key, body.prior);
246
+ if (!t.contAligned) {
247
+ try {
248
+ const mapping = await buildMapping(p, key, body.prior, when, ent);
249
+ t.contAligned = true;
250
+ if (t.contMappingDue?.prior === body.prior)
251
+ delete t.contMappingDue;
252
+ outbound.push(mapping);
253
+ }
254
+ catch (err) {
255
+ t.contMappingDue = { prior: body.prior, kind: 'align' };
256
+ return { handled: true, chained: p.contacts.get(body.prior)?.deactivated === true, name: t.name, outbound, outboundError: String(err?.message ?? err) };
257
+ }
258
+ }
259
+ }
260
+ else {
261
+ // Match-Report der Nicht-Record-Seite: darf die Wahl INFORMIEREN, kettet nie
262
+ if (iAmRecord(t, key) && !t.contChosen) {
263
+ const mapping = await buildMapping(p, key, body.prior, when, ent);
264
+ t.contChosen = body.prior;
265
+ chainTuple(p, key, body.prior);
266
+ outbound.push(mapping);
267
+ }
268
+ }
269
+ return { handled: true, chained: p.contacts.get(body.prior)?.deactivated === true, name: t.name, outbound };
270
+ }
271
+ /**
272
+ * The host's producing call for a mapping the receiver still owes
273
+ * (review 6, B-3): a transient seal failure during probe/mapping
274
+ * handling persists the obligation in contMappingDue; this call — not a
275
+ * redelivery, which the freshness rule forbids — builds and returns it.
276
+ */
277
+ export async function flushContinuity(p, newKey, when, ent = {}) {
278
+ const t = p.contacts.get(newKey);
279
+ if (!t?.contMappingDue)
280
+ return { outbound: [] };
281
+ // Single-Flight (Review 7, B-3): zwei parallele Flushes liefern nie
282
+ // zwei Mappings für EINE Pflicht
283
+ if (t.contFlushPending)
284
+ return { outbound: [], pending: true };
285
+ t.contFlushPending = true;
286
+ try {
287
+ const due = t.contMappingDue;
288
+ const mapping = await buildMapping(p, newKey, due.prior, when, ent);
289
+ // der Flush stellt GENAU den Zustand her, den der gelungene
290
+ // Direktpfad hinterlassen hätte — kind-bewusst. Und er löscht NUR
291
+ // die Pflicht, die er selbst gebaut hat: ist während des await eine
292
+ // NEUERE an ihre Stelle getreten, bleibt sie stehen (Review 8, B-3)
293
+ if (due.kind === 'align')
294
+ t.contAligned = true;
295
+ if (t.contMappingDue === due)
296
+ delete t.contMappingDue;
297
+ return { outbound: [mapping] };
298
+ }
299
+ finally {
300
+ t.contFlushPending = false;
301
+ }
302
+ }
303
+ //# sourceMappingURL=continuity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continuity.js","sourceRoot":"","sources":["../../src/probe/continuity.ts"],"names":[],"mappings":"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","sourcesContent":["// continuity.mjs — rltp-visibility §6a im Simulator: continuity-probe@1 +\n// continuity-mapping@1. „Kontinuität wird NACH der Zeremonie aufgelöst,\n// nie in ihr\" (Encounter 4.4): jedes Enactment UND jede Vorstellung prägt\n// ein frisches Tupel; ob es ein Wiedersehen war, klärt die Probe über den\n// frischen Kanal — automatisch, ohne Dialog, ohne Namen.\n//\n// Die Probe ist das dritte Mitglied EINER Familie (Stern, Mapping, Probe:\n// geblendete Werte unter Beziehungs-Schlüsseln): die EIGENEN bisherigen\n// pair-Anker reisen als HMAC unter k_p (DH des NEUEN Kanals), gepolstert\n// auf Vielfache von 256, sortiert, sequenziert. Nur wer eine der\n// Beziehungen wirklich hält, kann matchen; ein leerer Bestand sendet\n// trotzdem (sonst leakte die Abwesenheit die Kardinalität „null\").\n//\n// Die Maschine (6a.4): EIN Wähler (Record-Seite = lexikographisch\n// kleinerer neuer pair-Anker), EINE endgültige Wahl (eingefroren,\n// Abweichung = Äquivokation), EIN Auslöser (Record-Seite kettet atomar\n// mit ihrer Wahl; die Gegenseite NUR auf deren verifiziertes Mapping —\n// ihr eigenes ist ein Match-Report). Kette: neues Tupel wird Kopf, das\n// alte deaktiviert; Evidenz akkumuliert AUF DER BEZIEHUNG (✓ bleibt ✓),\n// für Dritte unsichtbar. Wer die Probe nicht beantworten kann, IST\n// protokollisch eine neue Beziehung (Identity 9.3).\nimport { jcs } from '../core.js'\nimport * as C from './deps.js'\nimport type { Person } from './deps.js'\nimport { hmac } from './trust.js'\n\nconst say = (p: Person, m: string) => p.log.push(m)\nconst CHUNK = 256\n\n// ── Schlüssel (Spec-Infostrings, richtungsgebunden) ─────────────────────\n// k_p = HKDF(ECDH(newPairX_sender, newPairX_recipient),\n// 'rltp/visibility/blind/probe/' + senderNewPair + '/' + recipientNewPair)\nconst kProbe = async (xPriv: CryptoKey, theirKa: string, senderAnchor: string, recipientAnchor: string) =>\n C.hkdf(await C.ecdh(xPriv, C.xRawOfMk(theirKa)!), 'rltp/visibility/blind/probe/' + senderAnchor + '/' + recipientAnchor)\nconst kMac = async (xPriv: CryptoKey, theirKa: string, info: string) =>\n C.hkdf(await C.ecdh(xPriv, C.xRawOfMk(theirKa)!), info)\n\n// ── Schnappschuss-Helfer (Prior-Candidate-Set, Sicht des Halters) ───────\n// wird von introduce.mjs BEI TUPEL-ERZEUGUNG aufgerufen: aktive Heads,\n// das frische Tupel selbst ausgeschlossen; deaktivierte Glieder nie.\nexport const snapshotPriors = (p: Person) => ({\n own: [...p.contacts.values()].filter((c) => !c.deactivated && c.channel?.own).map((c) => c.channel.own.anchor),\n counterpart: [...p.contacts.entries()].filter(([, c]) => !c.deactivated).map(([k]) => k),\n})\n\n// ── continuity-probe@1: bauen ───────────────────────────────────────────\nexport async function buildProbe (p: Person, newKey: string, ent: any = {}) {\n const t = p.contacts.get(newKey)\n if (!t?.channel?.own) return null\n t.probeSeqOut = (t.probeSeqOut ?? 0) + 1\n const kp = await kProbe(t.channel.own.x.priv, t.channel.counterpartKa, t.channel.own.anchor, newKey)\n const entries = []\n for (const a of t.priorCands?.own ?? []) entries.push(await hmac(kp, a))\n // Polstern auf das nächste Vielfache von 256 (leerer Bestand → 1 Voll-Polster-Chunk)\n const total = Math.max(CHUNK, Math.ceil(entries.length / CHUNK) * CHUNK)\n while (entries.length < total) entries.push(C.b64uOf(ent.pad ?? C.rand(32)))\n entries.sort()\n const outbound = []\n for (let i = 0; i * CHUNK < entries.length; i++) {\n const body = { type: 'continuity-probe@1', probe: String(t.probeSeqOut), seq: String(i + 1), last: (i + 1) * CHUNK >= entries.length, blinded: entries.slice(i * CHUNK, (i + 1) * CHUNK) }\n outbound.push({ to: t, kind: 'continuity-probe@1 (geblendet, gepolstert, versiegelt)', env: await C.seal({ ...body, mac: await hmac(kp, jcs(body)) }, t.channel.counterpartKa, ent) })\n }\n say(p, `Kontinuitäts-Probe an ${t.name}: ${t.priorCands?.own.length ?? 0} eigene Beziehungs-Anker, geblendet + gepolstert (${entries.length})`)\n return outbound\n}\n\n// ── continuity-mapping@1: bauen ─────────────────────────────────────────\n// prior = EIGENER alter pair-Anker · next = eigener neuer · to = neuer der\n// Gegenseite; mac1 unter dem ALTEN Beziehungs-Schlüssel, mac2 unter dem\n// NEUEN — nur derselbe Schlüssel-Inhaber kann beide (für die Adressatin\n// fälschbar: Klasse V, abstreitbar).\nasync function buildMapping (p: Person, newKey: string, oldKey: string, when: number, ent: any = {}) {\n const t = p.contacts.get(newKey), old = p.contacts.get(oldKey)\n const body = { type: 'continuity-mapping@1', prior: old.channel.own.anchor, next: t.channel.own.anchor, to: newKey, revision: '1', issuedAt: C.iso(when) }\n const msg = jcs(body)\n const doc = { ...body,\n mac1: await hmac(await kMac(old.channel.own.x.priv, old.channel.counterpartKa, 'rltp/visibility/mac/cont1'), msg),\n mac2: await hmac(await kMac(t.channel.own.x.priv, t.channel.counterpartKa, 'rltp/visibility/mac/cont2'), msg) }\n return { to: t, kind: 'continuity-mapping@1 (mac1 alt · mac2 neu, versiegelt)', env: await C.seal(doc, t.channel.counterpartKa, ent) }\n}\n\n// ── die Kette: neues Tupel wird Kopf, das alte deaktiviert ──────────────\n// Tupel-Ebene bleibt (Stern-Salts, Probe-Sequenzen); KETTEN-Ebene wandert:\n// Kontakt-Gedächtnis, Evidenz (✓ akkumuliert), Offenlegungen, Gruppen.\nfunction chainTuple (p: Person, newKey: string, oldKey: string) {\n const head = p.contacts.get(newKey), old = p.contacts.get(oldKey)\n if (!head || !old || old.deactivated) return false // idempotent\n old.deactivated = true\n old.chainedInto = newKey\n head.chain = [...(old.chain ?? []), oldKey]\n if (old.state === '✓' || head.state === '✓') { head.state = '✓'; head.provenance = 'ceremony' }\n else if (old.state === '⇄' || head.state === '⇄') head.state = '⇄'\n head.since = [old.since, head.since].filter(Boolean).sort()[0]\n head.name = old.name // lokales Kontakt-Gedächtnis\n head.selfAnchor ??= old.selfAnchor; head.mapping ??= old.mapping\n head.trustGiven ??= old.trustGiven; head.trustReceived ??= old.trustReceived\n head.trustPaused ??= old.trustPaused; head.sentMapping ??= old.sentMapping\n head.starReceived ??= old.starReceived; head.starInfo ??= old.starInfo\n head.sharedGroups = [...new Set([...(old.sharedGroups ?? []), ...(head.sharedGroups ?? [])])]\n say(p, `Kontinuität: ${head.name} wiedererkannt — neues Tupel an die Beziehung gekettet (${head.state}, ${head.chain.length + 1} Enactments)`)\n return true\n}\n\n// ── Empfangs-Dispatch ───────────────────────────────────────────────────\nconst iAmRecord = (t: any, newKey: string) => t.channel.own.anchor < newKey // lexikographisch kleinerer NEUER Anker\nconst tupleOf = (p: Person, rkid: string) => {\n const ctx = p.contexts.get(rkid)\n if (!ctx) return null\n const e = [...p.contacts.entries()].find(([, c]) => c.channel?.own?.anchor === ctx.anchor)\n return e ? { key: e[0], t: e[1], ctx } : null\n}\n\nexport async function receiveContinuity (p: Person, env: any, when: number, ent: any = {}) {\n const opened = await C.openEnvelope(p, env) // Stufen 1–4, Cache-Lesung\n if (opened.duplicate) return { handled: true, duplicate: true }\n if (opened.error) return { handled: false }\n const r = await receiveContinuityInner(p, env, opened.doc, when, ent)\n if (r?.handled && !(r as any).error) C.effectDone(p, opened.digest)\n return r\n}\nasync function receiveContinuityInner (p: Person, env: any, doc: any, when: number, ent: any = {}) {\n const hit = tupleOf(p, env.rkid)\n if (!hit) return { handled: false }\n if (doc?.type === 'continuity-probe@1') return handleProbe(p, hit, doc, when, ent)\n if (doc?.type === 'continuity-mapping@1') return handleMapping(p, hit, doc, when, ent)\n return { handled: false, doc }\n}\n\nasync function handleProbe (p: Person, { key, t }: any, doc: any, when: number, ent: any) {\n // form BEFORE fields (M-3): mac, sequence and chunk list as used below\n if (!C.shaped(doc, { mac: 'string', probe: 'string', seq: 'string', blinded: 'array' }) || typeof doc.last !== 'boolean'\n || !C.intStr(doc.probe) || !C.intStr(doc.seq) // NaN in probeSeqIn wäre Replay-Zustandskorruption (Review 9, P-B1)\n || doc.blinded.length !== 256 // die Chunkform ist FEST: genau 256 Einträge (Review 10, P-B1-Rest)\n || !doc.blinded.every((b: any) => typeof b === 'string')) return { handled: true, error: 'malformed probe' }\n // Senderseitiger k_p, aus Empfängersicht nachgerechnet (Richtung!)\n const kp = await kProbe(t.channel.own.x.priv, t.channel.counterpartKa, key, t.channel.own.anchor)\n const { mac, ...body } = doc\n if ((await hmac(kp, jcs(body))) !== mac) return { handled: true, error: 'probe mac' }\n if (BigInt(doc.probe) <= BigInt(t.probeSeqIn ?? '0')) return { handled: true, error: 'probe replay (nicht streng größer)' }\n // Chunk-Sammlung pro (Tupel, probe); höhere probe verwirft die offene\n if (t.probeAsm?.probe !== doc.probe) t.probeAsm = { probe: doc.probe, blinded: [], next: 1 }\n if (Number(doc.seq) !== t.probeAsm.next) return { handled: true, error: 'malformed probe (chunk order)' }\n t.probeAsm.next += 1\n t.probeAsm.blinded.push(...doc.blinded)\n if (!doc.last) return { handled: true, partial: true }\n // finaler Chunk: union lesen, dann vollzieht sich der EFFEKT (consume\n // + Wahl + Kette) VOR dem fehlbaren Mapping-Bau — bewusst, denn die\n // Frische-Regel verbietet Chunk-Replays als Retry-Weg; ein Bau-Fehler\n // wird als getypte Pflicht persistiert und über flushContinuity()\n // eingelöst (Reviews 5–8)\n const union = t.probeAsm.blinded\n const consume = () => { t.probeAsm = null; t.probeSeqIn = doc.probe } // als STRING — BigInt-Vergleich, 18 Stellen exakt\n // Schnitt gegen die GEGENSEITEN-Anker des eigenen Prior-Candidate-Sets\n const matches = []\n for (const cp of t.priorCands?.counterpart ?? []) {\n if (union.includes(await hmac(kp, cp))) matches.push(cp)\n }\n if (!matches.length) { consume(); say(p, `Probe von ${t.name}: kein Match — ehrlich eine neue Beziehung`); return { handled: true, matches: 0 } }\n const outbound = []\n let chained = false\n let outboundError\n if (iAmRecord(t, key)) {\n // Record-Seite: einmal wählen, endgültig; Kette atomar mit der Wahl\n if (!t.contChosen) {\n const choice = matches[0] // Wahl frei — hier: erster Match (reichste Historie wäre App-Sache)\n consume()\n t.contChosen = choice\n chained = chainTuple(p, key, choice)\n try { outbound.push(await buildMapping(p, key, choice, when, ent)) }\n catch (err: any) { t.contMappingDue = { prior: choice, kind: 'choice' }; outboundError = String(err?.message ?? err) }\n } else consume()\n } else {\n // Nicht-Record: Match-Report (löst NIE eine Kette aus)\n if (!t.contReported) {\n consume()\n t.contReported = matches[0]\n try { outbound.push(await buildMapping(p, key, matches[0], when, ent)) }\n catch (err: any) { t.contMappingDue = { prior: matches[0], kind: 'report' }; outboundError = String(err?.message ?? err) }\n } else consume()\n }\n return { handled: true, matches: matches.length, chained, name: chained ? t.name : undefined, outbound, outboundError }\n}\n\nasync function handleMapping (p: Person, { key, t }: any, doc: any, when: number, ent: any) {\n if (!C.shaped(doc, { mac1: 'string', mac2: 'string', prior: 'string', next: 'string', to: 'string' })) return { handled: true, error: 'malformed mapping' }\n const { mac1, mac2, ...body } = doc\n const fail = (why: string) => { say(p, `Kontinuitäts-Mapping verworfen: ${why}`); return { handled: true, error: why } }\n if (body.to !== t.channel.own.anchor) return fail('to ≠ eigener neuer Anker')\n if (body.next !== key) return fail('next ≠ neuer Anker der Gegenseite')\n // prior MUSS im Schnappschuss liegen (nie das frische Tupel, nie tiefere Kettenglieder)\n if (!(t.priorCands?.counterpart ?? []).includes(body.prior)) return fail('prior nicht im Prior-Candidate-Set')\n const old = p.contacts.get(body.prior)\n if (!old?.channel?.own) return fail('alte Beziehung ohne eigenen Kontext')\n const msg = jcs(body)\n if ((await hmac(await kMac(old.channel.own.x.priv, old.channel.counterpartKa, 'rltp/visibility/mac/cont1'), msg)) !== mac1) return fail('mac1 (alter Beziehungs-Schlüssel)')\n if ((await hmac(await kMac(t.channel.own.x.priv, t.channel.counterpartKa, 'rltp/visibility/mac/cont2'), msg)) !== mac2) return fail('mac2 (neuer Beziehungs-Schlüssel)')\n const senderIsRecord = key < t.channel.own.anchor\n const outbound = []\n if (senderIsRecord) {\n // Record-Freeze: abweichendes prior derselben (next,to) = Äquivokation\n if (t.contRecordPrior && t.contRecordPrior !== body.prior) return fail('Äquivokation — Record-Wahl ist eingefroren')\n // fehlbare Arbeit VOR der Mutation (B-2): erst das Alignment-Mapping\n // bauen, dann Freeze + Kette in einem synchronen Zug\n t.contRecordPrior = body.prior\n chainTuple(p, key, body.prior)\n if (!t.contAligned) {\n try { const mapping = await buildMapping(p, key, body.prior, when, ent); t.contAligned = true; if (t.contMappingDue?.prior === body.prior) delete t.contMappingDue; outbound.push(mapping) }\n catch (err: any) { t.contMappingDue = { prior: body.prior, kind: 'align' }; return { handled: true, chained: p.contacts.get(body.prior)?.deactivated === true, name: t.name, outbound, outboundError: String(err?.message ?? err) } }\n }\n } else {\n // Match-Report der Nicht-Record-Seite: darf die Wahl INFORMIEREN, kettet nie\n if (iAmRecord(t, key) && !t.contChosen) {\n const mapping = await buildMapping(p, key, body.prior, when, ent)\n t.contChosen = body.prior\n chainTuple(p, key, body.prior)\n outbound.push(mapping)\n }\n }\n return { handled: true, chained: p.contacts.get(body.prior)?.deactivated === true, name: t.name, outbound }\n}\n\n/**\n * The host's producing call for a mapping the receiver still owes\n * (review 6, B-3): a transient seal failure during probe/mapping\n * handling persists the obligation in contMappingDue; this call — not a\n * redelivery, which the freshness rule forbids — builds and returns it.\n */\nexport async function flushContinuity (p: Person, newKey: string, when: number, ent: any = {}) {\n const t = p.contacts.get(newKey)\n if (!t?.contMappingDue) return { outbound: [] }\n // Single-Flight (Review 7, B-3): zwei parallele Flushes liefern nie\n // zwei Mappings für EINE Pflicht\n if (t.contFlushPending) return { outbound: [], pending: true }\n t.contFlushPending = true\n try {\n const due = t.contMappingDue\n const mapping = await buildMapping(p, newKey, due.prior, when, ent)\n // der Flush stellt GENAU den Zustand her, den der gelungene\n // Direktpfad hinterlassen hätte — kind-bewusst. Und er löscht NUR\n // die Pflicht, die er selbst gebaut hat: ist während des await eine\n // NEUERE an ihre Stelle getreten, bleibt sie stehen (Review 8, B-3)\n if (due.kind === 'align') t.contAligned = true\n if (t.contMappingDue === due) delete t.contMappingDue\n return { outbound: [mapping] }\n } finally { t.contFlushPending = false }\n}\n"]}