sememe 0.4.25 → 0.4.27

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A local meaning layer for Solana bytes. `sememe pull <program>` fetches and verifies a program interface; every process on the machine can then decode instructions, accounts, and events offline.
4
4
 
5
- **Status: v0.4.25 — published on the npm registry AND as a git tag. Pull/store plus an offline decoder for instructions, Anchor event self-CPIs and account state, verified against real mainnet programs; union decode across a program's on-chain IDL and its adopted first-party published copy (`sememe adopt` + `sememe decode --union`); the fingerprint lockfile ships (`sememe lock` + `sememe verify`); the decode path installs zero runtime dependencies. The ecosystem-wide measured coverage is 88.8% (18,967/21,350 payloads across 6 finalized blocks, 2026-11-13; 96.4% on the curated watchlist, 63.8% outside it; byte-exact re-encode on 93.1% of decoded). The honest limits ride with every number: zero external users so far — publish is not consumption — one machine, no audit.**
5
+ **Status: v0.4.27 — published on the npm registry AND as a git tag. Pull/store plus an offline decoder for instructions, Anchor event self-CPIs and account state, verified against real mainnet programs; union decode across a program's on-chain IDL and its adopted first-party published copy (`sememe adopt` + `sememe decode --union`); the fingerprint lockfile ships (`sememe lock` + `sememe verify`); the decode path installs zero runtime dependencies. The ecosystem-wide measured coverage is 88.8% (18,967/21,350 payloads across 6 finalized blocks, 2026-11-13; 96.4% on the curated watchlist, 63.8% outside it; byte-exact re-encode on 93.1% of decoded). The honest limits ride with every number: zero external users so far — publish is not consumption — one machine, no audit.**
6
6
 
7
7
  Everything below the fold is what you run. The full measurement and qualification history — every number, artifact, defect found and fixed, and every honest limit — was moved verbatim to [docs/measured-history.md](https://github.com/auroraxo/sememe/blob/main/docs/measured-history.md) on 2026-11-13; nothing was deleted.
8
8
 
@@ -56,7 +56,7 @@ Measured on that sample (2026-11-04): 1,000 checked transactions, 6,337 payloads
56
56
  ## Install (as a consumer, not a contributor)
57
57
 
58
58
  ```bash
59
- npm install github:auroraxo/sememe#v0.4.25 # tagged release; no npm account needed
59
+ npm install github:auroraxo/sememe#v0.4.27 # tagged release; no npm account needed
60
60
  ```
61
61
 
62
62
  ```js
@@ -75,14 +75,14 @@ The **library** refuses the same way: importing `sememe/pull` or `sememe/gap` wi
75
75
  **From an install (not a clone), the CLI is `npx sememe …`** — npm exec resolves the package's own `bin` from `node_modules`; the bare command `sememe` is not on your `PATH` after a plain `npm install`, and `node bin/sememe.mjs` works only inside a clone of the repository (both measured 2026-11-05 from a real tarball install). The consumer journey from the install, verbatim:
76
76
 
77
77
  ```bash
78
- npm install github:auroraxo/sememe#v0.4.25 @solana/web3.js # acquisition needs the peer
78
+ npm install github:auroraxo/sememe#v0.4.27 @solana/web3.js # acquisition needs the peer
79
79
  npx sememe pull whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc --store ./store
80
80
  npx sememe decode whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc 2b04ed0b1ac91e62a40d0100000000000100000000000000af331ba8327fbb35b1c4feff00000000010001010000000601 --store ./store
81
81
  ```
82
82
 
83
83
  `./store` is the local content-addressed store the error `no sememe store at ./store: run "sememe pull" first` tells you to create — the `pull` above is that creation step; the vendored programs (System, ComputeBudget, SPL Token, Token-2022, Raydium AMM v4, SPL Associated Token Account) need no store at all: `npx sememe decode-native 11111111111111111111111111111111 <payload>` answers offline from the pinned upstream tables. If you ever point the decoder at a store directory that does not exist yet, the refusal names the fix; `npx sememe` is the form of that fix that runs where you are. Measured 2026-11-05 from the packed tarball of this tree: install (zero deps) → `npx sememe pull` (live mainnet) → `npx sememe decode` (byte-exact, live-pulled object byte-identical to the fixture store object `0b886e37…660f`).
84
84
 
85
- Publishing to the npm registry changed on 2026-11-12: the `sememe` name is now live on registry.npmjs.org (first shipped as 0.4.21; the measured registry install of `sememe@0.4.22` into an empty app decoded the Whirlpool fixture payload `swap_v2`, `roundTripByteExact: true`). `npm install sememe` and `npm install github:auroraxo/sememe#v0.4.25` deliver the same product files from the same code: the files allowlist already ships none of this repository's tests, fixtures or qualification harness (pinned by `test/package.test.mjs`), so the git tag remains the release artifact — `test/package.test.mjs` (the contract above), `tools/e2e-package-consumer.mjs` (a real install into an empty directory) and `test/install-surface.test.mjs` (the shipped README's install commands run verbatim from the packed tarball) keep both channels honest.
85
+ Publishing to the npm registry changed on 2026-11-12: the `sememe` name is now live on registry.npmjs.org (first shipped as 0.4.21; the measured registry install of `sememe@0.4.22` into an empty app decoded the Whirlpool fixture payload `swap_v2`, `roundTripByteExact: true`). `npm install sememe` and `npm install github:auroraxo/sememe#v0.4.27` deliver the same product files from the same code: the files allowlist already ships none of this repository's tests, fixtures or qualification harness (pinned by `test/package.test.mjs`), so the git tag remains the release artifact — `test/package.test.mjs` (the contract above), `tools/e2e-package-consumer.mjs` (a real install into an empty directory) and `test/install-surface.test.mjs` (the shipped README's install commands run verbatim from the packed tarball) keep both channels honest.
86
86
 
