@tacet-ink/journal-core 0.3.2 → 0.3.3
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
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
```sh
|
|
19
19
|
git clone https://github.com/tacet-ink/journal-core.git && cd journal-core
|
|
20
20
|
npm ci --ignore-scripts
|
|
21
|
-
npm run verify #
|
|
21
|
+
npm run verify # 522 assertions, all green, run against the real modules (no mocks); 0.3.3: 519 inside the published package ([21] SKIP line)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
```ts
|
|
@@ -124,7 +124,7 @@ jr3d/jr4d 兩入參數同輸入,域分離由 info 承載;凍結 KAT 兩 blob
|
|
|
124
124
|
|
|
125
125
|
```sh
|
|
126
126
|
npm ci --ignore-scripts
|
|
127
|
-
npm run verify #
|
|
127
|
+
npm run verify # 522 斷言對真模組(禁鏡像;限流單句 UPSERT…RETURNING 直載真 SQLite):roundtrip/AAD 防搬移/
|
|
128
128
|
# extractable/時代隔離/跨前綴家族隔離/payload 竄改/RFC 9106 KAT/BIP39 @scure 對照 200 組/
|
|
129
129
|
# server-side(inboundCipher/validWrappedKey/pickKeyPackage/checkRate/timingSafeEq)+ pinlock 全函式
|
|
130
130
|
# + 密語正規化 v3 世代(normalizePassphrase/jr4w./jr4d./PH1 v3 鹽域分離+帶內版本化舊契約向量)
|
|
@@ -136,7 +136,7 @@ npm run verify # 521 斷言對真模組(禁鏡像;限流單句 UPSERT…RE
|
|
|
136
136
|
# +KAT 凍結向量 [17](HKDF info 域世代分離+v3/raw 入口契約四 blob)
|
|
137
137
|
# +包裹前綴快檢 [13-2](錯前綴試探免付 KDF:五入口行為+計時帽+計數錨)
|
|
138
138
|
# +CI 供應鏈面 [21](npm ci --ignore-scripts+dependabot runner fromJSON 分流+腳本摘除+NIT-1 錨+r2 MINOR-1 URL 直傳;
|
|
139
|
-
#
|
|
139
|
+
# 522 帳=repo 樹——發行包內無 .github=[21] 四條 workflow 錨收斂為 1 顯性 SKIP 行→519(=522−3;消費端帳數真相源=本行,隨版記帳——閘 SKIP 行摘死數字指回本行,0.3.3 實測),SKIP 顯形非靜默)
|
|
140
140
|
# +發行 tarball 治理面 [22](pack 白名單零 open-card-pr.sh)
|
|
141
141
|
# +格式規格+測試向量 [26](docs/format-spec.md+docs/vectors 存在性/位元組 shape/欄位名對規格/凍結 KAT 逐字對閘)
|
|
142
142
|
# +高階 Vault API [27](roundtrip 三形/新舊前綴解鎖/upgrade/換密語/復原/VaultError 毒化/載體覆寫+Worker 回退鏈/vendor 孿生)
|
|
@@ -331,7 +331,7 @@ prefix family (`families.json`), known-answer vectors (RFC 9106 Argon2id, the ga
|
|
|
331
331
|
`scripts/generate-vectors.ts` against the real primitives and committed as frozen evidence; the
|
|
332
332
|
verify gate checks their presence and shape (section [26]) and does not regenerate them.
|
|
333
333
|
|
|
334
|
-
**Verification.** `npm run verify` runs
|
|
334
|
+
**Verification.** `npm run verify` runs 522 assertions against the real modules (no mocks; 0.3.3: the in-package count is 519 because the [21] workflow anchors skip as one line):
|
|
335
335
|
roundtrips, AAD tamper-evidence, extractability rules, era isolation, cross-prefix family
|
|
336
336
|
isolation, payload tampering, RFC 9106 KAT, a 200-vector BIP39 cross-check, and the
|
|
337
337
|
login ladder-guard behavior suite (external audit #8), and presence/shape checks for the format
|
|
@@ -41,6 +41,7 @@ export declare function isRateAllowed(count: number, max: number): boolean;
|
|
|
41
41
|
* - IPv4-mapped(::/96 前綴+映射尾組,RFC 4291 §2.5.5.2)→ 點分 IPv4 原鍵——mapped 位址就是 IPv4
|
|
42
42
|
* 節點的表示法,回 /32 與原生同鍵(hex 尾組 twin `::ffff:c000:201` 同鍵 192.0.2.1;禁併入 ::/64 全域桶)。
|
|
43
43
|
* - IPv4/`'unknown'`/空串/無法解析形:原樣返回(鍵照樣進表=可見面,不靜默丟鍵)。
|
|
44
|
+
* - embedded-IPv4 三家族刻意聚合進其所屬前綴的 /64(deliberate nondecision):IPv4-compatible(RFC 4291 §2.5.5.1 已棄)::192.0.2.1 → ::/64、SIIT ::ffff:0:192.0.2.1 → ::/64、NAT64 64:ff9b::192.0.2.1 → 64:ff9b::/64——僅 IPv4-mapped(§2.5.5.2)還原點分 IPv4。
|
|
44
45
|
*/
|
|
45
46
|
export declare function normalizeRateIp(ip: string): string;
|
|
46
47
|
export declare function checkRate(env: Env, w: RateWindow, ip: string): Promise<boolean>;
|
package/dist/server/ratelimit.js
CHANGED
|
@@ -96,6 +96,7 @@ function parseIpv6(s) {
|
|
|
96
96
|
* - IPv4-mapped(::/96 前綴+映射尾組,RFC 4291 §2.5.5.2)→ 點分 IPv4 原鍵——mapped 位址就是 IPv4
|
|
97
97
|
* 節點的表示法,回 /32 與原生同鍵(hex 尾組 twin `::ffff:c000:201` 同鍵 192.0.2.1;禁併入 ::/64 全域桶)。
|
|
98
98
|
* - IPv4/`'unknown'`/空串/無法解析形:原樣返回(鍵照樣進表=可見面,不靜默丟鍵)。
|
|
99
|
+
* - embedded-IPv4 三家族刻意聚合進其所屬前綴的 /64(deliberate nondecision):IPv4-compatible(RFC 4291 §2.5.5.1 已棄)::192.0.2.1 → ::/64、SIIT ::ffff:0:192.0.2.1 → ::/64、NAT64 64:ff9b::192.0.2.1 → 64:ff9b::/64——僅 IPv4-mapped(§2.5.5.2)還原點分 IPv4。
|
|
99
100
|
*/
|
|
100
101
|
export function normalizeRateIp(ip) {
|
|
101
102
|
if (typeof ip !== 'string' || !ip.includes(':'))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacet-ink/journal-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Zero-knowledge journal core — e2e crypto, passphrase-only auth, transport, ratelimit. The server never sees plaintext.",
|
|
5
5
|
"homepage": "https://tacet.ink",
|
|
6
6
|
"repository": {
|
|
@@ -157,7 +157,7 @@ async function A(name: string, cond: boolean | Promise<boolean>, detail = ''): P
|
|
|
157
157
|
// 帳面契約:--only 態總帳顯形「本輪 X/選段帳 N(N 案非本輪載)」+OK 標記帶 (--ONLY …) 尾碼;skip 段帳面恆列「未跑非通過」;[24]/[25] runner 自證段隨每輪在場。
|
|
158
158
|
// 段標記 helper(secOpen):全跑態輸出位元組恆等原 console.log('\n[N] …');--only 態僅選中段輸出段頭。
|
|
159
159
|
// 毒化矩陣內層 rerun(execFileSync+POISON_GATE_INNER=1)不傳遞 --only=內層恆全帳(POISON_GATE_INNER 語意不變)。
|
|
160
|
-
// 段帳=執行帳(healthy-repo 全跑 runtime 真值,2026-10-
|
|
160
|
+
// 段帳=執行帳(healthy-repo 全跑 runtime 真值,2026-10-12 實測 522——[12] 限流鍵 /64 正規化 +14+IPv4-mapped 面 +1+NIT-b 接線錨 +1(NIT-a nonstring 五型 Object.is=既有案就地強化 +0));帳面語意契約(逐段「案 X/帳」
|
|
161
161
|
// 對 secRan 執行值顯形):執行 < 帳 的段=本環境條件態未行使(非 skip 態),--only 期望算術對齊本帳。
|
|
162
162
|
// [23] 執行 18 帳 21=[23]④ lookup 未配置(0.2.3 單查恆等)/[23]⑤ fail-open(表缺席)兩情境
|
|
163
163
|
// 在本機樹由 [8](登入憑證 Argon2id 派生)內登入 lane 的訊號面真行使(try/catch 異常面)——車道分流非帳面虛報。
|
|
@@ -166,7 +166,7 @@ async function A(name: string, cond: boolean | Promise<boolean>, detail = ''): P
|
|
|
166
166
|
// for (const [k] of secRan) if (!secCases[k] && k !== 22) FAIL(負向斷言本體在 [25] 段
|
|
167
167
|
//——secCases 全摘即 RED);反向缺席段=帳面誠實「案 0/N · skip」。
|
|
168
168
|
// secCases 載體三群:[24]/[25] self-account+--only 段存在面+teeth(段帳由 secRan 執行自記)。
|
|
169
|
-
const secCases: Record<number, number> = { 1: 3, 2: 7, 3: 9, 4: 4, 5: 3, 6: 20, 7: 27, 8: 8, 9: 22, 10: 5, 11: 41, 12:
|
|
169
|
+
const secCases: Record<number, number> = { 1: 3, 2: 7, 3: 9, 4: 4, 5: 3, 6: 20, 7: 27, 8: 8, 9: 22, 10: 5, 11: 41, 12: 66, 13: 39, 14: 18, 15: 59, 16: 15, 17: 14, 18: 39, 19: 17, 20: 47, 21: 10, 23: 18, 24: 5, 25: 4, 26: 8, 27: 14 };
|
|
170
170
|
function parseOnly(spec: string): { err: string | null; set: number[] } {
|
|
171
171
|
const set = new Set<number>();
|
|
172
172
|
const specStr = spec.trim();
|
|
@@ -927,17 +927,24 @@ const plainCases12: Array<[string, (m: RateMod12) => Promise<boolean>]> = [
|
|
|
927
927
|
return m.normalizeRateIp('unknown') === 'unknown' && m.normalizeRateIp('') === ''
|
|
928
928
|
&& r.row('unknown')?.count === 1 && r.row('')?.count === 1;
|
|
929
929
|
}],
|
|
930
|
-
['normalizeRateIp 無法解析形原樣且零 throw(IPv4 五段/IPv6 單段 2001/缺組/雙 ::/:::/非 hex/9 組/:: 外 8 組/嵌入 IPv4
|
|
930
|
+
['normalizeRateIp 無法解析形原樣且零 throw(IPv4 五段/IPv6 單段 2001/缺組/雙 ::/:::/非 hex/9 組/:: 外 8 組/嵌入 IPv4 越界/方括號/前導空白;nonstring 五型 undefined/null/NaN/object+bad 字串面皆 Object.is 原樣回傳)', async (m) => {
|
|
931
931
|
const bad = ['1.2.3.4.5', '2001', '2001:db8', '1::2::3', ':::', '2001:db8::g', '1:2:3:4:5:6:7:8:9', '1:2:3:4:5:6:7:8::', '::ffff:999.0.0.1', '[2001:db8::1]', ' 2001:db8::1'];
|
|
932
|
+
const obj = { ip: '2001:db8::1' };
|
|
932
933
|
try {
|
|
933
|
-
return bad.every((ip) => m.normalizeRateIp(ip)
|
|
934
|
-
&& (m.normalizeRateIp(
|
|
934
|
+
return bad.every((ip) => Object.is(m.normalizeRateIp(ip), ip))
|
|
935
|
+
&& [undefined, null, NaN, obj].every((v) => Object.is(m.normalizeRateIp(v as unknown as string), v));
|
|
935
936
|
} catch { return false; }
|
|
936
937
|
}],
|
|
937
938
|
];
|
|
938
939
|
for (const [name, pred] of [...v6Cases12, ...plainCases12]) await A(name, await pred(rateReal12));
|
|
939
940
|
await A('index barrel 匯出 normalizeRateIp(=ratelimit 子路徑同一函式——單一真相)',
|
|
940
941
|
(await import('../src/index.ts')).normalizeRateIp === normalizeRateIp);
|
|
942
|
+
{
|
|
943
|
+
const src = await srcOf('../src/server/ratelimit.ts');
|
|
944
|
+
const needle = 'bumpRate(env, w, normalizeRateIp(ip))';
|
|
945
|
+
const n = src.split(needle).length - 1;
|
|
946
|
+
await A('checkRate 入口接線(單呼叫點):bumpRate 收 normalizeRateIp 產物(wiring 靜態錨恰 1——摘 normalize 直通形翻紅面)', n === 1, 'n=' + n);
|
|
947
|
+
}
|
|
941
948
|
// 毒化(拷貝樹 in-gate,[23] 母型):IPv6 聚合分支入口 `if (!g) return ip;` → `return ip;`(IPv6 一律原樣成鍵)
|
|
942
949
|
// → v6 面謂詞逐案真翻紅(false 非 crash)、非 v6 面照綠(毒面恰此面翻)。毒化跑在 /tmp 拷貝=本 repo 樹零接觸、樹出生即棄。
|
|
943
950
|
{
|
|
@@ -2654,7 +2661,10 @@ secOpen(21, '[21] CI 供應鏈面(npm ci --ignore-scripts+dependabot runner
|
|
|
2654
2661
|
await A('[21] publish.yml:npm ci --ignore-scripts(tag 驅動無 actor 面;typecheck+verify 先行=腳本面零在場)',
|
|
2655
2662
|
srcPy21.includes('npm ci --ignore-scripts'));
|
|
2656
2663
|
} else {
|
|
2657
|
-
|
|
2664
|
+
// F-1(t_0eb70367):僵字面帳數摘除——舊形寫死『消費端計數帳 503』每版必過期;執行期插值
|
|
2665
|
+
// 不可行(tarball 全帳含 secCases 體系外段,secCases 求和≠帳總——插值即印錯數,比僵字面更糟)。
|
|
2666
|
+
// 對帳值真相源=README「CI 供應鏈面」行(發布輪隨版維護);本行語意只聲明 SKIP 顯形與帳差 −3。
|
|
2667
|
+
await A('[21] CI 源碼面:.github 缺席環境(tarball 發行樹常態)=顯性 SKIP(workflow 斷言 4 收斂為 1 SKIP 行,帳差 −3;消費端計數帳對照 README「CI 供應鏈面」行隨版記帳)',
|
|
2658
2668
|
true, 'no .github dir — consumption tree face');
|
|
2659
2669
|
}
|
|
2660
2670
|
// 摘除自證(t_760f44e8 治理掃蕩補漏;fs 原語缺席環境=顯性 FAIL 形自守衛——非 silent true)
|
package/src/server/ratelimit.ts
CHANGED
|
@@ -106,6 +106,7 @@ function parseIpv6(s: string): number[] | null {
|
|
|
106
106
|
* - IPv4-mapped(::/96 前綴+映射尾組,RFC 4291 §2.5.5.2)→ 點分 IPv4 原鍵——mapped 位址就是 IPv4
|
|
107
107
|
* 節點的表示法,回 /32 與原生同鍵(hex 尾組 twin `::ffff:c000:201` 同鍵 192.0.2.1;禁併入 ::/64 全域桶)。
|
|
108
108
|
* - IPv4/`'unknown'`/空串/無法解析形:原樣返回(鍵照樣進表=可見面,不靜默丟鍵)。
|
|
109
|
+
* - embedded-IPv4 三家族刻意聚合進其所屬前綴的 /64(deliberate nondecision):IPv4-compatible(RFC 4291 §2.5.5.1 已棄)::192.0.2.1 → ::/64、SIIT ::ffff:0:192.0.2.1 → ::/64、NAT64 64:ff9b::192.0.2.1 → 64:ff9b::/64——僅 IPv4-mapped(§2.5.5.2)還原點分 IPv4。
|
|
109
110
|
*/
|
|
110
111
|
export function normalizeRateIp(ip: string): string {
|
|
111
112
|
if (typeof ip !== 'string' || !ip.includes(':')) return ip;
|