@kya-os/checkpoint-wasm-runtime 1.5.0 → 1.5.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,93 @@
1
1
  # @kya-os/checkpoint-wasm-runtime
2
2
 
3
+ ## 1.5.1 — 2026-05-20
4
+
5
+ **Tier 1 verification is now observable from the wire.** Closes the
6
+ diagnostic gap Bench-Detection-Delta V3 (2026-05-20) surfaced: although
7
+ Tier 1 RFC 9421 cryptographic verification was engaging correctly in
8
+ 1.5.0, the response headers only exposed `Decision`-level info — operators
9
+ couldn't tell whether the engine had verified the request OR what tier
10
+ fired. Required a bench-side `onResult` callback to see
11
+ `detectionDetail.metadata.verified_*`. This patch surfaces the same
12
+ information on the wire.
13
+
14
+ Patch bump (not minor) because both changes are additive observability
15
+ improvements — no public API surface change, no behavior change for any
16
+ existing consumer:
17
+
18
+ | Change | Type | Consumer impact |
19
+ | ------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `X-Checkpoint-Verified-Tier` / `-Vendor` / `-Protocol` response headers | Additive | New headers only emitted when `detectionDetail.metadata.verified_*` populated. No existing consumer parsed these (they didn't exist). |
21
+ | `ruleset_hash` `t1:` slot now populates with vendor-keys SHA-256 (`t1:<64-hex>` instead of `t1:unset`) | Additive | The structured format documented `unset` / `disabled` / `<64-hex>` as valid slot values per [[Engine-Tier-Ruleset-Hash-1]]. Operators pinning on the literal `t1:unset` string would see breakage — but doing so was always against the documented contract (the slot was documented as a placeholder pending wiring). |
22
+
23
+ ### Added
24
+
25
+ - **`X-Checkpoint-Verified-Tier` response header.** Emitted when
26
+ `detectionDetail.metadata.verified_tier` is a finite number or non-empty
27
+ string. Value is the stringified tier (`"1"` for Tier 1 RFC 9421 today;
28
+ `"2"` reserved for Tier 2 IP+UA cross-match attribution when that path
29
+ populates `verified_*`).
30
+ - **`X-Checkpoint-Verified-Vendor` response header.** Emitted when
31
+ `detectionDetail.metadata.verified_vendor` is a non-empty string.
32
+ Today: `openai` (the only Tier 1 vendor with a bundled JWK). Future
33
+ Anthropic / Google RFC 9421 signers will add `anthropic` / `google`
34
+ values without a SemVer event.
35
+ - **`X-Checkpoint-Verified-Protocol` response header.** Emitted when
36
+ `detectionDetail.metadata.verified_protocol` is a non-empty string.
37
+ Today: `rfc9421` (HTTP Message Signatures). Future MCP-I JWS envelope
38
+ verifications will emit `mcp-i` / `did-jws`.
39
+ - **`X-Checkpoint-Ruleset-Hash`'s `t1:` slot now populates.** Computed
40
+ at engine build time as SHA-256 of the canonical concat of every
41
+ `data/vendor-keys/*.json` file (sorted by filename, `\n--\n`
42
+ separator). Build-time panic gate on empty dir / empty file prevents
43
+ shipping a misleading empty-input hash (mirrors Tier 2/3 discipline).
44
+ The `cargo:rustc-env=KYA_OS_ENGINE_TIER1_KEYS_SHA256` producer in
45
+ `kya-os-engine/build.rs` is the single source of truth; `api.rs`
46
+ consumes via `env!()`.
47
+
48
+ ### Defensive guards (PR #2718 reviewer feedback)
49
+
50
+ - `X-Checkpoint-Verified-Tier` number path uses `Number.isFinite` to
51
+ reject `NaN` / `±Infinity` (`typeof NaN === 'number'` is true in JS,
52
+ so a plain typeof check would emit `Verified-Tier: NaN`).
53
+ - `X-Checkpoint-Verified-Tier` string path requires `length > 0`
54
+ (mirrors `Verified-Vendor` / `Verified-Protocol` defensive guards).
55
+
56
+ ### Tests
57
+
58
+ - New Rust drift gate `ruleset_hash_tier1_slot_resolves_to_vendor_keys_sha`
59
+ re-reads `data/vendor-keys/*.json` at test time + asserts the `t1:`
60
+ slot matches the computed SHA. Mirrors the existing Tier 2 YAML drift
61
+ gate. 96/96 lib tests pass.
62
+ - 6 new vitest cases on `render-decision.test.ts` cover the
63
+ `Verified-*` header emission (presence, absence, partial metadata,
64
+ Block-path co-emission, observe-mode co-emission, empty-string +
65
+ NaN + ±Infinity defensive guards, number-vs-string acceptance).
66
+ 26/26 render-decision tests pass.
67
+
68
+ ### What this DOESN'T change
69
+
70
+ - Engine verify-dispatch behavior — unchanged. Tier 1 was already
71
+ engaging in 1.5.0; this release only makes that visible.
72
+ - Public API surface — unchanged. No new exports, no new types, no
73
+ removed exports.
74
+ - Existing response headers — unchanged. `X-Checkpoint-Decision`,
75
+ `-Reason`, `-Engine`, `-Engine-Version` emit exactly as before.
76
+ - Ruleset-hash format — unchanged. The structured-prefix format
77
+ pinned by `ruleset_hash_matches_structured_per_tier_format` still
78
+ matches `sha256:t1:<slot>:t2:<slot>:t3:<slot>:t4:<slot>` where
79
+ each slot is one of `unset`/`disabled`/`[0-9a-f]{64}`. Only the
80
+ `t1:` slot's runtime value changed from `unset` to `<64-hex>`.
81
+
82
+ ### Pin guidance
83
+
84
+ - Customers pinning `^1.5.0` (caret) → auto-pick up 1.5.1 on next
85
+ install. No action needed.
86
+ - Customers pinning `~1.5.0` (tilde) → auto-pick up 1.5.1 on next
87
+ install. No action needed.
88
+ - Customers pinning `1.5.0` (exact) → no auto-pickup. Bump to
89
+ `^1.5.1` to see Tier 1 attribution on the wire.
90
+
3
91
  ## 1.5.0 — 2026-05-19
4
92
 
5
93
  **Formal announcement of HTTP-Sig-Verifier-1 — Tier 1 RFC 9421 HTTP
@@ -404,6 +404,23 @@ function buildBaseHeaders(result) {
404
404
  if (result.engineInfo.rulesetHash) {
405
405
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
406
406
  }
407
+ const meta = result.detectionDetail.metadata;
408
+ if (meta) {
409
+ const verifiedTier = meta.verified_tier;
410
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
411
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
412
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
413
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
414
+ }
415
+ const verifiedVendor = meta.verified_vendor;
416
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
417
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
418
+ }
419
+ const verifiedProtocol = meta.verified_protocol;
420
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
421
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
422
+ }
423
+ }
407
424
  return headers;
