@mikeargento/bitgraph-player 0.8.0 → 0.9.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.
package/dist/domain.js DELETED
@@ -1,212 +0,0 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
2
- /**
3
- * BitGraph Domain: a party's own domain publishing the keys that record
4
- * for it. The file (`bitgraph-domain/1`) is served at
5
- * `https://<domain>/.well-known/bitgraph` and is the party speaking for
6
- * itself, never BitGraph speaking about the party: parsing enforces
7
- * well-formedness only, and adopting the statement is the reader's act
8
- * (see pin.ts). The format is DOMAIN.md; evaluation semantics (SPEC.md
9
- * sections 1 through 9) are untouched by everything in this module.
10
- *
11
- * The file shares SPEC section 9.1's trusted-key grammar, so an entry
12
- * pastes into a format 2 rule's `trustedKeys` unchanged, and a key's
13
- * fingerprint is the lowercase hex SHA-256 of the decoded key bytes,
14
- * which for es256 is exactly the `keyId` actor proofs carry. Fingerprints
15
- * are always derived here and never read from the file, so a domain
16
- * cannot claim a key it does not show.
17
- */
18
- import { createHash } from "node:crypto";
19
- import { decodeDigestBytes } from "./rule.js";
20
- import { decodeB64Strict, keyObjectFor, parseSigFile, verifySigFile } from "./sig.js";
21
- export const DOMAIN_FILE_VERSION = "bitgraph-domain/1";
22
- export const DOMAIN_WELL_KNOWN_PATH = "/.well-known/bitgraph";
23
- export const DOMAIN_FILE_MAX_BYTES = 65_536;
24
- export class DomainFileError extends Error {
25
- issues;
26
- constructor(issues) {
27
- super(issues[0] ?? "invalid domain file");
28
- this.name = "DomainFileError";
29
- this.issues = issues;
30
- }
31
- }
32
- /**
33
- * Lowercase registrable hostname: dot-separated LDH labels, at least two,
34
- * no scheme, no port, no path. Also the safety property the pin store
35
- * rests on: a name this grammar accepts contains no path separators.
36
- */
37
- const LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
38
- const DOMAIN_RE = new RegExp(`^${LABEL}(?:\\.${LABEL})+$`);
39
- export function isDomainName(s) {
40
- return s.length > 0 && s.length <= 253 && DOMAIN_RE.test(s);
41
- }
42
- function isPlainObject(v) {
43
- return typeof v === "object" && v !== null && !Array.isArray(v);
44
- }
45
- /**
46
- * Parse and validate a bitgraph-domain/1 file. Strict: unknown fields are
47
- * errors (additions are a new format version), and key material that does
48
- * not decode is refused here rather than stored, so a pin can never hold
49
- * keys that silently match nothing. Throws DomainFileError with every
50
- * issue found.
51
- */
52
- export function parseDomainFile(input, expectedDomain) {
53
- const issues = [];
54
- const text = typeof input === "string" ? input : Buffer.from(input).toString("utf8");
55
- if (typeof input !== "string" && input.length > DOMAIN_FILE_MAX_BYTES) {
56
- throw new DomainFileError([`file is ${input.length} bytes; the cap is ${DOMAIN_FILE_MAX_BYTES}`]);
57
- }
58
- let raw;
59
- try {
60
- raw = JSON.parse(text);
61
- }
62
- catch (err) {
63
- throw new DomainFileError([`not JSON: ${err.message}`]);
64
- }
65
- if (!isPlainObject(raw))
66
- throw new DomainFileError(["not a JSON object"]);
67
- for (const k of Object.keys(raw)) {
68
- if (!["version", "domain", "party", "keys"].includes(k)) {
69
- issues.push(`unknown field "${k}" (additions are a new format version)`);
70
- }
71
- }
72
- if (raw["version"] !== DOMAIN_FILE_VERSION) {
73
- issues.push(`"version" must be exactly "${DOMAIN_FILE_VERSION}"`);
74
- }
75
- const domain = raw["domain"];
76
- if (typeof domain !== "string" || !isDomainName(domain)) {
77
- issues.push(`"domain" must be a lowercase hostname (no scheme, no port, no path)`);
78
- }
79
- else if (expectedDomain !== undefined && domain !== expectedDomain) {
80
- issues.push(`the file names domain "${domain}" but was requested for "${expectedDomain}"; refusing to store one party's file under another party's name`);
81
- }
82
- const party = raw["party"];
83
- if (typeof party !== "string" || party.trim().length === 0) {
84
- issues.push(`"party" is required: the name the domain gives itself`);
85
- }
86
- const keys = Object.create(null);
87
- const rawKeys = raw["keys"];
88
- if (!isPlainObject(rawKeys) || Object.keys(rawKeys).length === 0) {
89
- issues.push(`"keys" must be an object naming at least one key`);
90
- }
91
- else {
92
- for (const [name, entry] of Object.entries(rawKeys)) {
93
- const where = `keys.${name}`;
94
- if (!/^[A-Za-z0-9_.-]+$/.test(name) || /^[0-9]+$/.test(name)) {
95
- issues.push(`${where}: key name must match [A-Za-z0-9_.-]+ with at least one non-digit`);
96
- continue;
97
- }
98
- if (!isPlainObject(entry)) {
99
- issues.push(`${where}: must be an object`);
100
- continue;
101
- }
102
- for (const k of Object.keys(entry)) {
103
- if (k !== "alg" && k !== "publicKey")
104
- issues.push(`${where}: unknown field "${k}"`);
105
- }
106
- const alg = entry["alg"];
107
- const publicKey = entry["publicKey"];
108
- if (alg !== "ed25519" && alg !== "es256") {
109
- issues.push(`${where}: "alg" must be "ed25519" or "es256"`);
110
- continue;
111
- }
112
- if (typeof publicKey !== "string" || publicKey.length === 0) {
113
- issues.push(`${where}: "publicKey" is required and must be a non-empty string`);
114
- continue;
115
- }
116
- const key = { alg, publicKey };
117
- if (keyObjectFor(key) === undefined) {
118
- issues.push(`${where}: key material does not decode as ${alg} (${alg === "es256" ? "SPKI DER for P-256" : "raw 32-byte key"}, standard base64)`);
119
- continue;
120
- }
121
- keys[name] = key;
122
- }
123
- }
124
- if (issues.length > 0)
125
- throw new DomainFileError(issues);
126
- return { version: DOMAIN_FILE_VERSION, domain: domain, party: party.trim(), keys };
127
- }
128
- /**
129
- * Lowercase hex SHA-256 of the decoded publicKey bytes. For es256 this is
130
- * exactly the actor keyId (hex SHA-256 of the SPKI DER). Undefined when
131
- * the material does not decode, which parseDomainFile refuses anyway.
132
- */
133
- export function keyFingerprint(key) {
134
- const material = decodeB64Strict(key.publicKey);
135
- if (material === undefined)
136
- return undefined;
137
- return createHash("sha256").update(material).digest("hex");
138
- }
139
- /** Every key with its derived fingerprint, name order. */
140
- export function domainKeyRefs(file) {
141
- const refs = [];
142
- for (const name of Object.keys(file.keys).sort()) {
143
- const key = file.keys[name];
144
- const fingerprint = keyFingerprint(key);
145
- if (fingerprint !== undefined)
146
- refs.push({ name, key, fingerprint });
147
- }
148
- return refs;
149
- }
150
- /** What a re-pin would change, for showing before asking. */
151
- export function diffDomainFiles(before, after) {
152
- const beforeRefs = new Map(domainKeyRefs(before).map((r) => [r.name, r]));
153
- const afterRefs = new Map(domainKeyRefs(after).map((r) => [r.name, r]));
154
- const diff = { added: [], removed: [], changed: [], unchanged: 0 };
155
- if (before.party !== after.party)
156
- diff.partyChanged = { before: before.party, after: after.party };
157
- for (const [name, ref] of afterRefs) {
158
- const prior = beforeRefs.get(name);
159
- if (prior === undefined)
160
- diff.added.push(ref);
161
- else if (prior.key.alg !== ref.key.alg || prior.key.publicKey !== ref.key.publicKey) {
162
- diff.changed.push({ name, before: prior, after: ref });
163
- }
164
- else
165
- diff.unchanged += 1;
166
- }
167
- for (const [name, ref] of beforeRefs) {
168
- if (!afterRefs.has(name))
169
- diff.removed.push(ref);
170
- }
171
- return diff;
172
- }
173
- /**
174
- * The check-report adapter: what `check --from` consults per recording.
175
- * Defined as an interface in check.ts so the check module (which also
176
- * builds verify.html) never imports the crypto this module uses; the CLI
177
- * hands the report builder this object.
178
- */
179
- export function checkDomain(file) {
180
- const actorNames = new Map();
181
- for (const ref of domainKeyRefs(file)) {
182
- if (ref.key.alg !== "es256")
183
- continue;
184
- if (!actorNames.has(ref.fingerprint))
185
- actorNames.set(ref.fingerprint, ref.name);
186
- }
187
- const sigKeys = domainKeyRefs(file)
188
- .map((ref) => ({ name: ref.name, key: ref.key, keyObject: keyObjectFor(ref.key) }))
189
- .filter((k) => k.keyObject !== undefined);
190
- return {
191
- domain: file.domain,
192
- party: file.party,
193
- keyCount: Object.keys(file.keys).length,
194
- actorKeyName: (keyId) => actorNames.get(keyId.toLowerCase()),
195
- signatureKeyName: (targetSha256Hex, evidence) => {
196
- // Deterministic: candidates in ascending content-hash order, keys in
197
- // name order; the first verifying pair decides (SPEC §9.4 discipline).
198
- for (const sha256Hex of [...evidence.keys()].sort()) {
199
- const sig = parseSigFile(evidence.get(sha256Hex));
200
- if (sig === undefined)
201
- continue;
202
- for (const k of sigKeys) {
203
- if (verifySigFile(sig, k.key, k.keyObject, targetSha256Hex, decodeDigestBytes)) {
204
- return k.name;
205
- }
206
- }
207
- }
208
- return undefined;
209
- },
210
- };
211
- }
212
- //# sourceMappingURL=domain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"domain.js","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"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"}
package/dist/pin.d.ts DELETED
@@ -1,55 +0,0 @@
1
- import type { DomainFile } from "./domain.js";
2
- /** ~/.bitgraph/pins, or BITGRAPH_PINS, or the --pins flag above this. */
3
- export declare function defaultPinsDir(): string;
4
- export interface StoredPin {
5
- domain: string;
6
- file: DomainFile;
7
- bytes: Buffer;
8
- pinnedAt: Date;
9
- }
10
- /**
11
- * Read one stored pin. Undefined when no pin exists; throws
12
- * DomainFileError when the stored bytes no longer parse (the remedy is to
13
- * pin again, or --forget).
14
- */
15
- export declare function readPin(domain: string, pinsDir?: string): StoredPin | undefined;
16
- /** Store the fetched bytes verbatim. Overwrites: a pin is current by choice. */
17
- export declare function writePin(domain: string, bytes: Uint8Array, pinsDir?: string): string;
18
- /** Remove one pin. False when there was none. */
19
- export declare function forgetPin(domain: string, pinsDir?: string): boolean;
20
- export interface PinListEntry {
21
- domain: string;
22
- /** Undefined when the stored bytes no longer parse. */
23
- party?: string;
24
- keyCount?: number;
25
- pinnedAt: Date;
26
- malformed: boolean;
27
- }
28
- /** Every stored pin, domain order. Malformed entries are listed, flagged. */
29
- export declare function listPins(pinsDir?: string): PinListEntry[];
30
- /** The slice of fetch() this module uses; injectable for tests. */
31
- export interface FetchLike {
32
- (url: string, init?: {
33
- signal?: AbortSignal;
34
- redirect?: "follow";
35
- }): Promise<{
36
- ok: boolean;
37
- status: number;
38
- headers: {
39
- get(name: string): string | null;
40
- };
41
- arrayBuffer(): Promise<ArrayBuffer>;
42
- }>;
43
- }
44
- /**
45
- * Fetch a domain's bitgraph-domain/1 file: one HTTPS GET of the fixed
46
- * well-known path. Redirects are followed, but the file's own `domain`
47
- * field must equal the requested domain (parseDomainFile enforces it), so
48
- * a redirect cannot repoint the name. Throws on any failure; a malformed
49
- * file is refused here and never stored.
50
- */
51
- export declare function fetchDomainFile(domain: string, fetchImpl?: FetchLike): Promise<{
52
- bytes: Buffer;
53
- file: DomainFile;
54
- }>;
55
- //# sourceMappingURL=pin.d.ts.map
package/dist/pin.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"pin.d.ts","sourceRoot":"","sources":["../src/pin.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,yEAAyE;AACzE,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAQD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAyB,GAAG,SAAS,GAAG,SAAS,CAYjG;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,MAAyB,GAAG,MAAM,CAKtG;AAED,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAyB,GAAG,OAAO,CAOrF;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,IAAI,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,OAAO,GAAE,MAAyB,GAAG,YAAY,EAAE,CAyB3E;AAED,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;QAC3E,EAAE,EAAE,OAAO,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC;QAC9C,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;KACrC,CAAC,CAAC;CACJ;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,SAAoD,GAC9D,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAuB9C"}
package/dist/pin.js DELETED
@@ -1,126 +0,0 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
2
- /**
3
- * The pin store, and the fetch behind `bitgraph-play pin`: the ONLY code
4
- * in this package that touches the network, and only when the reader
5
- * invokes it. A pin is the reader's act and the reader's record: the
6
- * domain's file is stored byte-verbatim on the reader's machine, and
7
- * `check --from` reads the store and never fetches. A missing pin is an
8
- * invocation error with the pin command as its remedy, never a verdict,
9
- * and a stored pin outlives the file that provided it, deliberately.
10
- */
11
- import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
- import { homedir } from "node:os";
13
- import { join } from "node:path";
14
- import { DOMAIN_FILE_MAX_BYTES, DOMAIN_WELL_KNOWN_PATH, isDomainName, parseDomainFile } from "./domain.js";
15
- /** ~/.bitgraph/pins, or BITGRAPH_PINS, or the --pins flag above this. */
16
- export function defaultPinsDir() {
17
- const env = process.env["BITGRAPH_PINS"];
18
- return env !== undefined && env.length > 0 ? env : join(homedir(), ".bitgraph", "pins");
19
- }
20
- function pinPath(pinsDir, domain) {
21
- if (!isDomainName(domain))
22
- throw new Error(`not a domain name: ${domain}`);
23
- // The domain grammar admits no path separators, so the name is the file.
24
- return join(pinsDir, domain);
25
- }
26
- /**
27
- * Read one stored pin. Undefined when no pin exists; throws
28
- * DomainFileError when the stored bytes no longer parse (the remedy is to
29
- * pin again, or --forget).
30
- */
31
- export function readPin(domain, pinsDir = defaultPinsDir()) {
32
- const path = pinPath(pinsDir, domain);
33
- let bytes;
34
- let pinnedAt;
35
- try {
36
- bytes = readFileSync(path);
37
- pinnedAt = statSync(path).mtime;
38
- }
39
- catch {
40
- return undefined;
41
- }
42
- const file = parseDomainFile(bytes, domain);
43
- return { domain, file, bytes, pinnedAt };
44
- }
45
- /** Store the fetched bytes verbatim. Overwrites: a pin is current by choice. */
46
- export function writePin(domain, bytes, pinsDir = defaultPinsDir()) {
47
- const path = pinPath(pinsDir, domain);
48
- mkdirSync(pinsDir, { recursive: true });
49
- writeFileSync(path, bytes);
50
- return path;
51
- }
52
- /** Remove one pin. False when there was none. */
53
- export function forgetPin(domain, pinsDir = defaultPinsDir()) {
54
- try {
55
- unlinkSync(pinPath(pinsDir, domain));
56
- return true;
57
- }
58
- catch {
59
- return false;
60
- }
61
- }
62
- /** Every stored pin, domain order. Malformed entries are listed, flagged. */
63
- export function listPins(pinsDir = defaultPinsDir()) {
64
- let names;
65
- try {
66
- names = readdirSync(pinsDir);
67
- }
68
- catch {
69
- return [];
70
- }
71
- const entries = [];
72
- for (const name of names.sort()) {
73
- if (!isDomainName(name))
74
- continue;
75
- try {
76
- const pin = readPin(name, pinsDir);
77
- if (pin === undefined)
78
- continue;
79
- entries.push({
80
- domain: name,
81
- party: pin.file.party,
82
- keyCount: Object.keys(pin.file.keys).length,
83
- pinnedAt: pin.pinnedAt,
84
- malformed: false,
85
- });
86
- }
87
- catch {
88
- entries.push({ domain: name, pinnedAt: statSync(join(pinsDir, name)).mtime, malformed: true });
89
- }
90
- }
91
- return entries;
92
- }
93
- /**
94
- * Fetch a domain's bitgraph-domain/1 file: one HTTPS GET of the fixed
95
- * well-known path. Redirects are followed, but the file's own `domain`
96
- * field must equal the requested domain (parseDomainFile enforces it), so
97
- * a redirect cannot repoint the name. Throws on any failure; a malformed
98
- * file is refused here and never stored.
99
- */
100
- export async function fetchDomainFile(domain, fetchImpl = globalThis.fetch) {
101
- if (!isDomainName(domain)) {
102
- const hint = domain.includes("://") ? " (pass the bare hostname, without scheme or path)" : "";
103
- throw new Error(`not a domain name: ${domain}${hint}`);
104
- }
105
- const url = `https://${domain}${DOMAIN_WELL_KNOWN_PATH}`;
106
- let res;
107
- try {
108
- res = await fetchImpl(url, { signal: AbortSignal.timeout(15_000), redirect: "follow" });
109
- }
110
- catch (err) {
111
- throw new Error(`cannot fetch ${url}: ${err.message}`);
112
- }
113
- if (!res.ok)
114
- throw new Error(`no domain file at ${url} (HTTP ${res.status})`);
115
- const declared = res.headers.get("content-length");
116
- if (declared !== null && Number(declared) > DOMAIN_FILE_MAX_BYTES) {
117
- throw new Error(`the file at ${url} declares ${declared} bytes; the cap is ${DOMAIN_FILE_MAX_BYTES}`);
118
- }
119
- const bytes = Buffer.from(await res.arrayBuffer());
120
- if (bytes.length > DOMAIN_FILE_MAX_BYTES) {
121
- throw new Error(`the file at ${url} is ${bytes.length} bytes; the cap is ${DOMAIN_FILE_MAX_BYTES}`);
122
- }
123
- const file = parseDomainFile(bytes, domain);
124
- return { bytes, file };
125
- }
126
- //# sourceMappingURL=pin.js.map
package/dist/pin.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"pin.js","sourceRoot":"","sources":["../src/pin.ts"],"names":[],"mappings":"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"}