@openlfcp/wire 0.1.1 → 0.1.2

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 (52) hide show
  1. package/README.md +2 -2
  2. package/package.json +3 -3
  3. package/dist/capability.d.ts +0 -143
  4. package/dist/capability.js +0 -409
  5. package/dist/cbor/decode.d.ts +0 -23
  6. package/dist/cbor/decode.js +0 -163
  7. package/dist/cbor/encode.d.ts +0 -10
  8. package/dist/cbor/encode.js +0 -149
  9. package/dist/cbor/index.d.ts +0 -10
  10. package/dist/cbor/index.js +0 -10
  11. package/dist/cbor/text.d.ts +0 -11
  12. package/dist/cbor/text.js +0 -5
  13. package/dist/cbor/value.d.ts +0 -30
  14. package/dist/cbor/value.js +0 -23
  15. package/dist/chain.d.ts +0 -140
  16. package/dist/chain.js +0 -339
  17. package/dist/control-sync.d.ts +0 -69
  18. package/dist/control-sync.js +0 -44
  19. package/dist/control.d.ts +0 -173
  20. package/dist/control.js +0 -369
  21. package/dist/cose.d.ts +0 -81
  22. package/dist/cose.js +0 -133
  23. package/dist/data-unit.d.ts +0 -204
  24. package/dist/data-unit.js +0 -314
  25. package/dist/endpoint.d.ts +0 -51
  26. package/dist/endpoint.js +0 -116
  27. package/dist/epoch.d.ts +0 -109
  28. package/dist/epoch.js +0 -128
  29. package/dist/fields.d.ts +0 -18
  30. package/dist/fields.js +0 -78
  31. package/dist/handshake.d.ts +0 -175
  32. package/dist/handshake.js +0 -297
  33. package/dist/have.d.ts +0 -101
  34. package/dist/have.js +0 -268
  35. package/dist/index.d.ts +0 -21
  36. package/dist/index.js +0 -21
  37. package/dist/invite.d.ts +0 -80
  38. package/dist/invite.js +0 -247
  39. package/dist/key-package.d.ts +0 -92
  40. package/dist/key-package.js +0 -132
  41. package/dist/message.d.ts +0 -367
  42. package/dist/message.js +0 -690
  43. package/dist/objects.d.ts +0 -154
  44. package/dist/objects.js +0 -156
  45. package/dist/principal.d.ts +0 -43
  46. package/dist/principal.js +0 -81
  47. package/dist/session-state.d.ts +0 -47
  48. package/dist/session-state.js +0 -49
  49. package/dist/snapshot.d.ts +0 -86
  50. package/dist/snapshot.js +0 -189
  51. package/dist/transition.d.ts +0 -97
  52. package/dist/transition.js +0 -130