408
425
  }
409
426
  function httpStatusForBlockReason(reason) {
@@ -402,6 +402,23 @@ function buildBaseHeaders(result) {
402
402
  if (result.engineInfo.rulesetHash) {
403
403
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
404
404
  }
405
+ const meta = result.detectionDetail.metadata;
406
+ if (meta) {
407
+ const verifiedTier = meta.verified_tier;
408
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
409
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
410
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
411
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
412
+ }
413
+ const verifiedVendor = meta.verified_vendor;
414
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
415
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
416
+ }
417
+ const verifiedProtocol = meta.verified_protocol;
418
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
419
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
420
+ }
421
+ }
405
422
  return headers;
406
423
  }
407
424
  function httpStatusForBlockReason(reason) {
@@ -508,6 +508,23 @@ function buildBaseHeaders(result) {
508
508
  if (result.engineInfo.rulesetHash) {
509
509
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
510
510
  }
511
+ const meta = result.detectionDetail.metadata;
512
+ if (meta) {
513
+ const verifiedTier = meta.verified_tier;
514
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
515
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
516
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
517
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
518
+ }
519
+ const verifiedVendor = meta.verified_vendor;
520
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
521
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
522
+ }
523
+ const verifiedProtocol = meta.verified_protocol;
524
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
525
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
526
+ }
527
+ }
511
528
  return headers;
512
529
  }
513
530
  function httpStatusForBlockReason(reason) {
@@ -487,6 +487,23 @@ function buildBaseHeaders(result) {
487
487
  if (result.engineInfo.rulesetHash) {
488
488
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
489
489
  }
490
+ const meta = result.detectionDetail.metadata;
491
+ if (meta) {
492
+ const verifiedTier = meta.verified_tier;
493
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
494
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
495
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
496
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
497
+ }
498
+ const verifiedVendor = meta.verified_vendor;
499
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
500
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
501
+ }
502
+ const verifiedProtocol = meta.verified_protocol;
503
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
504
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
505
+ }
506
+ }
490
507
  return headers;
