@dcasegr/skein 0.0.0-stage → 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 (176) hide show
  1. package/README.md +525 -2
  2. package/bin/skein-host.js +22 -0
  3. package/bin/skein.js +8 -0
  4. package/dist/client/admin-cli.d.ts +10 -0
  5. package/dist/client/admin-cli.js +370 -0
  6. package/dist/client/admin.d.ts +139 -0
  7. package/dist/client/admin.js +256 -0
  8. package/dist/client/bundle.d.ts +9 -0
  9. package/dist/client/bundle.js +34 -0
  10. package/dist/client/cli.d.ts +44 -0
  11. package/dist/client/cli.js +306 -0
  12. package/dist/client/client.d.ts +161 -0
  13. package/dist/client/client.js +237 -0
  14. package/dist/client/config.d.ts +18 -0
  15. package/dist/client/config.js +31 -0
  16. package/dist/client/conversation.d.ts +62 -0
  17. package/dist/client/conversation.js +113 -0
  18. package/dist/client/raw.d.ts +94 -0
  19. package/dist/client/raw.js +154 -0
  20. package/dist/client/target.d.ts +34 -0
  21. package/dist/client/target.js +158 -0
  22. package/dist/dev/cli.d.ts +8 -0
  23. package/dist/dev/cli.js +248 -0
  24. package/dist/dev/scan.d.ts +15 -0
  25. package/dist/dev/scan.js +173 -0
  26. package/dist/envelope-cbor.d.ts +110 -0
  27. package/dist/envelope-cbor.js +160 -0
  28. package/dist/envelope.d.ts +38 -0
  29. package/dist/envelope.js +101 -0
  30. package/dist/host/arc.d.ts +131 -0
  31. package/dist/host/arc.js +425 -0
  32. package/dist/host/billing.d.ts +102 -0
  33. package/dist/host/billing.js +167 -0
  34. package/dist/host/boot.d.ts +246 -0
  35. package/dist/host/boot.js +643 -0
  36. package/dist/host/cli.d.ts +57 -0
  37. package/dist/host/cli.js +1253 -0
  38. package/dist/host/clock.d.ts +3 -0
  39. package/dist/host/clock.js +8 -0
  40. package/dist/host/control.d.ts +45 -0
  41. package/dist/host/control.js +170 -0
  42. package/dist/host/cron.d.ts +99 -0
  43. package/dist/host/cron.js +196 -0
  44. package/dist/host/deliver.d.ts +53 -0
  45. package/dist/host/deliver.js +251 -0
  46. package/dist/host/deploy.d.ts +29 -0
  47. package/dist/host/deploy.js +55 -0
  48. package/dist/host/door-answer.d.ts +24 -0
  49. package/dist/host/door-answer.js +21 -0
  50. package/dist/host/events.d.ts +89 -0
  51. package/dist/host/events.js +222 -0
  52. package/dist/host/fake-arcade.d.ts +40 -0
  53. package/dist/host/fake-arcade.js +149 -0
  54. package/dist/host/fake-discovery.d.ts +4 -0
  55. package/dist/host/fake-discovery.js +58 -0
  56. package/dist/host/feeds.d.ts +127 -0
  57. package/dist/host/feeds.js +422 -0
  58. package/dist/host/frontdoor.d.ts +92 -0
  59. package/dist/host/frontdoor.js +172 -0
  60. package/dist/host/genesis.d.ts +259 -0
  61. package/dist/host/genesis.js +353 -0
  62. package/dist/host/handles.d.ts +40 -0
  63. package/dist/host/handles.js +88 -0
  64. package/dist/host/hostenv.d.ts +30 -0
  65. package/dist/host/hostenv.js +83 -0
  66. package/dist/host/image-chain.d.ts +127 -0
  67. package/dist/host/image-chain.js +440 -0
  68. package/dist/host/install.d.ts +22 -0
  69. package/dist/host/install.js +139 -0
  70. package/dist/host/instances.d.ts +227 -0
  71. package/dist/host/instances.js +430 -0
  72. package/dist/host/kernel.d.ts +246 -0
  73. package/dist/host/kernel.js +603 -0
  74. package/dist/host/liveness.d.ts +65 -0
  75. package/dist/host/liveness.js +155 -0
  76. package/dist/host/manifest.d.ts +160 -0
  77. package/dist/host/manifest.js +597 -0
  78. package/dist/host/p2p.d.ts +337 -0
  79. package/dist/host/p2p.js +1002 -0
  80. package/dist/host/packet.d.ts +121 -0
  81. package/dist/host/packet.js +562 -0
  82. package/dist/host/plan.d.ts +154 -0
  83. package/dist/host/plan.js +418 -0
  84. package/dist/host/providers.d.ts +238 -0
  85. package/dist/host/providers.js +565 -0
  86. package/dist/host/roster.d.ts +72 -0
  87. package/dist/host/roster.js +172 -0
  88. package/dist/host/router.d.ts +782 -0
  89. package/dist/host/router.js +2411 -0
  90. package/dist/host/signer.d.ts +49 -0
  91. package/dist/host/signer.js +167 -0
  92. package/dist/host/store-sqlite.d.ts +39 -0
  93. package/dist/host/store-sqlite.js +170 -0
  94. package/dist/host/stores.d.ts +43 -0
  95. package/dist/host/stores.js +342 -0
  96. package/dist/host/sync.d.ts +121 -0
  97. package/dist/host/sync.js +467 -0
  98. package/dist/host/testbeef.d.ts +21 -0
  99. package/dist/host/testbeef.js +37 -0
  100. package/dist/host/testhost.d.ts +149 -0
  101. package/dist/host/testhost.js +211 -0
  102. package/dist/host/vcdiff.d.ts +9 -0
  103. package/dist/host/vcdiff.js +210 -0
  104. package/dist/host/wasm-kernel.d.ts +87 -0
  105. package/dist/host/wasm-kernel.js +296 -0
  106. package/dist/host/wasm-worker.d.ts +1 -0
  107. package/dist/host/wasm-worker.js +186 -0
  108. package/dist/peers/cron.d.ts +33 -0
  109. package/dist/peers/cron.js +75 -0
  110. package/dist/peers/infer-main.d.ts +2 -0
  111. package/dist/peers/infer-main.js +74 -0
  112. package/dist/peers/infer.d.ts +163 -0
  113. package/dist/peers/infer.js +367 -0
  114. package/dist/runtime/beef.d.ts +28 -0
  115. package/dist/runtime/beef.js +107 -0
  116. package/dist/runtime/bitcoin.d.ts +36 -0
  117. package/dist/runtime/bitcoin.js +141 -0
  118. package/dist/runtime/cid.d.ts +14 -0
  119. package/dist/runtime/cid.js +51 -0
  120. package/dist/runtime/dispatch.d.ts +92 -0
  121. package/dist/runtime/dispatch.js +142 -0
  122. package/dist/runtime/envelope.d.ts +60 -0
  123. package/dist/runtime/envelope.js +101 -0
  124. package/dist/runtime/heads.d.ts +25 -0
  125. package/dist/runtime/heads.js +37 -0
  126. package/dist/runtime/identity.d.ts +28 -0
  127. package/dist/runtime/identity.js +67 -0
  128. package/dist/runtime/index-store.d.ts +152 -0
  129. package/dist/runtime/index-store.js +715 -0
  130. package/dist/runtime/log.d.ts +62 -0
  131. package/dist/runtime/log.js +104 -0
  132. package/dist/runtime/programs.d.ts +30 -0
  133. package/dist/runtime/programs.js +61 -0
  134. package/dist/runtime/records.d.ts +135 -0
  135. package/dist/runtime/records.js +149 -0
  136. package/dist/runtime/sqlite.d.ts +33 -0
  137. package/dist/runtime/sqlite.js +604 -0
  138. package/dist/runtime/store.d.ts +141 -0
  139. package/dist/runtime/store.js +24 -0
  140. package/dist/runtime/syscalls.d.ts +9 -0
  141. package/dist/runtime/syscalls.js +41 -0
  142. package/dist/runtime/tree.d.ts +60 -0
  143. package/dist/runtime/tree.js +196 -0
  144. package/dist/runtime/types.d.ts +100 -0
  145. package/dist/runtime/types.js +7 -0
  146. package/dist/testapps.d.ts +101 -0
  147. package/dist/testapps.js +275 -0
  148. package/dist/testkit.d.ts +25 -0
  149. package/dist/testkit.js +48 -0
  150. package/dist/wallet.d.ts +35 -0
  151. package/dist/wallet.js +87 -0
  152. package/images/README.md +68 -0
  153. package/images/default/README.md +38 -0
  154. package/images/default/bin/frontdoor.cid +1 -0
  155. package/images/default/bin/frontdoor.json +5 -0
  156. package/images/default/bin/messagebox.cid +1 -0
  157. package/images/default/bin/messagebox.json +8 -0
  158. package/images/default/bin/wallet.cid +1 -0
  159. package/images/default/bin/wallet.json +14 -0
  160. package/images/default/etc/config.json +1 -0
  161. package/images/default/etc/dispatch.json +82 -0
  162. package/images/host/etc/apps.json +8 -0
  163. package/kernel-zig/README.md +1247 -0
  164. package/kernel-zig/web/client.js +65 -0
  165. package/kernel-zig/web/kernel.d.ts +59 -0
  166. package/kernel-zig/web/kernel.js +329 -0
  167. package/kernel-zig/web/store.js +67 -0
  168. package/kernel-zig/web/worker.js +79 -0
  169. package/kernel-zig/zig-out/web/skein-kernel.wasm +0 -0
  170. package/package.json +77 -4
  171. package/wasm/README.md +35 -0
  172. package/wasm/frontdoor.wasm +0 -0
  173. package/wasm/messagebox.wasm +0 -0
  174. package/wasm/resolve.wasm +0 -0
  175. package/wasm/wallet.wasm +0 -0
  176. package/wasm/wire-probe.wasm +0 -0