87
87
  ## Working now: pin the answers (`sememe lock` + `sememe verify`, 2026-11-05)
88
88
 
@@ -110,6 +110,41 @@ npx sememe verify --store ./store --lock ./sememe.lock
110
110
 
111
111
  What the lock does NOT do: it is not a freshness check against the chain (the pin says "this is what I answered", not "this is what the program is now"); it does not cover vendored programs (System/ComputeBudget/SPL Token/Token-2022/Raydium AMM v4/SPL Associated Token Account have no receipts, they are pinned from upstream sources instead); and it cannot stop you from re-pulling — it makes every re-pull that changes an answer visible.
112
112
 
113
+ ## Working now: when the on-chain interface copy is complete but incomplete on the wire (`sememe adopt-hadron`, 2026-11-14)
114
+
115
+ Most interface gaps are "no interface exists at all" — the ATA, Lighthouse and Manifest cases, closed by a derived wire table or a vendored first-party copy. HADRo (`HADRoNbLovyqhCsocfYQYB7QdfCAAinN9HTePvBCVDQ8`) is the other kind: the on-chain metadata IDL declares **all 38 instructions and every type**, and it is still incomplete **on the wire**. It lacks exactly two argument declarations — `sequence: u64`, last on `setCurve` and `setRiskCurve` — and the wire carries those 8 bytes after the points.
116
+
117
+ Measured on the 144 committed chain-accepted payloads of those two instructions:
118
+
119
+ | from the on-chain copy alone | |
120
+ |---|---|
121
+ | decoded | 144/144, 0 refused |
122
+ | payloads ending in unexplained bytes | 144/144 (8 bytes each) |
123
+ | **re-encoding byte-exactly** | **0/144** |
124
+ | refused under `--trailing error` | 144/144 |
125
+
126
+ So a consumer had no path to byte-exactness on real mainnet traffic, and `--trailing error` refused every one of those payloads outright.
127
+
128
+ ```bash
129
+ npx sememe adopt-hadron --store ./store
130
+ npx sememe decode --union HADRoNbLovyqhCsocfYQYB7QdfCAAinN9HTePvBCVDQ8 <payload> --store ./store
131
+ ```
132
+
133
+ `adopt-hadron` adopts the program's **first-party published copy** (`Hadron-Fi/hadron-tx-verifier`, Apache-2.0 AND MIT) from a sha256-pinned vendored file, hash-checked **before** any write, through the product's own write path. Afterwards the union reproduces **144/144 payloads byte-exactly**, 0 unexplained bytes, 0 refusals under `--trailing error` — and *names* the disagreement with the on-chain copy instead of silently picking one (`verdict: layout_drift`, drift path `sequence`, arbitration basis `byte_evidence_full_roundtrip`).
134
+
135
+ Neither half of this was sufficient alone, and that was measured rather than argued (`research/hadron-adoption-2026-11-14.json`, four faces over the same 144 payloads):
136
+
137
+ | face | decoded | refused | byte-exact |
138
+ |---|---|---|---|
139
+ | before the header-count rule + on-chain IDL | 72 | 72 | 0 |
140
+ | before the header-count rule + first-party IDL | 72 | 72 | 0 |
141
+ | after the header-count rule + on-chain IDL | 144 | 0 | 0 |
142
+ | after the header-count rule + first-party IDL | 144 | 0 | **144** |
143
+
144
+ The second row is the falsifier for "just adopt the source IDL": the first-party copy declares `points` as a Borsh vector too, so on its own it refuses the same 72 payloads. The third row is the falsifier for "the header rule alone is the fix": it stops the refusals but drops the 8-byte tail, so nothing round-trips. Only the composition reproduces every byte.
145
+
146
+ What it does **not** do: plain `sememe decode` keeps the on-chain receipt as its default answer (measured unchanged on both sides of the adoption — the adopted receipt sorts at slot 0, so it never wins the single-source read), and the receipt's origin stays `declared`: a commit is content-addressed, not signed, and nothing here proves the bytes describe the deployed program. Chain proof stays with the decode — every payload must re-encode byte-exactly or the union refuses. See [docs/measured-history.md](https://github.com/auroraxo/sememe/blob/main/docs/measured-history.md) and [research/hadron-adoption-2026-11-14.json](research/hadron-adoption-2026-11-14.json).
147
+
113
148
  ## Where the numbers come from
