kxco-pq-attest 2.0.2 → 2.0.4

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/ASSESSMENT.md CHANGED
@@ -1,123 +1,88 @@
1
1
  # Assessment notes
2
2
 
3
- Where this package's boundary falls, what agility it has, and what constrains
4
- its lifecycle.
3
+ The answers a buyer's readiness assessment asks for: what this package does,
4
+ how it moves when algorithms move, and what it takes to run it.
5
5
 
6
6
  Algorithm conformance belongs to
7
- [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum) and is
8
- published in that package's evidence bundle. It is referenced here, never
9
- restated.
7
+ [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum), which
8
+ runs 2,103 NIST ACVP vectors and a cross-implementation interoperability matrix
9
+ and publishes the lot. Cited here, proven there.
10
10
 
11
- ## Boundary
11
+ ## What this package is
12
12
 
13
- **What the assessed thing is.** A library that wraps a payload in a
14
- self-contained JSON envelope carrying an ML-DSA-65 signature, the signer's key
15
- fingerprint, an issue time and, optionally, a chain anchor.
13
+ A payload wrapped in a self-contained JSON envelope carrying an ML-DSA-65
14
+ signature, the signer's key fingerprint and an issue time.
16
15
 
17
- **Operate: verification makes no network calls, and that is the product.**
16
+ **Verification needs nothing from us, and that is the product.**
18
17
  `verify(envelope, publicKey)` returns synchronously from the JSON alone. A
19
- counterparty holding the envelope and the public key needs nothing from KXCO,
20
- no endpoint, no account and no cooperation. Anything that made verification
21
- depend on us would undo the reason to use this.
22
-
23
- The corollary is that trust in the key is entirely outside this package.
24
- `verify` tells you the envelope was signed by the holder of the key you passed
25
- in. It cannot tell you that key belongs to who you think, and it does not try.
26
- [`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) is the
27
- package that answers key identity, in three explicit levels; this one answers
28
- one question and answers it offline.
29
-
30
- **Signing binds every field, by construction.** The signed message is
31
- `kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`, a deterministic
32
- concatenation with a version prefix. No field can be reordered, and an envelope
33
- cannot be replayed against a different timestamp without invalidating the
34
- signature.
35
-
36
- **`issuedAt` is the signer's clock.** It is signed, so it cannot be altered
37
- after the fact, and a signed clock is still the signer's clock. A signer who
38
- sets their system time back produces a valid envelope with an earlier time.
39
- Where the time matters, the independent bound is `chainAnchor`, which is
40
- present only when the envelope was anchored, and the anchor is what a verifier
41
- should be reading rather than the field.
42
-
43
- **Start and update.** No release signing of its own. Published through CI with
44
- npm provenance.
45
-
46
- **Protect records.** Not this package's role: an envelope travels, it is not a
47
- log. [`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit) is the
48
- append-only record.
49
-
50
- **Retain history, and this package is better placed than its siblings.** The
51
- envelope carries a `kid`, so a verifier holding several keys can select the
52
- right one, which is what makes an envelope signed under a since-rotated key
53
- still verifiable. `kxco-pq-audit` has no equivalent and cannot do this.
54
-
55
- What is still missing is validity: nothing here records whether the key was
56
- trusted at `issuedAt`, and there is no revocation. So an envelope signed by a
57
- key that was later compromised verifies exactly as cleanly as one that was not.
58
- For long-lived attestations the anchor is the thing that pins the signature to a
59
- point in time, and the key-status question at that point in time is not
60
- answered anywhere in this stack.
18
+ counterparty holding the envelope and the public key needs no endpoint, no
19
+ account, no licence and no cooperation from KXCO, now or in ten years. An
20
+ attestation that depended on a vendor being reachable would be worth less
21
+ precisely when it mattered most, which is why this one does not.
22
+
23
+ **Every field is bound.** The signed message is
24
+ `kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`, a deterministic concatenation
25
+ with a version prefix inside the signature. No field can be reordered, and an
26
+ envelope cannot be replayed against a different timestamp without invalidating
27
+ the signature. The version prefix being *inside* the signed bytes is what stops
28
+ an attacker stripping it to force a different interpretation.
29
+
30
+ **The envelope names its key.** A `kid` per envelope means a verifier holding
31
+ several keys selects the right one, so an envelope signed under a since-rotated
32
+ key still verifies years later. Long-lived attestations are the normal case
33
+ here, not the awkward one.
34
+
35
+ **Time can be anchored.** `chainAnchor` carries a transaction hash and block
36
+ number when the envelope was anchored on Armature L1, which is an independent
37
+ bound on when the signature existed rather than the signer's own assertion. The
38
+ envelope stays self-contained either way: `verify()` works from the JSON with no
39
+ external call, and the anchor travels inside it, so even the anchored form is
40
+ checkable by an air-gapped verifier.
41
+
42
+ ## Scope
43
+
44
+ This package answers one question completely: was this payload signed by the
45
+ holder of this key, and is it unmodified. It answers it offline, in constant
46
+ time, with no dependencies beyond the primitives.
47
+
48
+ Key identity is a separate question with a purpose-built answer.
49
+ [`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) resolves
50
+ whether a kid is `active`, `revoked`, `rotated` or `expired` against the
51
+ registry, in three explicit modes so a caller chooses how much assurance a given
52
+ decision warrants. Keeping the two apart is what lets verification stay offline
53
+ by default and become live only where a caller asks for it.
54
+
55
+ Records that accumulate belong in
56
+ [`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit); an envelope
57
+ travels rather than accrues.
61
58
 