491
508
  }
492
509
  function httpStatusForBlockReason(reason) {
@@ -512,6 +512,23 @@ function buildBaseHeaders(result) {
512
512
  if (result.engineInfo.rulesetHash) {
513
513
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
514
514
  }
515
+ const meta = result.detectionDetail.metadata;
516
+ if (meta) {
517
+ const verifiedTier = meta.verified_tier;
518
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
519
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
520
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
521
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
522
+ }
523
+ const verifiedVendor = meta.verified_vendor;
524
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
525
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
526
+ }
527
+ const verifiedProtocol = meta.verified_protocol;
528
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
529
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
530
+ }
531
+ }
515
532
  return headers;
516
533
  }
517
534
  function httpStatusForBlockReason(reason) {
@@ -510,6 +510,23 @@ function buildBaseHeaders(result) {
510
510
  if (result.engineInfo.rulesetHash) {
511
511
  headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
512
512
  }
513
+ const meta = result.detectionDetail.metadata;
514
+ if (meta) {
515
+ const verifiedTier = meta.verified_tier;
516
+ if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
517
+ headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
518
+ } else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
519
+ headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
520
+ }
521
+ const verifiedVendor = meta.verified_vendor;
522
+ if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
523
+ headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
524
+ }
525
+ const verifiedProtocol = meta.verified_protocol;
526
+ if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
527
+ headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
528
+ }
529
+ }
513
530
  return headers;
514
531
  }
515
532
  function httpStatusForBlockReason(reason) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-os/checkpoint-wasm-runtime",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Checkpoint WASM runtime for AI agent detection across all environments (formerly @kya-os/agentshield-wasm-runtime)",
5
5
  "keywords": [
6
6
  "ai",
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "files": [
14
14
  "kya_os_engine_bg.wasm",
15
- "kya_os_engine.js"
15
+ "kya_os_engine.js",
16
+ "kya_os_engine.d.ts"
16
17
  ],
17
18
  "main": "kya_os_engine.js",
18
19
  "keywords": [
@@ -23,5 +24,6 @@
23
24
  "did"
24
25
  ],
25
26
  "type": "commonjs",
26
- "_note": "wasm-bindgen --target nodejs output: uses module.exports + require('fs') at module load. The parent wasm/package.json says type:module (for the older agentshield_wasm.js ESM file), which would make Node mis-classify this CJS file and throw ERR_REQUIRE_ESM. This nested package.json overrides per Node's nearest-package.json resolution algorithm. See SDK-WASM-Bundler-Loader-1 (#2613) for the original incident; AIVF-1 Path B (#2639) re-instated this override after a wasm-pack regen silently dropped it, plus a regen-pipeline patcher (rust/scripts/build-engine-wasm.sh) + an integrity test (packages/checkpoint-wasm-runtime/src/__tests__/wasm-artifact-integrity.test.ts) so the next regen can't silently break Node consumers again."
27
+ "types": "kya_os_engine.d.ts",
28
+ "_note": "wasm-bindgen --target nodejs output: uses module.exports + require('fs') at module load. The parent wasm/package.json says type:module (for the older agentshield_wasm.js ESM file), which would make Node mis-classify this CJS file and throw ERR_REQUIRE_ESM. This nested package.json overrides per Node's nearest-package.json resolution algorithm. See SDK-WASM-Bundler-Loader-1 (#2613) for the original incident; AIVF-1 Path B (#2639) re-instated this override after a wasm-pack regen silently dropped it, plus a regen-pipeline patcher (rust/scripts/build-engine-wasm.sh) + an integrity test (packages/checkpoint-wasm-runtime/src/__tests__/wasm-artifact-integrity.test.ts) so the next regen can't silently break Node consumers again. Engine-Pattern-Codegen-Retirement-1 extended the patcher to also restore the types/`files` .d.ts references that --no-typescript drops."
27
29
  }
@@ -13,7 +13,8 @@
13
13
  },
14
14
  "files": [
15
15
  "kya_os_engine_bg.wasm",
16
- "kya_os_engine.js"
16
+ "kya_os_engine.js",
17
+ "kya_os_engine.d.ts"
17
18
  ],
18
19
  "main": "kya_os_engine.js",
19
20
  "sideEffects": [
@@ -25,5 +26,6 @@
25
26
  "mcp-i",
26
27
  "agent",
27
28
  "did"
28
- ]
29
- }
29
+ ],
30
+ "types": "kya_os_engine.d.ts"
31
+ }