114
149
 
115
150
  Every claim above is pinned by a test and backed by a committed artifact. If you want to know **why** a number is what it is, and what was measured wrong before it was measured right:
package/bin/sememe.mjs CHANGED
@@ -52,6 +52,18 @@ const usage = `usage:
52
52
  (L2TExMFK…, 1,214 payloads in the committed 6-block window) from the
53
53
  vendored first-party copy (Jac0xb/lighthouse, MIT), hash-checked against
54
54
  its pin BEFORE any write; the receipt's origin stays declared.
55
+ sememe adopt-hadron [--store <directory>]
56
+ the measured case where the ON-CHAIN interface copy is complete but
57
+ incomplete ON THE WIRE: HADRo (HADRoNbLovy…) declares every instruction
58
+ and type, yet lacks the trailing "sequence: u64" of setCurve and
59
+ setRiskCurve, so 144 committed chain-accepted payloads decode with 8
60
+ unexplained trailing bytes and 0 of them re-encode byte-exactly
61
+ (research/hadron-adoption-2026-11-14.json). This adopts the first-party
62
+ published copy (Hadron-Fi/hadron-tx-verifier, Apache-2.0 AND MIT) from a
63
+ sha256-pinned vendored file, hash-checked BEFORE any write. Afterwards
64
+ "sememe decode --union" reproduces 144/144 payloads byte-exactly and
65
+ NAMES the disagreement with the on-chain copy (drift path "sequence").
66
+ Plain "sememe decode" keeps the on-chain receipt as its answer.
55
67
  sememe lock [--store <directory>] [--out <file>]
56
68
  snapshot the WHOLE store into a sememe.lock document: for every program,
57
69
  the exact fingerprint its newest verified receipt answered with, plus
@@ -279,6 +291,23 @@ try {
279
291
  path: result.receipt.path,
280
292
  receiptPath: result.receipt.receiptPath,
281
293
  }, null, 2));
294
+ } else if (command === 'adopt-hadron') {
295
+ const store = option('--store', path.join(os.homedir(), '.sememe'));
296
+ const { adoptHadron } = await import('../src/hadron.mjs');
297
+ const { adopt: adoptFn } = await import('../src/adopt.mjs');
298
+ const result = await adoptHadron(store, { adopt: adoptFn });
299
+ console.log(JSON.stringify({
300
+ program: result.receipt.program,
301
+ source: result.receipt.source,
302
+ origin: result.receipt.origin,
303
+ originStatus: result.receipt.originStatus,
304
+ originNote: result.receipt.originNote,
305
+ fingerprint: result.receipt.fingerprint,
306
+ instructionCount: result.receipt.instructionCount,
307
+ path: result.receipt.path,
308
+ receiptPath: result.receipt.receiptPath,
309
+ note: 'the on-chain receipt still answers plain "sememe decode"; use "sememe decode --union" to let the two sources arbitrate by bytes',
310
+ }, null, 2));
282
311
  } else if (command === 'decode') {
283
312
  if (!program) throw Error('decode needs a program id');
284
313
  const payload = args.shift();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sememe",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "description": "Local, fingerprint-verified Solana program interfaces: pull an IDL once, then decode instructions, accounts and events offline. Zero dependencies on the decode path. Ships derived wire tables for System, ComputeBudget, SPL Token, Token-2022 (incl. its token-metadata fallback), Raydium AMM v4 and the SPL Associated Token Account, pinned to upstream Rust.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,6 +24,7 @@
24
24
  "./raydium": "./src/raydium.mjs",
25
25
  "./ata": "./src/ata.mjs",
26
26
  "./lighthouse": "./src/lighthouse.mjs",
27
+ "./hadron": "./src/hadron.mjs",
27
28
  "./gap": "./src/gap.mjs"
28
29
  },
29
30
  "files": [
@@ -100,6 +100,10 @@ export const COMMAND_SPECS = {
100
100
  flags: { '--store': 'value' },
101
101
  positionals: 0,
102
102
  },
103
+ 'adopt-hadron': {
104
+ flags: { '--store': 'value' },
105
+ positionals: 0,
106
+ },
103
107
  decode: {
104
108
  flags: { '--store': 'value', '--trailing': 'value', '--absent': 'value', '--union': 'boolean' },
105
109
  positionals: 2,
package/src/codec.mjs CHANGED
@@ -365,7 +365,7 @@ function decodeAt(type, cur, ctx, path, depth) {
365
365
  // keeps the strict prefix codec byte for byte.
366
366
  if (ctx.__countFromHeader && ctx.__countFromHeader.vectorPath === path) {
367
367
  const length = ctx.__countFromHeader.count;
368
- const elementSize = ctx.__countFromHeader.pointBytes;
368
+ const elementSize = ctx.__countFromHeader.elementBytes;
369
369
  const needed = length * elementSize;
370
370
  if (needed > cur.remaining) {
371
371
  // The measured count does not fit the remaining bytes: this is NOT a
@@ -1167,7 +1167,7 @@ export function decodeInstructionData(idl, data, { allowTrailing, trailing, abse
1167
1167
  countArg: headerRule.countArg,
1168
1168
  vectorArg: headerRule.vectorArg,
1169
1169
  vectorPath: `${instruction.name}.${headerRule.vectorArg}`,
1170
- pointBytes: headerRule.pointBytes,
1170
+ elementBytes: headerRule.elementBytes,
1171
1171
  instruction: instruction.name,
1172
1172
  };
1173
1173
  }
@@ -1365,7 +1365,7 @@ export function encodeInstructionData(idl, name, args, { absent } = {}) {
1365
1365
  countArg: headerRule.countArg,
1366
1366
  vectorArg: headerRule.vectorArg,
1367
1367
  vectorPath: `${instruction.name}.${headerRule.vectorArg}`,
1368
- pointBytes: headerRule.pointBytes,
1368
+ elementBytes: headerRule.elementBytes,
1369
1369
  instruction: instruction.name,
1370
1370
  };
1371
1371
  }
package/src/hadron.mjs ADDED
@@ -0,0 +1,228 @@
1
+ // The HADRo acquisition: a pinned, hash-verified first-party published IDL for
2
+ // the ONE program sememe can decode but NOT fully re-encode from the on-chain
3
+ // copy (measured 2026-11-14, HADRoNbLovyqhCsocfYQYB7QdfCAAinN9HTePvBCVDQ8,
4
+ // research/hadron-adoption-2026-11-14.json).
5
+ //
6
+ // THE MEASURED GAP (this is the reason the file exists, not a preference):
7
+ //
8
+ // The on-chain metadata IDL (pulled at slot 468262003) is COMPLETE on
9
+ // instructions and types, but it is missing exactly two argument declarations
10
+ // — `sequence: u64` as the last argument of setCurve and setRiskCurve. The
11
+ // wire carries those 8 bytes after the points, so with the stored copy alone:
12
+ //
13
+ // 144/144 committed chain-accepted payloads decode (0 refused)
14
+ // 144/144 end with 8 UNEXPLAINED trailing bytes
15
+ // 0/144 round-trip byte-exact (the tail is dropped)
16
+ // 144/144 are REFUSED by --trailing error
17
+ //
18
+ // A user who wants byte-exactness has no fix available on that path: the bytes
19
+ // are real, the IDL simply does not declare them. `sememe adopt-hadron` puts
20
+ // the first-party published copy (Hadron-Fi/hadron-tx-verifier, which declares
21
+ // the two `sequence: u64` tails) into the store beside the on-chain receipt, so
22
+ // `sememe decode --union` arbitrates BY BYTES: the published copy then
23
+ // reproduces 144/144 payloads byte-exact with 0 trailing bytes, and the drift
24
+ // against the on-chain copy is reported by NAME (paths: ["sequence"]) instead
25
+ // of silently choosing one source.
26
+ //
27
+ // WHAT THE PIN DOES NOT CLAIM (src/adopt.mjs says this on every surface): the
28
+ // receipt's origin stays `declared` — the vendored bytes are bound to a commit
29
+ // by sha256, but a commit is content-addressed, not signed, and nothing here
30
+ // proves the bytes describe the DEPLOYED program. Chain proof stays with the
31
+ // decode: every payload must re-encode byte-exactly or union refuses.
32
+ //
33
+ // WHAT SHIPPING THE COPY DOES NOT CHANGE: plain `sememe decode` still reads
34
+ // the on-chain receipt (slot 0 sorts before every real slot — the rule
35
+ // src/adopt.mjs measures and pins). Adoption widens what `--union` can
36
+ // arbitrate; it never silently rewrites the default single-source answer.
37
+
38
+ import { readFileSync } from 'node:fs';
39
+ import { createHash } from 'node:crypto';
40
+ import path from 'node:path';
41
+ import { fileURLToPath } from 'node:url';
42
+
43
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
44
+ export const REPO_ROOT = path.resolve(HERE, '..');
45
+
46
+ export const HADRON_PROGRAM = 'HADRoNbLovyqhCsocfYQYB7QdfCAAinN9HTePvBCVDQ8';
47
+
48
+ export const HADRON_PIN = {
49
+ repo: 'Hadron-Fi/hadron-tx-verifier',
50
+ commit: '0c1bca1d8c9bc2abaa1b3d651d450c814cdd6328',
51
+ // Dual-licensed: the repo carries LICENSE-APACHE + LICENSE-MIT + NOTICE, all
52
+ // three vendored beside the IDL so a redistributor of the npm tarball has
53
+ // the terms and the attribution NOTICE in the same directory as the bytes.
54
+ license: 'Apache-2.0 AND MIT',
55
+ // sha256 of the bytes AS PUBLISHED at that commit, read via the GitHub
56
+ // contents API with `?ref=<commit>` — not from a mutable working tree.
57
+ files: [
58
+ {
59
+ file: 'idl/hadron.json',
60
+ role: 'first-party published IDL: the copy that declares the two sequence:u64 tails the on-chain metadata IDL lacks',
61
+ sha256: '219f44fb63729232b3685fbde49a817b0c8321a0203dcca68c88370fbed1279e',
62
+ },
63
+ {
64
+ file: 'LICENSE-APACHE',
65
+ role: 'Apache-2.0 terms the vendored copy ships under',
66
+ sha256: 'e4cbaf2699b8d6509727edfae292eabe12249f8a31d4dc978de59e15d12ec717',
67
+ },
68
+ {
69
+ file: 'LICENSE-MIT',
70
+ role: 'MIT terms the vendored copy ships under',
71
+ sha256: '81382b018e2877fc1f037cc427ecb62333e653ddd05e6e1ff141b184735c7d92',
72
+ },
73
+ {
74
+ file: 'NOTICE',
75
+ role: 'attribution NOTICE (fork of multisig-verifier, Squads Labs; Hadron modifications)',
76
+ sha256: 'e39dc25965a71c43d8e475edf3b123f6028239ba612026065d43a5f124dfc36f',
77
+ },
78
+ ],
79
+ // The FULL provenance string, as documentation. It is 96 characters, so
80
+ // `sememe adopt`'s own label sanitizer would TRUNCATE it to 64 (that is
81
+ // where the existing lighthouse origin loses its commit tail and its file
82
+ // path — a pre-existing shape this module does not copy). The label
83
+ // actually recorded is hadronOriginLabel() below, which FITS in 64 and
84
+ // therefore loses nothing.
85
+ origin: 'github.com/Hadron-Fi/hadron-tx-verifier@0c1bca1d8c9bc2abaa1b3d651d450c814cdd6328:idl/hadron.json',
86
+ };
87
+
88
+ /** The two arguments this acquisition exists to supply. They are the entire
89
+ * difference between an IDL that reports the wire's 8-byte tail as unexplained
90
+ * bytes and one that names it — measured, see research/hadron-adoption-*.json. */
91
+ export const HADRON_MISSING_ARGS = [
92
+ { instruction: 'setCurve', arg: 'sequence', type: 'u64' },
93
+ { instruction: 'setRiskCurve', arg: 'sequence', type: 'u64' },
94
+ ];
95
+
96
+ export function vendoredHadronPath(file, root = REPO_ROOT) {
97
+ return path.join(root, 'vendor', 'hadron', HADRON_PIN.commit, path.basename(file));
98
+ }
99
+
100
+ export function sha256File(filePath) {
101
+ return createHash('sha256').update(readFileSync(filePath)).digest('hex');
102
+ }
103
+
104
+ export class HadronPinError extends Error {
105
+ constructor(reason, detail) {
106
+ super(detail ? `${reason}: ${detail}` : reason);
107
+ this.name = 'HadronPinError';
108
+ this.reason = reason;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Read the vendored IDL and verify it against the pin. Every refusal is NAMED
114
+ * so a caller can tally failure faces without parsing prose.
115
+ *
116
+ * Returns { idl, path, sha256, bytes, declaredMissing } on success, where
117
+ * `declaredMissing` is the list of HADRON_MISSING_ARGS entries this copy
118
+ * actually declares — the measured reason for adopting it.
119
+ */
120
+ export function loadVendoredHadron(root = REPO_ROOT, pin = HADRON_PIN) {
121
+ const pinned = pin.files.find((f) => f.file.endsWith('hadron.json'));
122
+ const vendoredPath = path.join(root, 'vendor', 'hadron', pin.commit, path.basename(pinned.file));
123
+ let raw;
124
+ try {
125
+ raw = readFileSync(vendoredPath);
126
+ } catch (error) {
127
+ throw new HadronPinError(
128
+ 'hadron-vendor-missing',
129
+ `${vendoredPath} is not present (${error.code ?? error.message})`,
130
+ );
131
+ }
132
+ const now = createHash('sha256').update(raw).digest('hex');
133
+ if (now !== pinned.sha256) {
134
+ throw new HadronPinError(
135
+ 'hadron-vendor-hash-mismatch',
136
+ `pinned ${pinned.sha256}, hashed now ${now} — refusing to adopt bytes that are not the reviewed copy`,
137
+ );
138
+ }
139
+ let idl;
140
+ try {
141
+ idl = JSON.parse(raw.toString('utf8'));
142
+ } catch (error) {
143
+ throw new HadronPinError('hadron-vendor-unparseable', String(error.message ?? error));
144
+ }
145
+ if (!idl || typeof idl !== 'object' || Array.isArray(idl)) {
146
+ throw new HadronPinError('hadron-vendor-unparseable', 'not an IDL object');
147
+ }
148
+ // Address binding: this file claims HADRo by its own `address`, the same
149
+ // field the on-chain receipt binds. A file declaring another program is
150
+ // never re-labelled (the rule src/adopt.mjs measures: a declared program
151
+ // that differs from the receipt's program is a re-labelling, refused).
152
+ if (idl.address !== HADRON_PROGRAM) {
153
+ throw new HadronPinError(
154
+ 'hadron-address-disagrees',
155
+ `vendored file declares ${idl.address ?? 'nothing'}, pin binds ${HADRON_PROGRAM}`,
156
+ );
157
+ }
158
+ if (!Array.isArray(idl.instructions) || idl.instructions.length === 0) {
159
+ throw new HadronPinError('hadron-no-instructions', 'not an interface copy');
160
+ }
161
+ // THE MEASURED REASON FOR SHIPPING THIS FILE: each of the two arguments the
162
+ // on-chain copy lacks must be present, last, and typed u64. A copy that lost
163
+ // them would still hash correctly against nothing in particular — the pin
164
+ // covers BYTES, this check covers the CLAIM those bytes make.
165
+ const byName = new Map(idl.instructions.map((instruction) => [instruction.name, instruction]));
166
+ const declaredMissing = [];
167
+ for (const expected of HADRON_MISSING_ARGS) {
168
+ const instruction = byName.get(expected.instruction);
169
+ if (!instruction) {
170
+ throw new HadronPinError('hadron-instruction-missing', `${expected.instruction} is not in the vendored copy`);
171
+ }
172
+ const args = Array.isArray(instruction.args) ? instruction.args : [];
173
+ const last = args[args.length - 1];
174
+ if (last?.name === expected.arg && last?.type === expected.type) {
175
+ declaredMissing.push({ ...expected });
176
+ }
177
+ // A copy WITHOUT the argument is still adopted honestly — it just cannot
178
+ // claim the reason this module exists, so `declaredMissing` stays short
179
+ // and the adopting caller reports the shortfall instead of the promise.
180
+ }
181
+ if (declaredMissing.length !== HADRON_MISSING_ARGS.length) {
182
+ throw new HadronPinError(
183
+ 'hadron-tail-args-absent',
184
+ `this copy declares ${declaredMissing.length}/${HADRON_MISSING_ARGS.length} of the sequence:u64 tails `
185
+ + `the acquisition exists for (${HADRON_MISSING_ARGS.map((a) => `${a.instruction}.${a.arg}`).join(', ')}) `
186
+ + '— adopting it would promise byte-exactness it cannot deliver',
187
+ );
188
+ }
189
+ return { idl, path: vendoredPath, sha256: now, bytes: raw.length, declaredMissing };
190
+ }
191
+
192
+ /**
193
+ * The origin label adopt records.
194
+ *
195
+ * `sememe adopt` sanitizes the caller's label to a filename-safe form and
196
+ * caps it at 64 characters (src/adopt.mjs, `safeLabel`). A label longer than
197
+ * that is TRUNCATED, silently: the obvious label for this acquisition
198
+ * (`Hadron-Fi/hadron-tx-verifier@<40-hex commit>:idl/hadron.json`) is 96
199
+ * characters and comes back as a 64-char prefix that has LOST the tail of the
200
+ * commit hash and the whole file path — the same thing that already happens
201
+ * to the 105-character lighthouse origin. A commit truncated to 35 hex
202
+ * characters is not a commit, so this label drops the file path and the owner
203
+ * on purpose and keeps the FULL 40-hex commit: 59 characters, recorded whole.
204
+ * The owner and the file are in HADRON_PIN, beside the sha256 of every
205
+ * vendored byte — and the commit here is the directory the bytes live in.
206
+ */
207
+ export function hadronOriginLabel() {
208
+ return `hadron-tx-verifier@${HADRON_PIN.commit}`;
209
+ }
210
+
211
+ /**
212
+ * Adopt the verified first-party copy into a store through the PRODUCT's one
213
+ * write path (src/adopt.mjs — the same path `sememe adopt` uses).
214
+ *
215
+ * Order is the point: the pin is checked FIRST, so a tampered, missing or
216
+ * tail-less vendored file means NOTHING was written. The receipt's origin
217
+ * stays `declared`: the pin binds the BYTES to a commit, not the bytes to the
218
+ * deployed program.
219
+ */
220
+ export async function adoptHadron(storeDir, { adopt } = {}) {
221
+ if (typeof adopt !== 'function') throw Error('adoptHadron needs the product write path (src/adopt.mjs)');
222
+ const loaded = loadVendoredHadron();
223
+ const receipt = await adopt(HADRON_PROGRAM, loaded.path, storeDir, {
224
+ source: 'published',
225
+ origin: hadronOriginLabel(),
226
+ });
227
+ return { receipt, loaded };
228
+ }
@@ -49,21 +49,25 @@
49
49
  // tail after the points is 8 bytes for all 144 (an undeclared field the IDL
50
50
  // does not name — reported as trailing, never invented).
51
51
 
52
- const POINT_BYTES = 21;
53
-
54
52
  /**
55
53
  * The measured count-from-header table, keyed by program address.
56
54
  *
57
55
  * Every entry is a program+instruction pair whose wire layout was measured
58
56
  * against chain-accepted payloads with a committed artifact; nothing here is
59
- * inferred from an IDL claim or from a shape match.
57
+ * inferred from an IDL claim or from a shape match. `elementBytes` is the
58
+ * measured size of ONE vector element — a property of the element TYPE the
59
+ * measurement walked, never a global constant: CurvePointInput is 21 bytes
60
+ * (research/hadro-vec-wire-2026-11-13.json: 144/144 payloads fit n x 21),
61
+ * SpreadTriggerInput is 66 (research/live-layout-scan-hadro-witness-
62
+ * 2026-11-14.json: 2/2 chain-accepted live payloads numTriggers=5 x 66 =
63
+ * 330 body bytes, trailingBytes 0).
60
64
  */
61
65
  export const COUNT_FROM_HEADER = {
62
66
  HADRoNbLovyqhCsocfYQYB7QdfCAAinN9HTePvBCVDQ8: {
63
- source: 'research/hadro-vec-wire-2026-11-13.json',
64
67
  instructions: {
65
- setCurve: { countArg: 'numPoints', vectorArg: 'points' },
66
- setRiskCurve: { countArg: 'numPoints', vectorArg: 'points' },
68
+ setCurve: { countArg: 'numPoints', vectorArg: 'points', elementBytes: 21, source: 'research/hadro-vec-wire-2026-11-13.json' },
69
+ setRiskCurve: { countArg: 'numPoints', vectorArg: 'points', elementBytes: 21, source: 'research/hadro-vec-wire-2026-11-13.json' },
70
+ updateSpreadConfig: { countArg: 'numTriggers', vectorArg: 'triggers', elementBytes: 66, source: 'research/live-layout-scan-hadro-witness-2026-11-14.json' },
67
71
  },
68
72
  },
69
73
  };
@@ -79,10 +83,10 @@ export const COUNT_FROM_HEADER = {
79
83
  *
80
84
  * @param {object} idl the IDL being decoded (must carry `address`)
81
85
  * @param {string} name the instruction name being decoded/encoded
82
- * @returns {{countArg: string, vectorArg: string, pointBytes: number}|null}
86
+ * @returns {{countArg: string, vectorArg: string, elementBytes: number, source: string}|null}
83
87
  */
84
88
  export function countFromHeader(idl, name) {
85
89
  const entry = COUNT_FROM_HEADER[idl?.address]?.instructions?.[name];
86
90
  if (!entry) return null;
87
- return { ...entry, pointBytes: POINT_BYTES };
91
+ return { ...entry };
88
92
  }
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by the Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding any notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2025 Squads Labs, Inc.
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.