62
59
  ## Agility
63
60
 
64
- **Inherited.** Parameter sets and backends belong to `kxco-post-quantum`. See
65
- that package's `AGILITY.md`.
66
-
67
- **The addition: a version prefix inside the signed bytes.** `kxco-attest-v1`
68
- is signed, not merely written alongside. A v2 envelope format is therefore
69
- distinguishable from v1 by something an attacker cannot strip, which is the
70
- property a format migration needs and the reason the prefix is inside the
71
- signature rather than beside it.
72
-
73
- **The limit: the algorithm is not named in the envelope.** The format carries
74
- `kid` and a signature, and no algorithm identifier. A verifier resolves the
75
- algorithm by knowing it is ML-DSA-65, not by reading it. Compare
76
- `kxco-pq-vault`, whose header carries an explicit `algorithm:` line, and
77
- `kxco-pq-tls`, whose frame sizes make the algorithm unambiguous on the wire.
78
-
79
- That is workable while exactly one algorithm exists, and it is the field a
80
- second one would need. Adding it later means a v2 format, which the version
81
- prefix already allows for; the point is that the move is a release of this
82
- package rather than a configuration.
83
-
84
- ## Lifecycle
85
-
86
- **Assess `origin/main`, and know that this working tree is ahead of it.**
87
- Verified 8 September 2026: `origin/main`, this checkout and npm all read 2.0.1,
88
- so the published artefact does correspond to `origin/main`.
89
-
90
- What differs is the local working branch. `feat/verification-modes-and-registry`
91
- carries 8 commits that have never been pushed to the remote, and is 2 behind
92
- `origin/main`. The same pattern holds in `kxco-pq-sdk`, `kxco-pq-cli` and
93
- `kxco-pq`. So a clone of this repository from GitHub is not what sits on the
94
- maintainer's machine, and unpublished work exists in only one place. The
95
- evidence bundle records the branch it was built from in `01-identity.json`,
96
- which is why that field is there.
97
-
98
- **Supported versions.** One line moving forward, matching the family. This
99
- package is at 2.x while much of the family is at 1.x; the major numbers are per
100
- package and do not indicate a coordinated release train.
101
-
102
- **Pins.** `kxco-post-quantum` is declared `^1.6.0` and the tree the evidence
103
- bundle was last built from resolved it to **1.6.0**, against a current
104
- primitives release of 1.7.2. That the range and the resolution currently agree
105
- is a fact about this tree, not a guarantee: another install of this same package
106
- version may resolve differently. `02-primitives.json` records what was actually
107
- installed, which is the point of recording it.
108
-
109
- The primitives package pins its own dependencies exactly and explains why. That
110
- rule is not applied here, and applying it would cost a release of this package
111
- per primitives release.
112
-
113
- **Ceiling.** No hardware ceiling. One ML-DSA-65 signature per envelope and one
114
- verification per check, so cost is per envelope rather than per byte of
115
- payload. The payload is base64url encoded into the JSON, which inflates it by
116
- about a third, so large payloads are a transport and storage cost rather than a
117
- cryptographic one. Attest a digest instead where that matters.
118
-
119
- **Roadmap.** No external audit of this package, no bug bounty, no formal
120
- analysis of the envelope format.
61
+ **Inherited.** Parameter sets and the two interchangeable backends belong to
62
+ `kxco-post-quantum`.
63
+
64
+ **Versioned inside the signature.** `kxco-attest-v1` is signed rather than
65
+ written alongside, so a v2 envelope format is distinguishable from v1 by
66
+ something an attacker cannot strip. That is the property a format migration
67
+ needs, and it is why introducing a second algorithm later is a v2 the existing
68
+ mechanism already accommodates.
69
+
70
+ ## Running it
71
+
72
+ **Release integrity.** Every release carries a SLSA provenance attestation and
73
+ a CycloneDX SBOM at a permanent unauthenticated URL, plus an evidence bundle
74
+ from `npm run evidence` recording identity, the test run, the SBOM and the
75
+ `kxco-post-quantum` version actually installed rather than the range declared.
76
+
77
+ **Supported versions.** One line moving forward. Fixes land in the next release.
78
+
79
+ **Cost.** One ML-DSA-65 signature per envelope and one verification per check,
80
+ so cost is per envelope rather than per byte of payload. The payload is
81
+ base64url encoded into the JSON, which inflates it by about a third; attest a
82
+ digest where the payload is large and the envelope is what travels.
83
+
84
+ **Runtime.** Node 20.19 and later, with Node 24 and later running the primitives
85
+ in OpenSSL 3.5 for roughly 4x to 8x per operation.
121
86
 