@@ -0,0 +1,62 @@
1
+ import type { WalletProtocol } from "@bsv/sdk";
2
+ import { CID } from "multiformats/cid";
3
+ import { type Genesis } from "./records.ts";
4
+ import type { LogEntry, Store } from "./store.ts";
5
+ import { type Stamp } from "./syscalls.ts";
6
+ import type { Ms } from "./types.ts";
7
+ /** What an entry admits: exactly one of these (log.zig isLogEntry). */
8
+ export type EntryBody = {
9
+ genesis: CID;
10
+ } | {
11
+ request: CID;
12
+ transport: string;
13
+ } | {
14
+ mail: CID;
15
+ } | {
16
+ event: CID;
17
+ box: string;
18
+ };
19
+ /** A log entry in format 8. */
20
+ export type Entry = {
21
+ kind: "log";
22
+ prev: CID | null;
23
+ n: number;
24
+ time: Stamp;
25
+ } & EntryBody;
26
+ /** A stamp as the `at` (ms) of the records written while processing its entry. */
27
+ export declare const stampMs: (s: Stamp) => Ms;
28
+ /**
29
+ * A format-8 entry, as log.zig's isLogEntry has it: `kind: "log"`, a number
30
+ * `n`, exactly one of genesis | mail | event | request, a non-empty `box` with
31
+ * an event and only with it, a non-empty `transport` with a request and only
32
+ * with it, and none of the older formats' fields (`sig`, `wake`,
33
+ * `envelope`, `body`, `outcome`).
34
+ */
35
+ export declare function isLogEntry(x: unknown): x is Entry;
36
+ /**
37
+ * The next entry: extending the tip, stamped `time` (raised to the tip's
38
+ * stamp if earlier). The host builds only the genesis this way (#163); the
39
+ * kernel keeps the same rule for every entry it appends.
40
+ */
41
+ export declare function nextEntry(store: Store, body: EntryBody, time: Stamp): Promise<Entry>;
42
+ export declare const LOG_PROTOCOL: WalletProtocol;
43
+ export declare const LOG_KEY_ID = "1";
44
+ /** A format-1 entry's signed bytes: its dag-cbor with `sig` removed. */
45
+ export declare function entryBytes(e: LogEntry | Omit<LogEntry, "sig"> | Entry): Uint8Array;
46
+ /**
47
+ * `fuelPerStep` (issue #5): the one limit on a step's fuel (wasm instructions)
48
+ * in the Zig kernel, which records every step's fuel on its update; a step
49
+ * that runs out ends `errored`, "fuel exhausted". 10^12, generous until
50
+ * something hits it.
51
+ */
52
+ export declare const FUEL_PER_STEP = "1000000000000";
53
+ export declare const DEFAULTS: Record<string, string>;
54
+ /** The log as records, in order. */
55
+ export declare function readLog(store: Store): Promise<Array<{
56
+ cid: CID;
57
+ entry: LogEntry;
58
+ }>>;
59
+ /** The genesis a log starts with. */
60
+ export declare function genesisOf(store: Store): Promise<Genesis>;
61
+ /** Short form for log lines. */
62
+ export declare const short: (c: CID | string) => string;
@@ -0,0 +1,104 @@
1
+ // The input log (docs/VM.md, docs/MESSAGES.md "The log, format 8"): every
2
+ // input, in order, as a hash chain of log-entry records. The kernel
3
+ // (kernel-zig/src/log.zig) is authoritative: it refuses an entry that is
4
+ // malformed or does not extend its tip. This is the host's mirror of the
5
+ // shape, for building the genesis entry (src/host/genesis.ts over nextEntry)
6
+ // and reading entries. Every other entry the kernel appends (#163: the host
7
+ // hands over the body and its stamp, Kernel.admit; the kernel assigns prev
8
+ // and n).
9
+ //
10
+ // { kind: "log", prev: <entry | null>, n, time: [sec, nsec],
11
+ // genesis: <cid> n = 0: the starting state
12
+ // | request: <cid>, transport: <name> a package as a transport carried it in (#68):
13
+ // the record of an http request, a libp2p message
14
+ // or frame, or a provider's signed message (`local`)
15
+ // | mail: <cid> a message the host admits directly (#40, #70)
16
+ // | event: <cid>, box: <name> } a record from the host's wiring (a header, a
17
+ // proof), routed by box or subject (#29, #65)
18
+ //
19
+ // No signature (format 2, #33): the sender signed its request, `prev` fixes
20
+ // the order, and the stamp is the host's word. The tip entry's CID is the
21
+ // instance's **state hash**. `time` is the host's clock at admission, never
22
+ // before the previous entry's; everything inside the machine derives time
23
+ // from these stamps (syscalls.ts).
24
+ import { CID } from "multiformats/cid";
25
+ import { encode } from "./cid.js";
26
+ import { isGenesis } from "./records.js";
27
+ import { maxStamp } from "./syscalls.js";
28
+ /** A stamp as the `at` (ms) of the records written while processing its entry. */
29
+ export const stampMs = (s) => s[0] * 1000 + Math.floor(s[1] / 1_000_000);
30
+ const isCid = (x) => CID.asCID(x) !== null;
31
+ const isName = (x) => typeof x === "string" && x.length > 0;
32
+ /**
33
+ * A format-8 entry, as log.zig's isLogEntry has it: `kind: "log"`, a number
34
+ * `n`, exactly one of genesis | mail | event | request, a non-empty `box` with
35
+ * an event and only with it, a non-empty `transport` with a request and only
36
+ * with it, and none of the older formats' fields (`sig`, `wake`,
37
+ * `envelope`, `body`, `outcome`).
38
+ */
39
+ export function isLogEntry(x) {
40
+ const e = x;
41
+ if (!e || typeof e !== "object" || e.kind !== "log" || typeof e.n !== "number")
42
+ return false;
43
+ if (["sig", "wake", "envelope", "body", "outcome"].some((k) => e[k] !== undefined))
44
+ return false;
45
+ if (["genesis", "mail", "event", "request"].filter((k) => e[k] !== undefined).length !== 1)
46
+ return false;
47
+ if (e.event !== undefined ? !isCid(e.event) || !isName(e.box) : e.box !== undefined)
48
+ return false;
49
+ if (e.request !== undefined ? !isCid(e.request) || !isName(e.transport) : e.transport !== undefined)
50
+ return false;
51
+ if (e.mail !== undefined && !isCid(e.mail))
52
+ return false;
53
+ return true;
54
+ }
55
+ /**
56
+ * The next entry: extending the tip, stamped `time` (raised to the tip's
57
+ * stamp if earlier). The host builds only the genesis this way (#163); the
58
+ * kernel keeps the same rule for every entry it appends.
59
+ */
60
+ export async function nextEntry(store, body, time) {
61
+ const tipCid = await store.log.tip();
62
+ const tip = tipCid ? await store.get(tipCid) : undefined;
63
+ return { kind: "log", prev: tipCid ?? null, n: tip ? tip.n + 1 : 0, time: tip ? maxStamp(time, tip.time) : time, ...body };
64
+ }
65
+ // ---------------------------------------------------------------- format 1
66
+ // Before format 2 (#33) an entry was the host's statement, signed by the
67
+ // host: protocol [2, "skein log"], key "1", counterparty anyone, over the
68
+ // entry's dag-cbor without `sig`. The kernel refuses such a store; these are
69
+ // kept for what makes one to check that (kernel-zig/equiv/old-store.ts) and
70
+ // for the signature fixtures (kernel-zig/test/fixtures.ts).
71
+ export const LOG_PROTOCOL = [2, "skein log"];
72
+ export const LOG_KEY_ID = "1";
73
+ /** A format-1 entry's signed bytes: its dag-cbor with `sig` removed. */
74
+ export function entryBytes(e) {
75
+ const { sig: _, ...rest } = e;
76
+ return encode(rest).bytes;
77
+ }
78
+ /**
79
+ * `fuelPerStep` (issue #5): the one limit on a step's fuel (wasm instructions)
80
+ * in the Zig kernel, which records every step's fuel on its update; a step
81
+ * that runs out ends `errored`, "fuel exhausted". 10^12, generous until
82
+ * something hits it.
83
+ */
84
+ export const FUEL_PER_STEP = "1000000000000";
85
+ export const DEFAULTS = { model: "ripper/qwen38", thinking: "off", fuelPerStep: FUEL_PER_STEP };
86
+ /** The log as records, in order. */
87
+ export async function readLog(store) {
88
+ const out = [];
89
+ for await (const x of store.log.entries())
90
+ out.push(x);
91
+ return out;
92
+ }
93
+ /** The genesis a log starts with. */
94
+ export async function genesisOf(store) {
95
+ for await (const { entry } of store.log.entries()) {
96
+ const g = entry.genesis ? await store.get(entry.genesis) : undefined;
97
+ if (!isGenesis(g))
98
+ throw new Error("log: entry 0 is not a genesis");
99
+ return g;
100
+ }
101
+ throw new Error("log: empty");
102
+ }
103
+ /** Short form for log lines. */
104
+ export const short = (c) => c.toString().slice(-8);
@@ -0,0 +1,30 @@
1
+ import { CID } from "multiformats/cid";
2
+ export declare const RAW = 85;
3
+ /** CIDv1(raw, sha2-256) of a module's bytes: how modules are stored and named. */
4
+ export declare function rawCid(bytes: Uint8Array): CID;
5
+ /** The modules as committed under wasm/ (see wasm/README.md). A test checks these against the files. */
6
+ export declare const MODULES: {
7
+ readonly wallet: CID<unknown, number, number, import("multiformats/cid").Version>;
8
+ };
9
+ /** The wallet program record's description (also the default image's bin/wallet.json: the same record, #116). */
10
+ export declare const WALLET_DESCRIPTION = "The wallet (#29, #79, #116: core): its own records under wallet/ over the chain app's chain/state. A message's body {op: internalize|createAction|signAction|list, \u2026} (BRC-100's; every transaction ingested at the chain app); the kernel's pay step, args {pay: {to, x, checkpoint}} (#130: X sats, or all it has if less, to the host, the state record committed in a 0-sat OP_RETURN output, emitted as the event `payment`). Prints the result record's CID.";
11
+ /**
12
+ * The wallet's state as records (issue #29; docs/WALLET.md): the `wallet`
13
+ * box's handler, written in Zig (programs/wallet, over the SDK's wallet library). In the default image
14
+ * by CID (#116, #130: images/default/bin/wallet.cid and wallet.json — this very record): every skein made
15
+ * from it pays its host from it. A code genesis has none: an instance that wants it subscribes a box to
16
+ * its record's CID (an owner's box).
17
+ */
18
+ export declare const WALLET: import("./records.ts").Program;
19
+ /** A wasm binary's kind by its preamble: a core module, a component (#34), or neither. */
20
+ export declare function wasmKind(b: Uint8Array): "module" | "component" | undefined;
21
+ /**
22
+ * The program record for bin/<name>.wasm or bin/<name>.cid (its module's CID),
23
+ * with bin/<name>.json's {inputs, services, description} if the tree has one:
24
+ * what a boot puts, and what an app install (install.ts, #72) sends.
25
+ */
26
+ export declare function programRecord(name: string, module: CID, meta?: {
27
+ inputs?: unknown;
28
+ services?: string[];
29
+ description?: string;
30
+ }): Record<string, unknown>;
@@ -0,0 +1,61 @@
1
+ // The pinned modules as the node host names them: the wallet, by the CID of
2
+ // its bytes under wasm/. `skein-dev install` puts them into a store file (the
3
+ // bootstrap side door, docs/OPEN.md). The kernel's pins are the truth
4
+ // (kernel-zig/src/programs.zig, which also pins the handler programs: the
5
+ // front door, the messagebox, resolve); a test checks these against the
6
+ // committed files. The shell is not pinned (#83): its modules are the shell
7
+ // app's (shruggr/skein-shell), installed like any app's, and its program
8
+ // record is written by that install (src/host/install.ts shellProgram).
9
+ import { createHash } from "node:crypto";
10
+ import { CID } from "multiformats/cid";
11
+ import * as Digest from "multiformats/hashes/digest";
12
+ import { program } from "./records.js";
13
+ export const RAW = 0x55;
14
+ /** CIDv1(raw, sha2-256) of a module's bytes: how modules are stored and named. */
15
+ export function rawCid(bytes) {
16
+ return CID.createV1(RAW, Digest.create(0x12, createHash("sha256").update(bytes).digest()));
17
+ }
18
+ /** The modules as committed under wasm/ (see wasm/README.md). A test checks these against the files. */
19
+ export const MODULES = {
20
+ // The wallet's state inside the VM (issue #29): Zig, wasm32-wasi (programs/wallet over the SDK's wallet library, built by scripts/build-programs.sh).
21
+ "wallet": CID.parse("bafkreifmo2fz45wn3vukhcf226bpnfh6klu7w5m43of76mx6olgghnzqkq"),
22
+ };
23
+ /** The wallet program record's description (also the default image's bin/wallet.json: the same record, #116). */
24
+ export const WALLET_DESCRIPTION = "The wallet (#29, #79, #116: core): its own records under wallet/ over the chain app's chain/state. A message's body {op: internalize|createAction|signAction|list, …} (BRC-100's; every transaction ingested at the chain app); the kernel's pay step, args {pay: {to, x, checkpoint}} (#130: X sats, or all it has if less, to the host, the state record committed in a 0-sat OP_RETURN output, emitted as the event `payment`). Prints the result record's CID.";
25
+ /**
26
+ * The wallet's state as records (issue #29; docs/WALLET.md): the `wallet`
27
+ * box's handler, written in Zig (programs/wallet, over the SDK's wallet library). In the default image
28
+ * by CID (#116, #130: images/default/bin/wallet.cid and wallet.json — this very record): every skein made
29
+ * from it pays its host from it. A code genesis has none: an instance that wants it subscribes a box to
30
+ * its record's CID (an owner's box).
31
+ */
32
+ export const WALLET = program({
33
+ name: "wallet",
34
+ code: { wasm: MODULES.wallet },
35
+ inputs: { envelope: "cid", body: "cid", box: "string", sender: "identity", event: "cid" },
36
+ services: ["wallet", "http"],
37
+ description: WALLET_DESCRIPTION,
38
+ });
39
+ /** A wasm binary's kind by its preamble: a core module, a component (#34), or neither. */
40
+ export function wasmKind(b) {
41
+ if (b.length < 8 || b[0] !== 0 || b[1] !== 0x61 || b[2] !== 0x73 || b[3] !== 0x6d)
42
+ return undefined;
43
+ if (b[4] === 1 && b[5] === 0 && b[6] === 0 && b[7] === 0)
44
+ return "module";
45
+ if (b[4] === 0x0d && b[5] === 0 && b[6] === 1 && b[7] === 0)
46
+ return "component";
47
+ return undefined;
48
+ }
49
+ /** A handler program's record inputs when bin/<name>.json gives none (the kernel's handler inputs). */
50
+ const HANDLER_INPUTS = { message: "cid", body: "cid", box: "string", sender: "identity" };
51
+ /**
52
+ * The program record for bin/<name>.wasm or bin/<name>.cid (its module's CID),
53
+ * with bin/<name>.json's {inputs, services, description} if the tree has one:
54
+ * what a boot puts, and what an app install (install.ts, #72) sends.
55
+ */
56
+ export function programRecord(name, module, meta = {}) {
57
+ return {
58
+ kind: "program", name, code: { wasm: module },
59
+ inputs: meta.inputs ?? HANDLER_INPUTS, services: meta.services ?? [], description: meta.description ?? `${name} (from the system tree)`,
60
+ };
61
+ }
@@ -0,0 +1,135 @@
1
+ import { CID } from "./cid.ts";
2
+ import type { Ms, Ref } from "./types.ts";
3
+ import type { Signer } from "./identity.ts";
4
+ import type { DispatchRow } from "./dispatch.ts";
5
+ export type * from "./types.ts";
6
+ /** A compressed secp256k1 public key, hex. */
7
+ export type Identity = string;
8
+ export declare const isIdentity: (x: unknown) => x is Identity;
9
+ export type Message = {
10
+ kind: "message";
11
+ from: Identity;
12
+ to?: string;
13
+ seq: number;
14
+ at: Ms;
15
+ body: unknown;
16
+ refs: Ref[];
17
+ sig: Uint8Array;
18
+ };
19
+ export type UnsignedMessage = Omit<Message, "sig">;
20
+ /** The signed bytes: dag-cbor of the record with `sig` removed (every other field is covered). */
21
+ export declare function messageBytes(m: UnsignedMessage | Message): Uint8Array;
22
+ /** What ECDSA signs: sha256(messageBytes(m)). */
23
+ export declare function messageDigest(m: UnsignedMessage | Message): Uint8Array;
24
+ export declare function signMessage(signer: Signer, m: {
25
+ to?: string;
26
+ seq: number;
27
+ body: unknown;
28
+ refs?: Ref[];
29
+ at: Ms;
30
+ }): Promise<Message>;
31
+ export declare function isMessage(x: unknown): x is Message;
32
+ /** Shape plus signature against `from`. Synchronous for the store's transactions; see verifyMessage. */
33
+ export declare function verifyMessageSync(m: unknown): m is Message;
34
+ export declare function verifyMessage(m: unknown): Promise<boolean>;
35
+ export type Program = {
36
+ kind: "program";
37
+ name: string;
38
+ code: {
39
+ ts: string;
40
+ } | {
41
+ wasm: CID;
42
+ };
43
+ inputs: unknown;
44
+ services: string[];
45
+ description: string;
46
+ };
47
+ export declare function program(p: Omit<Program, "kind">): Program;
48
+ export declare function isProgram(x: unknown): x is Program;
49
+ /**
50
+ * A route of the route table (#77, #143; dispatch.ts, docs/MESSAGES.md "The
51
+ * dispatch table"): the kernel's rule for a route (dispatch.zig `problem`).
52
+ * `program` is a program record's CID, "kernel" for an admin operation named
53
+ * by `fn`, or absent for a read route (http only: its filters answer). No
54
+ * `sender` (#143: routing only).
55
+ */
56
+ export declare function isDispatchRow(x: unknown): x is DispatchRow;
57
+ /** What a kernel route may name (its `fn`): the admin operations (#143: `grant` among them), the claim (#89: an image's one route; root to the claimant, the route removed), and the host's tick (#130). */
58
+ export declare const KERNEL_OPS: readonly string[];
59
+ /**
60
+ * An instance's starting state: the record the first log entry names. Its
61
+ * `identity` is the instance wallet's identity key, which its outbound
62
+ * envelopes are signed by; `host` is the identity of the host that delivers
63
+ * its inputs (format 1 only: format 2 dropped it); `owner` is the identity
64
+ * it acted for (format 1-8: #143 replaced it with `root`); `programs` are the programs it starts with, by name;
65
+ * `dispatch` is the seed of its dispatch table (dispatch.ts): written as the
66
+ * table's chain's first updates when the entry is processed, never read for
67
+ * routing; `scopes` are the heads each genesis-wired program may advance
68
+ * (program name → head names, a prefix ending in `/`).
69
+ */
70
+ export type Genesis = {
71
+ kind: "genesis";
72
+ identity: Identity;
73
+ handle: string;
74
+ domain: string;
75
+ /** #143: the initial root holders; absent in an image (#89: root comes with the claim). */
76
+ root?: Identity[];
77
+ /** #143: what gates the genesis's own programs' functions: {<role>: ["<program>.<fn>"]}. */
78
+ roles?: Record<string, string[]>;
79
+ host?: Identity;
80
+ programs: Record<string, CID>;
81
+ dispatch: DispatchRow[];
82
+ scopes?: Record<string, string[]>;
83
+ /** Peers by role, e.g. `infer`: the identity a program sends that kind of request to. */
84
+ peers?: Record<string, Identity>;
85
+ /** Defaults programs fall back on, e.g. `model` for the loop. */
86
+ defaults?: Record<string, string>;
87
+ /** Handles of identities programs send to whose envelopes may not name them (the owner, the peers): an outbound envelope's `recipient`. */
88
+ names?: Record<Identity, {
89
+ handle: string;
90
+ domain: string;
91
+ }>;
92
+ /** Boxes the delivery provider collects besides the dispatched ones: replies (e.g. `completions`), routed only by `replyTo`. */
93
+ collect?: string[];
94
+ };
95
+ export declare function isGenesis(x: unknown): x is Genesis;
96
+ /**
97
+ * What a handler emits: an outbound message to `to` in `box`. `envelope` is the
98
+ * complete BRC-169 envelope (signed metadata and BRC-78 content), which the
99
+ * program sealed through the instance wallet; `body` is its plaintext content,
100
+ * a record. The kernel checks the envelope before accepting it.
101
+ */
102
+ export type Emit = {
103
+ kind: "emit";
104
+ to: Identity;
105
+ box: string;
106
+ body: CID;
107
+ envelope: Record<string, unknown> & {
108
+ content: string;
109
+ };
110
+ };
111
+ export declare function isEmit(x: unknown): x is Emit;
112
+ /**
113
+ * A signer call's record (format 6, #67: the one call a step makes out
114
+ * mid-step): a BRC-100 wire request frame and its result frame, at (thread,
115
+ * step, i). Referenced, in order, from the step's update (`calls`); on replay
116
+ * the answer is served from here, not a wallet. (Before format 6 these were
117
+ * `attested` records, with `http` and `libp2p` calls beside the wallet's.)
118
+ * The record's kind is still the string `oracle`, the signer's old name: it
119
+ * is part of the format, so the rename left it as it was.
120
+ */
121
+ export type SignerCall = {
122
+ kind: "oracle";
123
+ thread: CID;
124
+ step: number;
125
+ i: number;
126
+ request: Uint8Array;
127
+ result: Uint8Array;
128
+ };
129
+ export declare function isSignerCall(x: unknown): x is SignerCall;
130
+ /**
131
+ * Wallet wire calls a program may make (BRC-100 call codes): key derivation
132
+ * and crypto only — no actions, no certificates (the kernel's scheduler.zig
133
+ * `wallet_calls`). By code, for showing a signer call.
134
+ */
135
+ export declare const WALLET_CALLS: ReadonlyMap<number, string>;
@@ -0,0 +1,149 @@
1
+ // v2 record kinds (docs/VM.md): message, program, genesis (with its dispatch rows).
2
+ // Types, not interfaces, so they satisfy Block's index signature.
3
+ // Thread/node shapes stay in types.ts and are re-exported so v2 code has one
4
+ // import. Validators are structural; a message is only trusted once
5
+ // verifyMessage passes, which needs nothing but the record itself.
6
+ import { isFilterRef } from "./dispatch.js";
7
+ import { createHash } from "node:crypto";
8
+ import { PublicKey, Signature } from "@bsv/sdk";
9
+ import { CID, decode, encode, isCID } from "./cid.js";
10
+ const IDENTITY = /^0[23][0-9a-f]{64}$/;
11
+ export const isIdentity = (x) => typeof x === "string" && IDENTITY.test(x);
12
+ /** The signed bytes: dag-cbor of the record with `sig` removed (every other field is covered). */
13
+ export function messageBytes(m) {
14
+ const { sig: _, ...rest } = m;
15
+ return encode(rest).bytes;
16
+ }
17
+ /** What ECDSA signs: sha256(messageBytes(m)). */
18
+ export function messageDigest(m) {
19
+ return new Uint8Array(createHash("sha256").update(messageBytes(m)).digest());
20
+ }
21
+ export async function signMessage(signer, m) {
22
+ // Round-trip through dag-cbor so the record we return is exactly what gets
23
+ // stored and re-encoded at verify time (undefined dropped, CIDs canonical).
24
+ const unsigned = decode(encode({
25
+ kind: "message", from: signer.identity, to: m.to, seq: m.seq, at: m.at, body: m.body, refs: m.refs ?? [],
26
+ }).bytes);
27
+ if (!isMessage({ ...unsigned, sig: new Uint8Array() }))
28
+ throw new TypeError("signMessage: malformed message");
29
+ return { ...unsigned, sig: await signer.sign(messageBytes(unsigned)) };
30
+ }
31
+ export function isMessage(x) {
32
+ if (!isObj(x) || x.kind !== "message")
33
+ return false;
34
+ return isIdentity(x.from)
35
+ && (x.to === undefined || typeof x.to === "string")
36
+ && Number.isSafeInteger(x.seq) && x.seq >= 0
37
+ && typeof x.at === "number"
38
+ && x.body !== undefined
39
+ && Array.isArray(x.refs) && x.refs.every(isRef)
40
+ && x.sig instanceof Uint8Array;
41
+ }
42
+ /** Shape plus signature against `from`. Synchronous for the store's transactions; see verifyMessage. */
43
+ export function verifyMessageSync(m) {
44
+ if (!isMessage(m))
45
+ return false;
46
+ try {
47
+ // PublicKey.verify hashes with sha256 itself, so this checks the signature over messageDigest(m).
48
+ return PublicKey.fromString(m.from).verify([...messageBytes(m)], Signature.fromDER([...m.sig]));
49
+ }
50
+ catch {
51
+ return false; // not a curve point, not DER
52
+ }
53
+ }
54
+ export async function verifyMessage(m) {
55
+ return verifyMessageSync(m);
56
+ }
57
+ export function program(p) {
58
+ const out = { kind: "program", ...p };
59
+ if (!isProgram(out))
60
+ throw new TypeError("program: malformed");
61
+ return out;
62
+ }
63
+ export function isProgram(x) {
64
+ if (!isObj(x) || x.kind !== "program")
65
+ return false;
66
+ const code = x.code;
67
+ const codeOk = isObj(code) && (typeof code.ts === "string") !== isCID(code.wasm);
68
+ return typeof x.name === "string" && x.name !== "" && codeOk
69
+ && x.inputs !== undefined
70
+ && Array.isArray(x.services) && x.services.every((s) => typeof s === "string")
71
+ && typeof x.description === "string";
72
+ }
73
+ // ---------------------------------------------------------------- dispatch row
74
+ /**
75
+ * A route of the route table (#77, #143; dispatch.ts, docs/MESSAGES.md "The
76
+ * dispatch table"): the kernel's rule for a route (dispatch.zig `problem`).
77
+ * `program` is a program record's CID, "kernel" for an admin operation named
78
+ * by `fn`, or absent for a read route (http only: its filters answer). No
79
+ * `sender` (#143: routing only).
80
+ */
81
+ export function isDispatchRow(x) {
82
+ if (!isObj(x))
83
+ return false;
84
+ const t = x.transport;
85
+ if (t !== "mailbox" && t !== "event" && t !== "http" && t !== "libp2p" && t !== "local")
86
+ return false;
87
+ if (x.sender !== undefined)
88
+ return false;
89
+ if (typeof x.address !== "string" || x.address === "" || /[ \0]/.test(x.address))
90
+ return false;
91
+ if (x.prefix !== undefined && x.prefix !== null && x.prefix !== false && !(x.prefix === true && t === "http"))
92
+ return false;
93
+ const filters = x.filters === undefined || x.filters === null ? [] : x.filters;
94
+ if (!Array.isArray(filters) || !filters.every(isFilterRef))
95
+ return false;
96
+ if (filters.length && (t === "local" || (t === "event" && !(filters.length === 1 && filters[0] === "kernel.beef"))))
97
+ return false;
98
+ if (x.fn !== undefined && x.fn !== null && typeof x.fn !== "string")
99
+ return false;
100
+ // A read route (#143): http, or a libp2p stream protocol (#154: `/…`; a topic carries no answer).
101
+ if (x.program === undefined)
102
+ return (t === "http" || (t === "libp2p" && x.address.startsWith("/"))) && filters.length > 0 && x.fn === undefined;
103
+ if (x.program === "kernel")
104
+ return t === "mailbox" && filters.length === 0 && typeof x.fn === "string" && KERNEL_OPS.includes(x.fn);
105
+ return isCID(x.program);
106
+ }
107
+ /** What a kernel route may name (its `fn`): the admin operations (#143: `grant` among them), the claim (#89: an image's one route; root to the claimant, the route removed), and the host's tick (#130). */
108
+ export const KERNEL_OPS = ["objects", "head", "dispatch", "peers", "grant", "claim", "tick"];
109
+ export function isGenesis(x) {
110
+ return isObj(x) && x.kind === "genesis" && isIdentity(x.identity) && x.owner === undefined && (x.root === undefined || (Array.isArray(x.root) && x.root.every(isIdentity))) && (x.host === undefined || isIdentity(x.host))
111
+ && (x.roles === undefined || (isObj(x.roles) && Object.values(x.roles).every((v) => Array.isArray(v) && v.every((f) => typeof f === "string"))))
112
+ && typeof x.handle === "string" && typeof x.domain === "string"
113
+ && isObj(x.programs) && Object.values(x.programs).every(isCID)
114
+ && Array.isArray(x.dispatch) && x.dispatch.every(isDispatchRow)
115
+ && (x.scopes === undefined || (isObj(x.scopes) && Object.values(x.scopes).every((v) => Array.isArray(v) && v.every((h) => typeof h === "string" && h !== ""))))
116
+ && (x.peers === undefined || (isObj(x.peers) && Object.values(x.peers).every(isIdentity)))
117
+ && (x.defaults === undefined || (isObj(x.defaults) && Object.values(x.defaults).every((v) => typeof v === "string")))
118
+ && (x.names === undefined || (isObj(x.names) && Object.entries(x.names).every(([k, v]) => isIdentity(k) && isObj(v) && typeof v.handle === "string" && typeof v.domain === "string")))
119
+ && (x.collect === undefined || (Array.isArray(x.collect) && x.collect.every((b) => typeof b === "string" && b !== "")));
120
+ }
121
+ export function isEmit(x) {
122
+ return isObj(x) && x.kind === "emit" && isIdentity(x.to) && typeof x.box === "string" && x.box !== "" && isCID(x.body)
123
+ && isObj(x.envelope) && typeof x.envelope.content === "string";
124
+ }
125
+ export function isSignerCall(x) {
126
+ return isObj(x) && x.kind === "oracle" && isCID(x.thread) && typeof x.step === "number" && typeof x.i === "number"
127
+ && x.request instanceof Uint8Array && x.result instanceof Uint8Array;
128
+ }
129
+ /**
130
+ * Wallet wire calls a program may make (BRC-100 call codes): key derivation
131
+ * and crypto only — no actions, no certificates (the kernel's scheduler.zig
132
+ * `wallet_calls`). By code, for showing a signer call.
133
+ */
134
+ export const WALLET_CALLS = new Map([
135
+ [8, "getPublicKey"], [11, "encrypt"], [12, "decrypt"], [13, "createHmac"], [14, "verifyHmac"], [15, "createSignature"], [16, "verifySignature"],
136
+ ]);
137
+ function isObj(v) {
138
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !isCID(v) && !(v instanceof Uint8Array);
139
+ }
140
+ function isPlain(v) {
141
+ if (!isObj(v))
142
+ return false;
143
+ const proto = Object.getPrototypeOf(v);
144
+ return proto === Object.prototype || proto === null;
145
+ }
146
+ function isRef(r) {
147
+ return isObj(r) && (isCID(r.to) || typeof r.to === "string") && typeof r.rel === "string"
148
+ && (r.locator === undefined || typeof r.locator === "string");
149
+ }
@@ -0,0 +1,33 @@
1
+ import { type SQLOutputValue } from "node:sqlite";
2
+ import { CID } from "./cid.ts";
3
+ import { type Store } from "./store.ts";
4
+ import type { Ref } from "./types.ts";
5
+ type Obj = Record<string, unknown>;
6
+ export interface EdgeRow {
7
+ to: string;
8
+ rel: string;
9
+ locator: string | null;
10
+ }
11
+ /** The SQLite store adds prefix lookup over origins (for the CLI's short CIDs). */
12
+ export interface SqliteStore extends Store {
13
+ findByPrefix(prefix: string): Promise<CID[]>;
14
+ }
15
+ /**
16
+ * `readOnly`: open an existing file for reading only (the explorer beside a
17
+ * live runtime): no schema, no migration, no journal change; writes throw.
18
+ */
19
+ export declare function openStore(path: string, o?: {
20
+ readOnly?: boolean;
21
+ }): SqliteStore;
22
+ export declare function originEdges(b: Obj): EdgeRow[];
23
+ export declare function updateEdges(u: Obj): EdgeRow[];
24
+ /**
25
+ * The edges an update's kept records declare (#37, the Zig kernel's
26
+ * index.zig `updateEdgesWithKept`): each kept record with `refs: [{to, rel,
27
+ * locator?}]` gives an edge with the record's own rel, after the update's own
28
+ * edges. `kept` is the update's kept records, resolved (undefined: not held
29
+ * or not a dag-cbor map). Readers of a Zig store use it.
30
+ */
31
+ export declare function keptEdges(kept: unknown[]): EdgeRow[];
32
+ export declare function toRef(to: CID | string, rel: string, locator: SQLOutputValue): Ref;
33
+ export {};