@@ -1,163 +0,0 @@
1
- import { bytesEqual, LfcpError } from "@openlfcp/core";
2
- import { compareEncodedKeys, encode } from "./encode.js";
3
- import { strictUtf8Decoder } from "./text.js";
4
- import { cborMap, MAX_DEPTH } from "./value.js";
5
- const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
6
- const asInteger = (v) => (v <= MAX_SAFE && v >= -MAX_SAFE ? Number(v) : v);
7
- class Reader {
8
- bytes;
9
- pos = 0;
10
- constructor(bytes) {
11
- this.bytes = bytes;
12
- }
13
- need(n) {
14
- if (BigInt(this.pos) + BigInt(n) > BigInt(this.bytes.length))
15
- throw new LfcpError("CBOR_TRUNCATED", "input ends inside an item");
16
- return Number(n);
17
- }
18
- byte() {
19
- this.need(1);
20
- return this.bytes[this.pos++];
21
- }
22
- take(n) {
23
- this.need(n);
24
- const out = this.bytes.slice(this.pos, this.pos + n);
25
- this.pos += n;
26
- return out;
27
- }
28
- /** Reads an argument and enforces the shortest form (§5.2 rule 1). */
29
- argument(ai) {
30
- if (ai < 24)
31
- return BigInt(ai);
32
- if (ai === 31)
33
- throw new LfcpError("CBOR_INDEFINITE_LENGTH", "indefinite lengths are not allowed (§5.2 rule 2)");
34
- if (ai > 27)
35
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "reserved additional information value");
36
- const size = 1 << (ai - 24);
37
- let v = 0n;
38
- for (const b of this.take(size))
39
- v = (v << 8n) | BigInt(b);
40
- const floor = [24n, 0x100n, 0x10000n, 0x100000000n][ai - 24];
41
- if (v < floor)
42
- throw new LfcpError("CBOR_NON_CANONICAL", "integer or length not in its shortest form");
43
- return v;
44
- }
45
- }
46
- function readKey(r, depth) {
47
- const v = readValue(r, depth);
48
- if (typeof v === "number" ||
49
- typeof v === "bigint" ||
50
- typeof v === "string" ||
51
- v instanceof Uint8Array)
52
- return v;
53
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "map keys must be integers, text or byte strings");
54
- }
55
- function readValue(r, depth) {
56
- if (depth > MAX_DEPTH)
57
- throw new LfcpError("CBOR_TOO_DEEP", `nesting deeper than ${MAX_DEPTH}`);
58
- const ib = r.byte();
59
- const major = ib >> 5;
60
- const ai = ib & 0x1f;
61
- switch (major) {
62
- case 0:
63
- return asInteger(r.argument(ai));
64
- case 1:
65
- return asInteger(-1n - r.argument(ai));
66
- case 2:
67
- return r.take(r.need(r.argument(ai)));
68
- case 3: {
69
- const raw = r.take(r.need(r.argument(ai)));
70
- try {
71
- return strictUtf8Decoder.decode(raw);
72
- }
73
- catch {
74
- throw new LfcpError("CBOR_INVALID_UTF8", "text string is not well-formed UTF-8");
75
- }
76
- }
77
- case 4: {
78
- const count = r.argument(ai);
79
- r.need(count); // every item takes at least one byte
80
- const items = [];
81
- for (let i = 0n; i < count; i += 1n)
82
- items.push(readValue(r, depth + 1));
83
- return items;
84
- }
85
- case 5: {
86
- const count = r.argument(ai);
87
- r.need(count * 2n);
88
- const entries = [];
89
- const seen = new Set();
90
- let previous = null;
91
- for (let i = 0n; i < count; i += 1n) {
92
- const start = r.pos;
93
- const key = readKey(r, depth + 1);
94
- const keyBytes = r.bytes.subarray(start, r.pos);
95
- const id = Array.from(keyBytes, (b) => b.toString(16).padStart(2, "0")).join("");
96
- if (seen.has(id))
97
- throw new LfcpError("CBOR_DUPLICATE_KEY", "map contains a duplicate key");
98
- if (previous && compareEncodedKeys(previous, keyBytes) > 0) {
99
- throw new LfcpError("CBOR_NON_CANONICAL", "map keys are not in deterministic order (§5.2 rule 4)");
100
- }
101
- seen.add(id);
102
- previous = keyBytes;
103
- entries.push([key, readValue(r, depth + 1)]);
104
- }
105
- return cborMap(entries);
106
- }
107
- case 6:
108
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "CBOR tags are not used by LFCP (§5.2 rule 6)");
109
- default:
110
- if (ai === 20)
111
- return false;
112
- if (ai === 21)
113
- return true;
114
- if (ai === 22)
115
- return null;
116
- if (ai === 31)
117
- throw new LfcpError("CBOR_INDEFINITE_LENGTH", "unexpected break code");
118
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "floats, undefined and other simple values are not used by LFCP");
119
- }
120
- }
121
- /**
122
- * Decodes exactly one deterministic CBOR item (LFCP-WIRE-01 §5.2) and
123
- * rejects anything else: indefinite lengths, non-shortest heads, unsorted
124
- * or duplicate map keys, tags, floats and other simple values, invalid
125
- * UTF-8, truncated input and trailing bytes.
126
- */
127
- export function decodeStrict(bytes) {
128
- const r = new Reader(bytes);
129
- const value = readValue(r, 0);
130
- if (r.pos !== bytes.length)
131
- throw new LfcpError("CBOR_TRAILING_BYTES", "bytes remain after the top-level item");
132
- return value;
133
- }
134
- /**
135
- * The §5.2 receiver check: `bytes` decode strictly and re-encode to exactly
136
- * the same bytes. Signature and object-ID checks still use the received
137
- * bytes; this only decides whether they are the deterministic encoding.
138
- */
139
- /**
140
- * Strict decode plus the §5.2 re-encode comparison (N7): the bytes must be
141
- * well-formed (CBOR_* errors from `decodeStrict` otherwise) and exactly the
142
- * deterministic encoding of their value (CBOR_NON_CANONICAL otherwise). The
143
- * re-encoding is only compared, never returned.
144
- */
145
- export function decodeDeterministic(bytes) {
146
- const value = decodeStrict(bytes);
147
- if (!bytesEqual(encode(value), bytes))
148
- throw new LfcpError("CBOR_NON_CANONICAL", "the bytes are not the deterministic encoding");
149
- return value;
150
- }
151
- export function isDeterministic(bytes) {
152
- let value;
153
- try {
154
- value = decodeStrict(bytes);
155
- }
156
- catch (e) {
157
- if (e instanceof LfcpError)
158
- return false;
159
- throw e;
160
- }
161
- return bytesEqual(encode(value), bytes);
162
- }
163
- //# sourceMappingURL=decode.js.map
@@ -1,10 +0,0 @@
1
- import { type CborValue } from "./value.js";
2
- /** Byte order of encoded map keys (§5.2 rule 4): shorter encodings first, then bytewise. */
3
- export declare function compareEncodedKeys(a: Uint8Array, b: Uint8Array): number;
4
- /**
5
- * Deterministic CBOR encoding (LFCP-WIRE-01 §5.2): shortest heads,
6
- * definite lengths, map keys sorted by encoded length then bytewise,
7
- * duplicate keys rejected, array order kept, no tags.
8
- */
9
- export declare function encode(value: CborValue): Uint8Array;
10
- //# sourceMappingURL=encode.d.ts.map
@@ -1,149 +0,0 @@
1
- import { LfcpError } from "@openlfcp/core";
2
- import { strictUtf8Decoder, utf8Encoder } from "./text.js";
3
- import { isCborKey, isCborMap, MAX_DEPTH, UINT64_MAX } from "./value.js";
4
- /** Byte order of encoded map keys (§5.2 rule 4): shorter encodings first, then bytewise. */
5
- export function compareEncodedKeys(a, b) {
6
- if (a.length !== b.length)
7
- return a.length - b.length;
8
- for (let i = 0; i < a.length; i += 1) {
9
- const d = a[i] - b[i];
10
- if (d !== 0)
11
- return d;
12
- }
13
- return 0;
14
- }
15
- class Writer {
16
- buf = new Uint8Array(256);
17
- len = 0;
18
- reserve(n) {
19
- if (this.len + n <= this.buf.length)
20
- return;
21
- let size = this.buf.length * 2;
22
- while (size < this.len + n)
23
- size *= 2;
24
- const next = new Uint8Array(size);
25
- next.set(this.buf.subarray(0, this.len));
26
- this.buf = next;
27
- }
28
- byte(b) {
29
- this.reserve(1);
30
- this.buf[this.len++] = b;
31
- }
32
- bytes(b) {
33
- this.reserve(b.length);
34
- this.buf.set(b, this.len);
35
- this.len += b.length;
36
- }
37
- /** Major type and argument in the shortest form (§5.2 rule 1). */
38
- head(major, n) {
39
- const m = major << 5;
40
- const v = BigInt(n);
41
- if (v < 24n) {
42
- this.byte(m | Number(v));
43
- }
44
- else if (v <= 0xffn) {
45
- this.byte(m | 24);
46
- this.byte(Number(v));
47
- }
48
- else if (v <= 0xffffn) {
49
- this.byte(m | 25);
50
- this.bigEndian(v, 2);
51
- }
52
- else if (v <= 0xffffffffn) {
53
- this.byte(m | 26);
54
- this.bigEndian(v, 4);
55
- }
56
- else {
57
- this.byte(m | 27);
58
- this.bigEndian(v, 8);
59
- }
60
- }
61
- bigEndian(v, size) {
62
- for (let i = size - 1; i >= 0; i -= 1)
63
- this.byte(Number((v >> BigInt(8 * i)) & 0xffn));
64
- }
65
- result() {
66
- return this.buf.slice(0, this.len);
67
- }
68
- }
69
- function writeInteger(w, value) {
70
- if (typeof value === "number" && !Number.isSafeInteger(value)) {
71
- throw new LfcpError(Number.isInteger(value) ? "CBOR_OUT_OF_RANGE" : "CBOR_UNSUPPORTED_TYPE", Number.isInteger(value)
72
- ? "integers beyond the safe range must be bigint"
73
- : "floating-point numbers are not used by LFCP");
74
- }
75
- const v = BigInt(value);
76
- if (v > UINT64_MAX || v < -(UINT64_MAX + 1n))
77
- throw new LfcpError("CBOR_OUT_OF_RANGE", "integer outside -2^64 .. 2^64-1");
78
- if (v >= 0n)
79
- w.head(0, v);
80
- else
81
- w.head(1, -1n - v);
82
- }
83
- function writeValue(w, value, depth) {
84
- if (depth > MAX_DEPTH)
85
- throw new LfcpError("CBOR_TOO_DEEP", `nesting deeper than ${MAX_DEPTH}`);
86
- if (value === null) {
87
- w.byte(0xf6);
88
- return;
89
- }
90
- if (typeof value === "boolean") {
91
- w.byte(value ? 0xf5 : 0xf4);
92
- return;
93
- }
94
- if (typeof value === "number" || typeof value === "bigint") {
95
- writeInteger(w, value);
96
- return;
97
- }
98
- if (value instanceof Uint8Array) {
99
- w.head(2, value.length);
100
- w.bytes(value);
101
- return;
102
- }
103
- if (typeof value === "string") {
104
- const raw = utf8Encoder.encode(value);
105
- // TextEncoder replaces lone surrogates; refuse instead of changing the text.
106
- if (strictUtf8Decoder.decode(raw) !== value)
107
- throw new LfcpError("CBOR_INVALID_UTF8", "string contains lone surrogates");
108
- w.head(3, raw.length);
109
- w.bytes(raw);
110
- return;
111
- }
112
- if (Array.isArray(value)) {
113
- w.head(4, value.length);
114
- for (const item of value)
115
- writeValue(w, item, depth + 1);
116
- return;
117
- }
118
- if (isCborMap(value)) {
119
- const encoded = value.entries.map(([k, v]) => {
120
- if (!isCborKey(k))
121
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "map keys must be integers, text or byte strings");
122
- return [encode(k), v];
123
- });
124
- encoded.sort((a, b) => compareEncodedKeys(a[0], b[0]));
125
- for (let i = 1; i < encoded.length; i += 1) {
126
- if (compareEncodedKeys(encoded[i - 1][0], encoded[i][0]) === 0) {
127
- throw new LfcpError("CBOR_DUPLICATE_KEY", "map contains a duplicate key");
128
- }
129
- }
130
- w.head(5, encoded.length);
131
- for (const [k, v] of encoded) {
132
- w.bytes(k);
133
- writeValue(w, v, depth + 1);
134
- }
135
- return;
136
- }
137
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", `cannot encode ${typeof value} as LFCP CBOR`);
138
- }
139
- /**
140
- * Deterministic CBOR encoding (LFCP-WIRE-01 §5.2): shortest heads,
141
- * definite lengths, map keys sorted by encoded length then bytewise,
142
- * duplicate keys rejected, array order kept, no tags.
143
- */
144
- export function encode(value) {
145
- const w = new Writer();
146
- writeValue(w, value, 0);
147
- return w.result();
148
- }
149
- //# sourceMappingURL=encode.js.map
@@ -1,10 +0,0 @@
1
- /**
2
- * Low-level deterministic CBOR for LFCP-WIRE-01 §5.2.
3
- *
4
- * This is a building block for COSE, Wire structures and vectors; it is not
5
- * the LFCP API. Import it from `@openlfcp/wire/cbor`.
6
- */
7
- export { decodeDeterministic, decodeStrict, isDeterministic } from "./decode.js";
8
- export { compareEncodedKeys, encode } from "./encode.js";
9
- export { type CborKey, type CborMap, type CborValue, cborMap, isCborMap, MAX_DEPTH, } from "./value.js";
10
- //# sourceMappingURL=index.d.ts.map
@@ -1,10 +0,0 @@
1
- /**
2
- * Low-level deterministic CBOR for LFCP-WIRE-01 §5.2.
3
- *
4
- * This is a building block for COSE, Wire structures and vectors; it is not
5
- * the LFCP API. Import it from `@openlfcp/wire/cbor`.
6
- */
7
- export { decodeDeterministic, decodeStrict, isDeterministic } from "./decode.js";
8
- export { compareEncodedKeys, encode } from "./encode.js";
9
- export { cborMap, isCborMap, MAX_DEPTH, } from "./value.js";
10
- //# sourceMappingURL=index.js.map
@@ -1,11 +0,0 @@
1
- interface Utf8Encoder {
2
- encode(text: string): Uint8Array;
3
- }
4
- interface Utf8Decoder {
5
- decode(bytes: Uint8Array): string;
6
- }
7
- export declare const utf8Encoder: Utf8Encoder;
8
- /** Throws on malformed UTF-8 instead of inserting U+FFFD. */
9
- export declare const strictUtf8Decoder: Utf8Decoder;
10
- export {};
11
- //# sourceMappingURL=text.d.ts.map
package/dist/cbor/text.js DELETED
@@ -1,5 +0,0 @@
1
- const g = globalThis;
2
- export const utf8Encoder = new g.TextEncoder();
3
- /** Throws on malformed UTF-8 instead of inserting U+FFFD. */
4
- export const strictUtf8Decoder = new g.TextDecoder("utf-8", { fatal: true });
5
- //# sourceMappingURL=text.js.map
@@ -1,30 +0,0 @@
1
- /**
2
- * The CBOR data model LFCP uses (LFCP-WIRE-01 §5). Everything else is
3
- * unrepresentable here and rejected by the decoder:
4
- *
5
- * - integers (major types 0 and 1, §5.1): `number` when it is a safe
6
- * integer, `bigint` otherwise, in -2^64 .. 2^64-1;
7
- * - byte strings: `Uint8Array`; text strings: `string` (well-formed UTF-8);
8
- * - arrays: `CborValue[]`, order preserved (§5.2 rule 5);
9
- * - maps: `CborMap`, an explicit entry list, never a JS object (§5.2 rule 4);
10
- * - `true`, `false`, `null`.
11
- *
12
- * No tags (§5.2 rule 6: LFCP requires none in deterministic structures), no
13
- * floating-point numbers, no `undefined`, no other simple values.
14
- */
15
- export type CborValue = CborKey | boolean | null | readonly CborValue[] | CborMap;
16
- /** Map keys: integers, text strings and byte strings. */
17
- export type CborKey = number | bigint | string | Uint8Array;
18
- /** A CBOR map. Entry order is irrelevant on input; encoding sorts the keys. */
19
- export interface CborMap {
20
- readonly kind: "cbor-map";
21
- readonly entries: readonly (readonly [CborKey, CborValue])[];
22
- }
23
- export declare function isCborMap(value: unknown): value is CborMap;
24
- export declare function isCborKey(value: unknown): value is CborKey;
25
- /** Builds a map from `[key, value]` pairs. Duplicate keys are rejected when encoding. */
26
- export declare function cborMap(entries: Iterable<readonly [CborKey, CborValue]>): CborMap;
27
- /** Largest nesting depth accepted when encoding or decoding; an SDK safety limit, not a protocol rule. */
28
- export declare const MAX_DEPTH = 128;
29
- export declare const UINT64_MAX: bigint;
30
- //# sourceMappingURL=value.d.ts.map
@@ -1,23 +0,0 @@
1
- import { LfcpError } from "@openlfcp/core";
2
- export function isCborMap(value) {
3
- return (typeof value === "object" && value !== null && value.kind === "cbor-map");
4
- }
5
- export function isCborKey(value) {
6
- return (typeof value === "number" ||
7
- typeof value === "bigint" ||
8
- typeof value === "string" ||
9
- value instanceof Uint8Array);
10
- }
11
- /** Builds a map from `[key, value]` pairs. Duplicate keys are rejected when encoding. */
12
- export function cborMap(entries) {
13
- const list = [...entries].map(([k, v]) => {
14
- if (!isCborKey(k))
15
- throw new LfcpError("CBOR_UNSUPPORTED_TYPE", "map keys must be integers, text or byte strings");
16
- return [k, v];
17
- });
18
- return Object.freeze({ kind: "cbor-map", entries: Object.freeze(list) });
19
- }
20
- /** Largest nesting depth accepted when encoding or decoding; an SDK safety limit, not a protocol rule. */
21
- export const MAX_DEPTH = 128;
22
- export const UINT64_MAX = (1n << 64n) - 1n;
23
- //# sourceMappingURL=value.js.map
package/dist/chain.d.ts DELETED
@@ -1,140 +0,0 @@
1
- import { type ControlRecordId, type DataEpoch, type Hash32, LfcpError, type PrincipalId, type ResourceId } from "@openlfcp/core";
2
- import { type Authorization, type Grant } from "./capability.js";
3
- import { type ControlRecord } from "./control.js";
4
- import type { Endpoint } from "./endpoint.js";
5
- import type { ActorHave } from "./have.js";
6
- import type { PrincipalDescriptor } from "./principal.js";
7
- /**
8
- * Control Chain validation (LFCP-WIRE-01 §13, §13.1, §13.2, §15).
9
- *
10
- * A pure function over exact signed Control Records: no I/O, clock or
11
- * randomness. The records may arrive in any order; the chain is ordered by
12
- * its own links (prev_control_id from Genesis), so every permutation of a
13
- * set gives the same result. Diagnostics are sorted by sequence and then
14
- * record ID only to make them reproducible; nothing ever chooses a branch.
15
- *
16
- * A fork (two validly signed records naming the same previous record) is a
17
- * conflict, never resolved by time, ID order, arrival order or server
18
- * preference (§13.2). A chain containing a Coordinator Recovery (7) or
19
- * Resource Tombstone (8) record is refused with PROTOCOL_UNSUPPORTED
20
- * (MVP-0.1-PROTOCOL-SCOPE §4, DV1). Extension records (32 and above) need
21
- * owner authority (§14) and stay in the chain, since the links run through
22
- * them, but are not applied to the derived state; they are listed in
23
- * `unappliedRecords`.
24
- */
25
- /** The initial and current route (§15, §20). */
26
- export interface ControlRoute {
27
- readonly endpoints: readonly Endpoint[];
28
- readonly coordinatorUrl: string;
29
- }
30
- /** A Data Epoch and its DEK commitment (§11, §15, §19). */
31
- export interface ControlEpoch {
32
- readonly epoch: DataEpoch;
33
- readonly dekCommitment: Hash32;
34
- }
35
- /**
36
- * One Data Epoch in the chain's history (§11, §15, §19): opened by Genesis
37
- * (epoch 0) or a Key Epoch record, and closed by the next Key Epoch record,
38
- * which records its final frontier (§19.1). Kept for every epoch, so units
39
- * of any older epoch stay evaluable.
40
- */
41
- export interface EpochHistory extends ControlEpoch {
42
- readonly openedBy: ControlRecordId;
43
- /** The Key Epoch record that closed this epoch, or null while it is current. */
44
- readonly closedBy: ControlRecordId | null;
45
- /** The accepted final frontier of this epoch (canonical, G-CP1), or null while it is current. */
46
- readonly finalFrontier: readonly ActorHave[] | null;
47
- }
48
- /**
49
- * The validated state of one Resource's Control Chain. This is the single
50
- * Control state type: LFCP-021 (capabilities) and LFCP-023 (epochs and
51
- * cutoff) extend it here and in `applyRecord`, never with a parallel type.
52
- *
53
- * LFCP-020 fills the chain position and what Genesis establishes;
54
- * LFCP-021 adds the grants and applies route updates; LFCP-023 applies
55
- * Key Epoch rotation (`epoch`, `epochs`).
56
- */
57
- export interface ControlState {
58
- readonly resourceId: ResourceId;
59
- readonly genesisId: ControlRecordId;
60
- /** The current Control Head: the record ID of the last validated record. */
61
- readonly head: ControlRecordId;
62
- readonly seq: bigint;
63
- readonly dataProfile: string;
64
- /** The owner: the Genesis owner, or the new owner after a verified transfer (§15, §23.3). */
65
- readonly owner: PrincipalDescriptor;
66
- readonly route: ControlRoute;
67
- /** §20 route version of `route`; Genesis implies route version 0 (§15). */
68
- readonly routeVersion: bigint;
69
- /** The current Data Epoch. */
70
- readonly epoch: ControlEpoch;
71
- /** Every Data Epoch so far, by epoch number (decimal string) (§19, LFCP-023). */
72
- readonly epochs: ReadonlyMap<string, EpochHistory>;
73
- /** Grants created through this head (§17.2, §18.1), by grant ID hex; see capability.ts. */
74
- readonly grants: ReadonlyMap<string, Grant>;
75
- /**
76
- * Principal Descriptors carried by applied records (Genesis owner, grant
77
- * subjects, claimants), by Principal ID hex. Descriptors are
78
- * self-certifying (§7), so knowing one grants nothing; it only lets later
79
- * records by that Principal be verified.
80
- */
81
- readonly principals: ReadonlyMap<string, PrincipalDescriptor>;
82
- }
83
- /** Why a chain is invalid. */
84
- export type ChainProblem = "MALFORMED" | "UNSUPPORTED_TYPE" | "NO_GENESIS" | "GENESIS_SIGNER" | "RESOURCE" | "SEQUENCE" | "PREVIOUS" | "SIGNATURE" | "UNRESOLVED_ISSUER" | "EPOCH" | "UNAUTHORIZED" | "DEFERRED_TYPE";
85
- export type ChainResult = {
86
- readonly kind: "linear";
87
- readonly state: ControlState;
88
- /** The validated records in chain order (from the start, Genesis included when no start was given). */
89
- readonly records: readonly ControlRecord[];
90
- /** Extension records kept in the chain but not applied (mvpSupported = false), in chain order. */
91
- readonly unappliedRecords: readonly ControlRecordId[];
92
- /**
93
- * The validated state at any head of this chain (from the start, or
94
- * Genesis), for evaluating authority at an older Control Head. States
95
- * are kept by immutable head ID; undefined for a head not on the chain.
96
- */
97
- readonly stateAt: (head: Uint8Array) => ControlState | undefined;
98
- } | {
99
- readonly kind: "conflict";
100
- readonly wireCode: "CONTROL_CONFLICT";
101
- /** The record both branches name as previous; null when two Genesis records compete. */
102
- readonly commonHead: ControlRecordId | null;
103
- /** The contested sequence. */
104
- readonly seq: bigint;
105
- /** The competing record IDs, sorted by bytes for reproducible output (not a ranking). */
106
- readonly competing: readonly ControlRecordId[];
107
- /** The validated common prefix up to commonHead (null for a Genesis conflict); no branch is applied. */
108
- readonly prefixState: ControlState | null;
109
- } | {
110
- readonly kind: "invalid";
111
- readonly problem: ChainProblem;
112
- readonly wireCode: string;
113
- /** Position in the input array of the offending record (null when no record is at fault). */
114
- readonly index: number | null;
115
- readonly recordId: ControlRecordId | null;
116
- readonly error: LfcpError;
117
- };
118
- export interface ChainOptions {
119
- /** Continue after an already validated head instead of starting from Genesis. */
120
- readonly start?: ControlState;
121
- /**
122
- * Resolves an issuer that no earlier record in the chain describes.
123
- * The returned descriptor is checked against the ID like any other.
124
- */
125
- readonly resolvePrincipal?: (id: PrincipalId) => PrincipalDescriptor | undefined;
126
- /**
127
- * Authorization, called for every non-Genesis record with the state
128
- * before it (the previous head). Defaults to authorizeControlRecord, the
129
- * LFCP-021 capability engine; a caller may substitute a policy (tests).
130
- * A refusal makes the chain invalid (UNAUTHORIZED).
131
- */
132
- readonly authorize?: (record: ControlRecord, state: ControlState) => boolean | Authorization;
133
- }
134
- /**
135
- * Validates a set of exact signed Control Records of one Resource, from
136
- * Genesis or after `options.start`. Input objects that are already decoded
137
- * are re-decoded from their exact bytes, never trusted as decoded.
138
- */
139
- export declare function validateControlChain(inputs: readonly (Uint8Array | ControlRecord)[], options?: ChainOptions): ChainResult;
140
- //# sourceMappingURL=chain.d.ts.map