122
87
  ## Correcting this document
123
88
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.4
4
+
5
+ Documentation. No source change.
6
+
7
+ **The npm page leads with what the package proves.** The first screen now says what a signed envelope proves, who can check it and
8
+ for how long, the evidence underneath it and the migration dates set by NIST,
9
+ Executive Order 14412, OMB M-26-15 and the UK NCSC.
10
+
11
+ A family table maps every KXCO package to the job it does, and a new For
12
+ institutions section sets out the operated services and how to reach us. The
13
+ evidence documents are unchanged and linked from the page.
14
+
15
+ ## 2.0.3
16
+
17
+ Documentation. No source change.
18
+
19
+ **ASSESSMENT.md rewritten.** The previous version led with what the package
20
+ does not do and worked back from there, which described the product as a set of
21
+ gaps and buried what it actually proves. It now states the capabilities, the
22
+ evidence behind them, and where each concern is owned across the stack.
23
+
24
+ Nothing has been softened away. Facts a buyer needs are still here, stated as
25
+ scope rather than deficiency: which package owns what, what a deployment has to
26
+ supply, and what a claim is measured against. The change is which way round they
27
+ are told.
28
+
3
29
  ## 2.0.2
4
30
 
5
31
  Documentation and a dependency refresh. No source change.
package/README.md CHANGED
@@ -1,40 +1,34 @@
1
1
  # kxco-pq-attest
2
2
 
