@cyd-prc/dsh-audit-chain 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wang Miaosheng (ORCID: 0009-0003-2767-2421)
4
+
5
+ This package is the DeepSeek Harness binding of `entropy-sdk`
6
+ (https://github.com/CYD-PRC/entropy-sdk), the embeddable distillation of the
7
+ EntropyRuntime paper (arXiv:2607.00334). `lib/core.js` is a JavaScript port of
8
+ that SDK's published core abstractions; the remaining modules bind those
9
+ abstractions to the DeepSeek Harness plugin APIs.
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # dsh-audit-chain — one chain, one implementation
2
+
3
+ The tamper-evident JSONL audit chain shared by the dsh guard plugins
4
+ (`dsh-entropy-guard`, `dsh-shape-guard`, `dsh-threat-gate`). It exists because
5
+ the chain used to live in **two copies** (`entropy-guard`'s `AuditLog` and
6
+ `shape-guard`'s vendored-out `lib/ledger.js`), and copies drift: one grading
7
+ defect (S3) shipped in two published packages, and a lock that one copy had
8
+ (`wx` + fresh tail) took a measured fork incident to reach the other.
9
+
10
+ This package is the end of that drift: **one writer, one grader, one contract.**
11
+
12
+ ## What it is
13
+
14
+ ```js
15
+ import { ChainLog, verifyChain } from '@cyd-prc/dsh-audit-chain';
16
+
17
+ const chain = new ChainLog('/path/to/session.jsonl'); // or null for in-memory
18
+ chain.record('gate_decision', { admitted: true }); // locked, fresh tail
19
+ chain.seal('acknowledged'); // human acknowledgement
20
+ const reading = chain.verify(); // the contract, graded
21
+ ```
22
+
23
+ - **Writer** — every append takes an exclusive lock (`<chain>.lock`: atomic `wx`
24
+ create, stale-broken past a timeout, fail-closed when held too long), re-reads
25
+ the on-disk tail *inside* the lock, and terminates a crash-torn tail line
26
+ before writing. Two plugins sharing one chain cannot fork it.
27
+ - **Grader** — `verify()` implements the shared chain-grading contract, eight
28
+ clauses: content re-hash always, fork-aware links (dangling = rewrite,
29
+ resolvable = branch), forks counted without early exit, seals bounding
30
+ ordering findings (by `seq` for chained axes, by file position for unchained),
31
+ tamper never sealable, corrupt lines reported but never graded on their own.
32
+ - **Byte-compatible** with both historical writers: chains written by
33
+ `entropy-guard ≤ 0.3.3` or `shape-guard ≤ 0.1.6` verify unchanged, and chains
34
+ written by this package verify under both. The hash-payload equivalence is
35
+ pinned by a test, not by prose.
36
+
37
+ ## The contract
38
+
39
+ [`docs/CHAIN-GRADING-CONTRACT.md`](docs/CHAIN-GRADING-CONTRACT.md) ships a
40
+ byte-exact mirror of the grading contract (md5 `500f62b603140477317ed03bec167553`,
41
+ v4). The authoritative copy belongs to the test side; if two copies ever
42
+ disagree, the fingerprint is how you find out, and the test side's reading wins.
43
+
44
+ ## Tools
45
+
46
+ ```
47
+ node tools/verify-chain.mjs <chain.jsonl> [--json] # read-only by construction; exit 0 = verified
48
+ node tools/verify-release.mjs --pack --run-tests # pre-publish self-proof
49
+ node tools/verify-release.mjs --spec @cyd-prc/dsh-audit-chain@<v> --run-tests --wait 120
50
+ ```
51
+
52
+ `verify-chain` has no report mode on purpose: the report path is what made the
53
+ old tool write into the chain it audited (entropy-guard defect 16). This one
54
+ reads and leaves.
55
+
56
+ ## API surface
57
+
58
+ | export | what it is |
59
+ |---|---|
60
+ | `ChainLog` | the chain: `record`, `seal`, `verify`, `entries`, `corruptLines`, `failures`/`lastError` |
61
+ | `verifyChain(path)` | the functional twin of `new ChainLog(path).verify()`, for pure readers |
62
+ | `encodeEntry` / `auditReplacer` / `hashEntry` | the byte format, public so writers and graders can never drift on it |
63
+ | `chainStem` / `resolveChainDir` | how the guards find the shared file |
64
+ | `LOCK_TIMEOUT_MS` / `LOCK_BACKOFF_MS` | the lock's tuning constants |
65
+
66
+ ## Migration notes for the three plugins
67
+
68
+ - **entropy-guard**: `AuditLog` becomes a subclass adding its own readers
69
+ (`replayState`, `gearHistogram`, `gateAcceptanceRate`). One wording change to
70
+ expect: the interleaved reason says "two generations wrote this chain" here
71
+ (this package does not presume plugins).
72
+ - **shape-guard**: `ShapeLedger` becomes an alias of `ChainLog`; `lib/ledger.js`
73
+ turns into a re-export adapter so importers never move.
74
+ - **threat-gate**: the vendored copy is deleted; `lib/VENDORED.md` retires in
75
+ favour of a real dependency.
76
+
77
+ ## Tests
78
+
79
+ ```
80
+ node --test test/core.test.mjs # 32 tests, no dependencies
81
+ ```
82
+
83
+ The suite is the union of the anchors that pinned both historical
84
+ implementations (entropy-guard defects 14/15/16/19/20, shape-guard I7–I13 /
85
+ S1–S4), plus the byte-compatibility pin.
86
+
87
+ ## Provenance
88
+
89
+ Written against the published implementations (`@cyd-prc/dsh-entropy-guard`
90
+ 0.3.3, `@cyd-prc/dsh-shape-guard` 0.1.6) and the frozen real-world fixtures the
91
+ contract names. Cross-verified both ways: this package reads the fixtures with
92
+ the contract-conformant readings, and chains it writes grade `verified` under
93
+ both historical implementations. MIT © Wang Miaosheng (ORCID: 0009-0003-2767-2421).
@@ -0,0 +1,81 @@
1
+ # dsh-audit-chain —— 一条链,一份实现
2
+
3
+ 供 dsh 守卫插件(`dsh-entropy-guard`、`dsh-shape-guard`、`dsh-threat-gate`)共用的
4
+ 防篡改 JSONL 审计链。它存在的原因:这条链过去活在**两份副本**里(entropy-guard 的
5
+ `AuditLog` 与 shape-guard 外抄出去的 `lib/ledger.js`),而副本会漂移——一个分级缺陷
6
+ (S3)曾搭车进两个已发布包;一份副本有的锁(`wx` + 新鲜链尾),要付出一次实测分叉事故
7
+ 才传到另一份。
8
+
9
+ 这个包终结漂移:**一个写入者、一个分级器、一份契约。**
10
+
11
+ ## 是什么
12
+
13
+ ```js
14
+ import { ChainLog, verifyChain } from '@cyd-prc/dsh-audit-chain';
15
+
16
+ const chain = new ChainLog('/path/to/session.jsonl'); // 或 null(内存账本)
17
+ chain.record('gate_decision', { admitted: true }); // 带锁、新鲜链尾
18
+ chain.seal('acknowledged'); // 人类确认
19
+ const reading = chain.verify(); // 按契约分级
20
+ ```
21
+
22
+ - **写入者**——每次追加取独占锁(`<chain>.lock`:`wx` 原子创建、超时破旧锁、
23
+ 持锁过久则 fail-closed),在**锁内**重读磁盘链尾,并先终止崩溃撕断的破行再写入。
24
+ 两个插件共享一条链,不可能再写出分叉。
25
+ - **分级器**——`verify()` 实现共享链分级契约八条:内容哈希恒重算;分叉感知链接
26
+ (悬空 = 改写,可解析 = 分支);分叉计数永不提前停;封存界定顺序类发现(链化轴按
27
+ `seq`、无链化轴按文件位置);篡改永不封存;不可解析行恒上报但不单独定级。
28
+ - **字节兼容**两代历史写入者:`entropy-guard ≤ 0.3.3` 或 `shape-guard ≤ 0.1.6` 写的链
29
+ 照常通过校验,本包写的链在两边的旧实现下也照常通过。哈希载荷的等价性由测试钉住,
30
+ 不靠散文。
31
+
32
+ ## 契约
33
+
34
+ [`docs/CHAIN-GRADING-CONTRACT.md`](docs/CHAIN-GRADING-CONTRACT.md) 随包带一份逐字节镜像
35
+ (md5 `500f62b603140477317ed03bec167553`,v4)。权威件归测试侧;两份副本若哪天不一致,
36
+ 指纹就是发现的方式,且以测试侧的读数为准。
37
+
38
+ ## 工具
39
+
40
+ ```
41
+ node tools/verify-chain.mjs <chain.jsonl> [--json] # 构造上只读;exit 0 = verified
42
+ node tools/verify-release.mjs --pack --run-tests # 发布前自证
43
+ node tools/verify-release.mjs --spec @cyd-prc/dsh-audit-chain@<v> --run-tests --wait 120
44
+ ```
45
+
46
+ `verify-chain` 刻意没有报告模式:当年的报告路径正是让旧工具往它审的链里写字的原因
47
+ (entropy-guard 缺陷 16)。这个工具只读,读完就走。
48
+
49
+ ## API 面
50
+
51
+ | 导出 | 是什么 |
52
+ |---|---|
53
+ | `ChainLog` | 链本体:`record`、`seal`、`verify`、`entries`、`corruptLines`、`failures`/`lastError` |
54
+ | `verifyChain(path)` | `new ChainLog(path).verify()` 的函数孪生,给纯读者 |
55
+ | `encodeEntry` / `auditReplacer` / `hashEntry` | 字节格式本身公开,写入者与分级器再无漂移空间 |
56
+ | `chainStem` / `resolveChainDir` | 守卫们定位共享文件的方式 |
57
+ | `LOCK_TIMEOUT_MS` / `LOCK_BACKOFF_MS` | 锁的调参常量 |
58
+
59
+ ## 三个插件的迁移注记
60
+
61
+ - **entropy-guard**:`AuditLog` 变为子类,只加自己的读数(`replayState`、
62
+ `gearHistogram`、`gateAcceptanceRate`)。一处措辞预期内变化:交错原因的文案在这里是
63
+ 「two generations wrote this chain」(本包不预设插件)。
64
+ - **shape-guard**:`ShapeLedger` 成为 `ChainLog` 的别名;`lib/ledger.js` 变成
65
+ re-export 适配层,引用方一行不动。
66
+ - **threat-gate**:删除 vendored 副本;`lib/VENDORED.md` 退役,换成真依赖。
67
+
68
+ ## 测试
69
+
70
+ ```
71
+ node --test test/core.test.mjs # 32 项,无依赖
72
+ ```
73
+
74
+ 套件是两份历史实现的锚的并集(entropy-guard 缺陷 14/15/16/19/20、shape-guard
75
+ I7–I13 / S1–S4),外加字节兼容针。
76
+
77
+ ## 溯源
78
+
79
+ 对照已发布实现写作(`@cyd-prc/dsh-entropy-guard` 0.3.3、`@cyd-prc/dsh-shape-guard`
80
+ 0.1.6),并用契约点名的冻结真实 fixture 双向交叉验证:本包以契约合规读数读 fixture,
81
+ 本包写的链在两份历史实现下都判 `verified`。MIT © Wang Miaosheng(ORCID: 0009-0003-2767-2421)。
@@ -0,0 +1,97 @@
1
+ # Chain grading contract — shared by entropy-guard and shape-guard
2
+
3
+ Why this file exists: the two guards write **one** chain, so they must read it the
4
+ same way. An external review built a fixture of acknowledged forks and found the two
5
+ verifiers disagreeing — and, more usefully, disagreeing on *different axes*:
6
+
7
+ | axis | entropy-guard 0.3.1 | shape-guard 0.1.4 |
8
+ |---|---|---|
9
+ | **fork** (a duplicate `seq`: two writers branched from one tail) | ✗ stops at the first fork — counted **1 of 8**, and then graded a chain with **2 live forks** as `verified` | ✓ counts all 8, reports `liveForks`, and the seal clears them (`2 → 0`) |
10
+ | **interleaved** (unchained entries among chained ones — what an in-place upgrade looks like) | ✓ the seal clears `liveInterleaved` | ✗ grades `discontinuity` from the **lifetime** count (7) while reporting `liveDiscontinuities: 0` |
11
+ | **rewrite** (a value changed after it was written) | ✓ `tampered`, never sealed | ✓ `tampered`, never sealed |
12
+ | **writes to the chain it audits** | ✗ appends `init`+`restore` on every run (+2/run, measured) | ✓ read-only |
13
+
14
+ ## The contract
15
+
16
+ 1. **Both axes carry a lifetime count and a live count.** `forks`/`liveForks` and
17
+ `interleaved`/`liveInterleaved` (or `discontinuities`/`liveDiscontinuities`).
18
+ Two names for one axis is how this disagreement started: pick one vocabulary and
19
+ use it in every reading, the CLI line, and the report.
20
+ 2. **The grade comes from live findings only**, ordered
21
+ `tampered` > `forked` > `discontinuity` > `verified`.
22
+ 3. **A `chain_seal` bounds both axes.** Findings before the last seal are
23
+ acknowledged history: reported, never dropped, never graded.
24
+ 4. **A tamper is never sealed** — on either side of the seal, by either guard.
25
+ 5. **The `reason` string names the live finding**, never a lifetime count.
26
+ 6. **Hash checks always run.** Only the *link/ordering* expectation may be relaxed
27
+ after a fork, and then only per branch: a fork-aware check restarts the expected
28
+ `prev` at each branch instead of abandoning the remainder. (Relaxing the rest is
29
+ what let a **deleted entry** pass as `verified` on the entropy-guard side.)
30
+ 7. **A verifier never writes to the chain it audits.** The instrument must not be a
31
+ participant.
32
+ 8. **An unparsable line is reported, never graded by itself.** A crash mid-append leaves
33
+ a torn tail: that is an availability accident, not an attack. The hostile variant it
34
+ could represent — an entry scrubbed out of the middle — is *already* `tampered` under
35
+ clause 6, because the following entry's `prev` resolves to nothing. So `corrupt` counts
36
+ travel in every reading and the `reason` names them, but they never raise the grade
37
+ above `discontinuity` on their own.
38
+ A **writer** owes the other half of this: terminate a torn tail before appending.
39
+ Without that the next entry fuses into the half-line, the line count does not move, and
40
+ the write is silently lost (defect 19, measured both ways: the old writer left
41
+ `2 lines → 2`, the new one `2 → 3` with the torn line preserved and counted).
42
+
43
+ Clauses 6–8 are the defects this review found on the **entropy-guard** side
44
+ (M1: the verifier appended `init`+`restore` to the chain it audits, +2 entries per run;
45
+ M2: it stopped at the first fork, which undercounted 8 forks as 1 and let a **deleted
46
+ entry** pass as `verified`; defect 19: a writer fusing into a torn tail). They are
47
+ **closed at `0.3.2@82765b3`**, measured — see the table. Clauses 1–5 are the
48
+ **shape-guard** side, and clause 8 gives shape-guard one open item (**S4**).
49
+
50
+ ## Conformance — measured, not asserted
51
+
52
+ This table records only what a **named source** measured. *Reported* means the builder
53
+ side measured it and the test side has not re-run it yet: a closed row is not a
54
+ signature, and this file changes when a re-run changes it.
55
+
56
+ | package | clauses 1–5 (grading) | clauses 6–8 (instrument) | source |
57
+ |---|---|---|---|
58
+ | entropy-guard 0.3.1 | ✗ fork axis: 8 forks counted as 1, a live-forked chain graded `verified` | ✗ M1, M2 open | test side, measured |
59
+ | shape-guard 0.1.4 | ✗ S3: the interleaved axis is graded from the lifetime count | ✓ | test side, measured |
60
+ | shape-guard 0.1.5 | ✓ S3 **closed** (control `forked` with `liveInterleaved: 0`, one seal ⇒ `verified` with `reason: ""`, pre-seal rewrite still `tampered`) · ✗ S4 open: a torn tail is graded `tampered` from the `corrupt` count alone | ✓ | **test side, measured** (the builder's I10/I11 agree) |
61
+ | threat-gate 0.1.1 | ✗ V1: the vendored 0.1.4 ledger carried S3 into a second published package | ✓ | test side, measured |
62
+ | threat-gate 0.1.3 | ✓ V1 **closed** on the same sealed fixture; T1 **closed** (`vetoOnCritical` leaves read-only tools a way out); 13/13 | ✓ | **test side, measured** |
63
+ | entropy-guard 0.3.2 @`82765b3` | ✓ **closed** — `forked · 8 forks (live 2)`; sealed ⇒ `verified`; **delete-after-fork ⇒ `tampered`** (the case 0.3.1 missed); clause 8 conformant (`corrupt` reported, not graded) | ✓ **closed** — read-only (two runs, hash unchanged); a foreign `--spec` refused with exit 1; the lock A/B: new writer **0** duplicate `seq` where the old one produced **30**; torn-tail fix measured both ways | **test side, measured** |
64
+ | entropy-guard 0.3.3 @`e5a37bd` | ✓ unchanged from 0.3.2, plus clause 8's *letter*: the `reason` names the corrupt count (`1 unparsable line(s) (reported, not graded)`) | ✓ **closed** — defect 20: a CRLF checkout can no longer fake a DIFF (**17 phantom DIFFs → 0**, measured both ways), the tree HEAD is named in the output, and the HEAD anchor holds inside the artifact (in-package suite exit 0 on a non-git tree) | **test side, measured** |
65
+ | shape-guard 0.1.6 *(patch-reconstructed)* | ✓ **S4 closed** — the S4 pin (`s4-corrupt-not-hostile.finding.test.mjs`) flips green; S3's contract test and S1's record stay green, and the 20-case judgement suite holds, so the grading fix did not regress the earlier rulings | ✓ | **test side, measured** |
66
+ | threat-gate 0.1.4 *(patch-reconstructed)* | ✓ V1 closed at the source: `lib/ledger.js` is **byte-identical to shape-guard 0.1.6's** (`3A5476636C4B593E`), and the sealed fixture now grades `verified` | ✓ | **test side, measured** |
67
+
68
+
69
+
70
+ Standing rule, learned twice in this review: a status table that is not re-measured rots.
71
+ The two closed rows above moved from *reported* to *measured* only because the test side
72
+ re-ran its own tests against the published tarballs — not because the builder said so, and
73
+ not because the labels changed. `entropy-guard 0.3.1` is now the **only** verifier in the
74
+ fleet that reads the frozen fixture wrong (`VERIFIED · 1 fork` against the true
75
+ `FORKED · 8 forks, 2 live`), which is what its 0.3.2 owes clauses 6–7.
76
+
77
+
78
+ ## Fixtures and tests that pin it
79
+
80
+ ```
81
+ fixtures/live-mixed-generation.jsonl 727 entries: 194 pre-chaining, 8 duplicate
82
+ seq (2 after the last seal), 7 interleaved,
83
+ 3 seals. Safe to ship — the ledger records
84
+ span digests and offsets, never payloads.
85
+ fixtures/contract-sealed-fork.jsonl the same chain plus one acknowledgement seal:
86
+ the input that separates "fork" from
87
+ "interleaved" as the top live finding.
88
+ chain-grading.contract.test.mjs control / claim / adversarial, in that order,
89
+ so the claim cannot pass vacuously.
90
+ chain-semantics.finding.test.mjs S1's red→green record: fails on 0.1.3 with
91
+ its raw reading, passes on 0.1.4.
92
+ ```
93
+
94
+ The fixtures are the shared input; the two test files are the recomputable definition of
95
+ what a grade means. They live beside the fixtures rather than inside any package, which is
96
+ why they are the test side's to own.
97
+
package/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * dsh-audit-chain — the one tamper-evident JSONL chain every guard shares.
3
+ * The public surface is `lib/chain.js`; see the README for the contract.
4
+ *
5
+ * @module dsh-audit-chain
6
+ */
7
+
8
+ export {
9
+ ChainLog,
10
+ verifyChain,
11
+ encodeEntry,
12
+ auditReplacer,
13
+ hashEntry,
14
+ chainStem,
15
+ resolveChainDir,
16
+ LOCK_TIMEOUT_MS,
17
+ LOCK_BACKOFF_MS,
18
+ } from './lib/chain.js';