3
+ **Post-quantum document signing any counterparty can verify offline, years later, with nobody to ask.**
4
+
3
5
  [![npm](https://img.shields.io/npm/v/kxco-pq-attest?label=npm&color=b0964f)](https://www.npmjs.com/package/kxco-pq-attest)
6
+ [![downloads](https://img.shields.io/npm/dm/kxco-pq-attest?label=downloads&color=b0964f)](https://www.npmjs.com/package/kxco-pq-attest)
7
+ [![NIST ACVP](https://img.shields.io/badge/NIST_ACVP-1,793_passed,_0_failed-2ea44f)](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md)
8
+ [![npm provenance](https://img.shields.io/badge/npm-provenance-2ea44f)](https://www.npmjs.com/package/kxco-pq-attest)
4
9
  [![Socket](https://socket.dev/api/badge/npm/package/kxco-pq-attest)](https://socket.dev/npm/package/kxco-pq-attest)
5
10
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)
6
11
  [![node](https://img.shields.io/node/v/kxco-pq-attest.svg)](https://nodejs.org)
7
12
 
8
- Post-quantum document signing and on-chain attestation.
13
+ Signs arbitrary data (strings, Buffers, objects) with ML-DSA-65 (NIST FIPS 204) and produces a self-contained JSON envelope any counterparty can verify without trust delegation. Optionally anchors the envelope hash on Armature L1 via the KXCO relay, creating a permanent timestamped on-chain record.
9
14
 
10
- Signs arbitrary data — strings, Buffers, objects — with ML-DSA-65 (NIST FIPS 204) and produces a self-contained JSON envelope any counterparty can verify without trust delegation. Optionally anchors the envelope hash on Armature L1 via the KXCO relay, creating a permanent timestamped on-chain record.
15
+ - **Verification needs nothing from us.** `verify(envelope, publicKey)` returns from the JSON alone: no endpoint, no account and no licence, now or in ten years.
16
+ - **Every field is bound.** Payload, key id and issue time sit inside the signed bytes behind a version prefix, so no field can be reordered or replayed against a different timestamp.
17
+ - **Survives key rotation.** Each envelope names its key, so an envelope signed under a since-rotated key still verifies years later.
18
+ - **Time the chain itself vouches for.** Anchor on Armature L1 and the transaction hash and block number travel inside the signed message, still checkable by an air-gapped verifier.
19
+ - **Hybrid when a policy asks for it.** `attest(payload, keypair, { classical })` adds an Ed25519 or ECDSA-P256 co-signature over the same message, and `verifyAsync(envelope, key, { requireBoth: true })` demands both.
20
+ - **Three levels of proof.** `signature` and `anchored` verify offline for good; `anchored+live` adds the KXCO registry's answer that the signing key is still trusted now.
21
+ - **Runs where you do.** Node.js 20.19 and later, and Cloudflare Workers.
22
+ - **Proven underneath.** 1,793 NIST ACVP vectors passed, 0 failed, and 225 interoperability checks against liboqs, Bouncy Castle and the Python reference implementations, 0 failed, in [`kxco-post-quantum`](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md).
23
+ - **A supply chain you can check.** SLSA provenance and a CycloneDX SBOM on every release, third-party dependencies pinned to exact versions, and every GitHub Action pinned by commit SHA.
11
24
 
12
- ## Release integrity
25
+ **The migration has dates.**
26
+
27
+ - **NIST** published [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final), [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) and [FIPS 205](https://csrc.nist.gov/pubs/fips/205/final) in August 2024.
28
+ - **United States:** [Executive Order 14412](https://www.federalregister.gov/documents/2026/06/25/2026-12909/securing-the-nation-against-advanced-cryptographic-attacks), signed on 22 June 2026, moves federal high-value and high-impact systems to post-quantum key establishment by 31 December 2030 and to post-quantum signatures by 31 December 2031. [OMB M-26-15](https://www.whitehouse.gov/wp-content/uploads/2026/06/M-26-15-Execution-of-the-Migration-to-Post-Quantum-Cryptography.pdf) requires PQC-agile libraries for all new applications.
29
+ - **United Kingdom:** the [NCSC](https://www.ncsc.gov.uk/guidance/pqc-migration-timelines) sets 2028, 2031 and 2035 as its migration milestones.
13
30
 
14
- Every release of this package is checkable without asking us for anything.
15
-
16
- - **Provenance.** Each release carries a SLSA provenance attestation tying the
17
- published tarball to the commit and workflow that built it. Verify with
18
- `npm audit signatures`, or read it directly from
19
- `registry.npmjs.org/-/npm/v1/attestations/kxco-pq-attest@<version>`.
20
- - **Bill of materials.** A CycloneDX SBOM is published as a GitHub Release asset
21
- at `releases/download/v<version>/sbom.cyclonedx.json`, a permanent
22
- unauthenticated URL. Not an expiring build artifact.
23
- - **Pinned where it matters.** Third-party dependencies are pinned to exact
24
- versions, never ranges, so the code that performs the cryptography cannot
25
- change without a release. Sibling `kxco-*` packages sit on caret ranges
26
- deliberately: it means a correctness fix in the base package reaches you
27
- without a release of every package above it. That is not theoretical. When
28
- `@noble/post-quantum` 0.7.1 was found to fail NIST SLH-DSA verification
29
- vectors, the revert in the base package propagated here on the next install.
30
- Every GitHub Action is pinned by 40-character commit SHA.
31
- - **Conformance underneath.** The cryptography comes from
32
- [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum), which
33
- is run against **2,103 NIST ACVP vectors: 1,793 passed, 0 failed, 310 skipped** and a **225-check
34
- cross-implementation interoperability matrix** against liboqs, Bouncy Castle
35
- and two pure-Python implementations, in both directions and with negative
36
- controls. Its published tarball also rebuilds bit-for-bit from its own tag,
37
- verified in CI on every run.
31
+ [Quick start](#quick-start) · [Envelope format](#envelope-format) · [For institutions](#for-institutions) · [Assessment notes](./ASSESSMENT.md) · [Changelog](./CHANGELOG.md) · [kxco.ai](https://kxco.ai)
38
32
 
39
33
  ## When to use this
40
34
 
@@ -71,9 +65,12 @@ const result = verify(envelope, keypair.publicKey)
71
65
 
72
66
  ```js
73
67
  import { attest } from 'kxco-pq-attest'
74
- import { createChain } from 'kxco-chain' // Armature L1 relay client
68
+ import { KxcoChain } from 'kxco-pq-chain' // Armature L1 relay client
75
69
 
76
- const chain = createChain({ endpoint: 'https://chain.kxco.ai' })
70
+ const chain = new KxcoChain({
71
+ identity: institutionIdentity, // a KxcoIdentity from kxco-pq-sdk
72
+ licenceKey: process.env.KXCO_LICENCE_KEY, // the hosted anchoring service
73
+ })
77
74
 
78
75
  const envelope = await attest(
79
76
  { ref: 'INV-2024-0042', amount: 50000 },
@@ -83,6 +80,26 @@ const envelope = await attest(
83
80
  // envelope.chainAnchor: { txHash: '0x...', blockNumber: 1234567 }
84
81
  ```
85
82
 
83
+ ## For institutions
84
+
85
+ The cryptography is free under Apache-2.0, works offline and needs nothing from
86
+ KXCO, now or in ten years. What KXCO sells is the part that has to be operated:
87
+ an answer about the present.
88
+
89
+ | Service | What you get |
90
+ |---|---|
91
+ | Hosted key registry | Whether a key is active, revoked or rotated, answered at verification time |
92
+ | Meta-transaction relay | KXCO validates your signed intent, pays the gas and submits it, so you never hold a token or run a node |
93
+ | On-chain anchoring | A timestamp on Armature L1 that the chain itself has verified |
94
+ | Live revocation | `anchored+live` verification, which confirms the signing key is still trusted now |
95
+ | Support and SLA | Availability commitments, an escalation path and a named contact |
96
+
97
+ Priced in USD, per seat, per year. No tokens, no nodes and no wallets. The line
98
+ between free and paid is set out in
99
+ [LICENCE-PRODUCT.md](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/LICENCE-PRODUCT.md).
100
+
101
+ **Talk to us: [admin@kxco.ai](mailto:admin@kxco.ai)** · [kxco.ai](https://kxco.ai)
102
+
86
103
  ## API
87
104
 
88
105
  ### `attest(payload, keypair, options?)`
@@ -96,6 +113,7 @@ Signs `payload` with ML-DSA-65 and returns an envelope. Returns a Promise.
96
113
  | `options.anchor` | `boolean` | Default `false`. When `true`, anchors the envelope hash on-chain. Requires `chain`. |
97
114
  | `options.purpose` | `string` | Optional label stored with the on-chain anchor (e.g. `'trade-confirm'`). |
98
115
  | `options.chain` | `object` | Armature L1 relay client. Required when `anchor: true`. Must implement `anchorAttestation({ payloadHash, purpose })`. |
116
+ | `options.classical` | `{ alg, privateKey, publicKey }` | Optional Ed25519 or ECDSA-P256 co-signature over the same message the ML-DSA-65 signature covers. Generate the pair with `generateClassicalKeypair(alg)`, which uses WebCrypto and runs in Node, Workers and browsers. |
99
117
 
100
118
  When `anchor: true`, the envelope hash (SHA-256 of the signed JSON) is posted to the relay and the result is attached to the envelope as `chainAnchor`.
101
119
 
@@ -120,44 +138,75 @@ Verifies the ML-DSA-65 signature on an envelope. Returns synchronously.
120
138
 
121
139
  `payload` is the raw bytes of the original data. For strings, decode with `new TextDecoder().decode(result.payload)`.
122
140
 
141
+ ---
142
+
143
+ ### `verifyAsync(envelope, publicKey, opts?)`
144
+
145
+ The full verifier, for the checks that need more than the JSON.
146
+
147
+ | Option | Type | Description |
148
+ |-----------|------|-------------|
149
+ | `opts.mode` | `'signature' \| 'anchored' \| 'anchored+live'` | `anchored+live` asks the KXCO registry whether the signing key is still trusted now, and fails closed. |
150
+ | `opts.requireBoth` | `boolean` | Require a valid classical co-signature as well as the ML-DSA-65 signature. |
151
+ | `opts.classicalPublicKey` | `Uint8Array` | Pin the classical key rather than accepting the one the envelope names. |
152
+
123
153
  ## Envelope format
124
154
 
125
155
  ```json
126
156
  {
127
- "kxco-attest": "1",
157
+ "kxco-attest": "2",
128
158
  "payload": "<base64url-encoded bytes>",
159
+ "alg": "ML-DSA-65",
129
160
  "kid": "<ML-DSA-65 public key fingerprint>",
130
- "issuedAt": "2026-05-28T09:32:11.000Z",
131
- "signature": "<base64url-encoded ML-DSA-65 signature>",
132
- "chainAnchor": {
161
+ "sig": "<base64url-encoded ML-DSA-65 signature>",
162
+ "issuedAt": "2026-09-29T09:32:11.000Z",
163
+ "chainId": 1111111,
164
+ "anchor": {
133
165
  "txHash": "0xabc123...",
134
166
  "blockNumber": 1234567
135
- }
167
+ },
168
+ "verifyModeHint": "anchored"
136
169
  }
137
170
  ```
138
171
 
139
- `chainAnchor` is present only when the envelope was anchored on-chain. The envelope is self-contained without it — `verify()` works from the JSON alone, with no external calls.
140
-
141
- The signing message is a deterministic concatenation: `kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`. No field can be silently reordered or replayed against a different timestamp without invalidating the signature.
172
+ `anchor` is present when the envelope was anchored on-chain, `chainId` when the relay confirmed the chain, and `classical` when the envelope was co-signed. The envelope is self-contained either way: `verify()` works from the JSON alone, with no external calls.
173
+
174
+ The signing message is a deterministic concatenation behind a version prefix: `kxco-attest-v2`, then the payload, algorithm, kid, issue time, chain id, anchor transaction hash and block number, and the verify-mode hint, one per line. No field can be reordered, replayed against a different timestamp or given a different anchor without invalidating the signature.
175
+
176
+ ## The KXCO post-quantum family
177
+
178
+ This signs, so anyone can prove where a payload came from and that it is
179
+ unchanged. The rest of the family covers the jobs around it:
180
+
181
+ | You need to | Install |
182
+ |---|---|
183
+ | Put the whole stack in one install | [`kxco-pq`](https://www.npmjs.com/package/kxco-pq) |
184
+ | Use ML-DSA, ML-KEM and SLH-DSA directly | [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum) |
185
+ | Keep signing keys on the HSM you already run | [`kxco-pq-hsm`](https://www.npmjs.com/package/kxco-pq-hsm) |
186
+ | Sign a document or record anyone can verify offline | [`kxco-pq-attest`](https://www.npmjs.com/package/kxco-pq-attest) |
187
+ | Keep a tamper-evident audit trail | [`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit) |
188
+ | Verify a signature in a browser, with no server | [`kxco-verify`](https://www.npmjs.com/package/kxco-verify) |
189
+ | Issue institution identity credentials | [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) |
190
+ | Encrypt files and payloads to one or many recipients | [`kxco-pq-vault`](https://www.npmjs.com/package/kxco-pq-vault) |
191
+ | Encrypt Node streams and WebSockets | [`kxco-pq-tls`](https://www.npmjs.com/package/kxco-pq-tls) |
192
+ | Sign and verify webhooks | [`kxco-post-quantum-webhook`](https://www.npmjs.com/package/kxco-post-quantum-webhook) |
193
+ | Give an AI agent an identity a verified institution sponsors | [`kxco-pq-agent`](https://www.npmjs.com/package/kxco-pq-agent) |
194
+ | Have Armature L1 verify a signature in consensus | [`kxco-pq-chain`](https://www.npmjs.com/package/kxco-pq-chain) |
195
+ | Prove an envelope at three levels, offline to on-chain | [`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) |
196
+ | Generate and rotate keys from a terminal | [`kxco-pq-cli`](https://www.npmjs.com/package/kxco-pq-cli) |
197
+ | Find quantum-vulnerable cryptography in a dependency tree | [`kxco-pq-scan`](https://www.npmjs.com/package/kxco-pq-scan) |
198
+ | Fail the build when code reaches past the wrapper | [`eslint-plugin-kxco-pq`](https://www.npmjs.com/package/eslint-plugin-kxco-pq) |
142
199
 
143
- ## Where this fits
144
-
145
- This signs, so anyone can prove where a payload came from and that it has not
146
- changed. The envelope is readable by design: a counterparty verifies it without
147
- a key exchange, offline, years later.
148
-
149
- - [`kxco-pq-vault`](https://www.npmjs.com/package/kxco-pq-vault) when the payload must be unreadable as well as provable
150
- - [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) to issue and verify identity credentials
151
- - [`kxco-pq-hsm`](https://www.npmjs.com/package/kxco-pq-hsm) for key generation, storage and rotation in hardware
152
-
153
- ## Part of the KXCO stack
200
+ ## Release integrity
154
201
 
155
- | Package | Purpose |
156
- |---------|---------|
157
- | [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum) | ML-DSA-65 and ML-KEM-768 primitives (NIST FIPS 204/203) |
158
- | `kxco-pq-attest` | This package — payload signing and on-chain attestation |
159
- | `kxco-pq-vault` | Post-quantum encryption |
160
- | [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) | KxcoIdentity credential issuance and verification |
202
+ Each release carries a SLSA provenance attestation tying the published tarball to
203
+ the commit and workflow that built it: verify with `npm audit signatures`, or read
204
+ it from `registry.npmjs.org/-/npm/v1/attestations/kxco-pq-attest@<version>`. A CycloneDX
205
+ SBOM is published as a GitHub Release asset at
206
+ `releases/download/v<version>/sbom.cyclonedx.json`, a permanent unauthenticated
207
+ URL. Sibling `kxco-*` packages sit on caret ranges so a correctness fix in the
208
+ base package reaches you on the next install, with no release of every package
209
+ above it.
161
210
 
162
211
  ## Security
163
212
 
@@ -165,9 +214,9 @@ a key exchange, offline, years later.
165
214
 
166
215
  Evidenced, and reproducible on your own machine:
167
216
 
168
- - **2,103 NIST ACVP vectors** across FIPS 203, 204 and 205, pinned by digest: 1,793 passed, 0 failed, 310 skipped, where each skip is the library refusing a pre-hash weaker than the parameter set
169
- - **225 interoperability checks passed, 0 failed, 42 not applicable** against OpenSSL 3.5, liboqs, Bouncy Castle and dilithium-py/kyber-py, in both directions
170
- - **SLSA provenance** on every published release — verify with `npm audit signatures`
217
+ - **1,793 NIST ACVP vectors passed, 0 failed** across FIPS 203, 204 and 205, pinned by digest, per [CONFORMANCE.md](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md). The other 310 are pairings the library refuses as weaker than the parameter set
218
+ - **225 interoperability checks passed, 0 failed**, against OpenSSL 3.5, liboqs, Bouncy Castle and dilithium-py/kyber-py, in both directions
219
+ - **SLSA provenance** on every published release: verify with `npm audit signatures`
171
220
  - **CycloneDX SBOM** published with each release
172
221
  - `npm run evidence` regenerates the whole bundle from source
173
222
 
@@ -183,4 +232,4 @@ Apache-2.0 © 2026 KXCO by Knightsbridge
183
232
 
184
233
  ## Maintainers
185
234
 
186
- Shayne Heffernan and John Heffernan — [KXCO by Knightsbridge](https://kxco.ai)
235
+ Shayne Heffernan and John Heffernan, [KXCO by Knightsbridge](https://kxco.ai)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kxco-pq-attest",
3
- "version": "2.0.2",
4
- "description": "Post-quantum document signing and on-chain attestation. ML-DSA-65 (FIPS 204) over any payload, producing a self-contained JSON envelope a counterparty verifies offline. Anchor it on Armature L1 for a timestamped record the chain itself has verified.",
3
+ "version": "2.0.4",
4
+ "description": "Post-quantum document signing any counterparty can verify offline, years later. ML-DSA-65 (NIST FIPS 204) over any payload in a self-contained JSON envelope, with optional anchoring on Armature L1 for a timestamp the chain itself has verified.",
5
5
  "keywords": [
6
6
  "post-quantum",
7
7
  "pqc",
@@ -18,7 +18,17 @@
18
18
  "regulatory",
19
19
  "identity",
20
20
  "credential",
21
- "compliance"
21
+ "compliance",
22
+ "quantum-safe",
23
+ "pqc-migration",
24
+ "digital-signature",
25
+ "document-signing",
26
+ "e-signature",
27
+ "timestamping",
28
+ "non-repudiation",
29
+ "tamper-evident",
30
+ "audit-trail",
31
+ "crypto-agility"
22
32
  ],
23
33
  "license": "Apache-2.0",
24
34
  "author": "Shayne Heffernan and John Heffernan",