@nexart/cli 0.15.1 → 0.16.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 +99 -0
- package/README.md +184 -31
- package/dist/index.d.ts +49 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +557 -70
- package/dist/index.js.map +1 -1
- package/dist/standaloneVerify.d.ts +18 -2
- package/dist/standaloneVerify.d.ts.map +1 -1
- package/dist/standaloneVerify.js +40 -1
- package/dist/standaloneVerify.js.map +1 -1
- package/dist/trustRoots.d.ts +6 -0
- package/dist/trustRoots.d.ts.map +1 -0
- package/dist/trustRoots.js +84 -0
- package/dist/trustRoots.js.map +1 -0
- package/dist/tsaVerify.d.ts.map +1 -1
- package/dist/tsaVerify.js +58 -4
- package/dist/tsaVerify.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,104 @@
|
|
|
1
1
|
# @nexart/cli — Changelog
|
|
2
2
|
|
|
3
|
+
## v0.16.1 (2026-06-17) — `verify-record`: 3-state verification (VERIFIED / AUTHENTIC (REDACTED) / FAILED)
|
|
4
|
+
|
|
5
|
+
UX release. `verify-bundle` is **byte-for-byte unchanged**; the new third state applies
|
|
6
|
+
to `verify-record` (and the `verify-url` / `verify-hash` commands that render through it).
|
|
7
|
+
|
|
8
|
+
- **Privacy-redacted records are no longer reported as FAILED.** A public record served
|
|
9
|
+
from a node may have the snapshot's `input` / `output` *content* stripped while the
|
|
10
|
+
`inputHash` / `outputHash` anchors are kept. Because `certificateHash` is sealed over the
|
|
11
|
+
full snapshot, recompute is impossible on a redacted snapshot — but that is **not
|
|
12
|
+
tampering**. Such records now report **`FINAL STATUS: AUTHENTIC (REDACTED)`** with
|
|
13
|
+
**`⚠ Integrity: NOT RECOMPUTABLE (redacted)`** and **exit 0**, provided the receipt
|
|
14
|
+
signature still verifies.
|
|
15
|
+
- **Three exit-stable states.** `VERIFIED` (exit 0) → content present, hash recomputes;
|
|
16
|
+
`AUTHENTIC (REDACTED)` (exit 0) → content stripped, hashes kept, signature passes;
|
|
17
|
+
`FAILED` (exit 1) → a real content mismatch or an invalid signature. A genuine mismatch
|
|
18
|
+
is **never** softened to redacted, and a redacted record whose hash still recomputes
|
|
19
|
+
stays **VERIFIED**.
|
|
20
|
+
- **Report.** For a redacted record the Signature line is shown before the Integrity line,
|
|
21
|
+
and a short **Public Verification (Redacted)** note explains that full integrity
|
|
22
|
+
verification requires the original (un-redacted) execution data. `--short` now emits
|
|
23
|
+
`✔ VERIFIED` / `⚠ AUTHENTIC (REDACTED)` / `✖ FAILED`. `--json` gains a `redacted`
|
|
24
|
+
boolean, an `authentic-redacted` `status`, and a `redacted` `integrity` value.
|
|
25
|
+
- **No cryptographic change.** `certificateHash` computation, canonicalisation, and the
|
|
26
|
+
signature / timestamp logic are untouched — this is purely a classification and
|
|
27
|
+
presentation layer. `verify-bundle` does not opt into redaction classification and is
|
|
28
|
+
byte-for-byte unchanged.
|
|
29
|
+
- **UX hardening.** The redacted context block now names the redaction source
|
|
30
|
+
(`Redaction: Applied by certification node (privacy mode)`) and sits tight under the
|
|
31
|
+
headline. A present-but-unverifiable timestamp (parse failure, or verification not
|
|
32
|
+
attempted) reports `⚠ Timestamp: PRESENT BUT NOT VERIFIED` instead of
|
|
33
|
+
`INVALID (malformed token)`, and a missing one reports `Timestamp: NOT INCLUDED` — the
|
|
34
|
+
timestamp still never affects `FINAL STATUS`. Section order is fixed: Signature →
|
|
35
|
+
Integrity → Timestamp → Node → Identity → Execution.
|
|
36
|
+
- **Timestamp compatibility (no longer "malformed token").** RFC 3161 timestamp
|
|
37
|
+
verification now accepts the full `TimeStampResp` returned by the certification node
|
|
38
|
+
(PKIStatusInfo + `timeStampToken`) in addition to a bare `TimeStampToken` (CMS
|
|
39
|
+
`ContentInfo`). The wrapper is detected structurally and unwrapped to the inner
|
|
40
|
+
`ContentInfo` before the unchanged signature / imprint / chain checks run. This is a
|
|
41
|
+
parsing-layer fix only — `certificateHash`, the imprint binding, the signature logic,
|
|
42
|
+
and the "timestamp never affects `FINAL STATUS`/exit code" invariant are all
|
|
43
|
+
unchanged; an unparseable token still degrades to `PRESENT BUT NOT VERIFIED`.
|
|
44
|
+
|
|
45
|
+
## v0.16.0 (2026-06-17) — `verify-record`: fully automatic, audit-grade, self-contained
|
|
46
|
+
|
|
47
|
+
Feature release. `verify-bundle` is **byte-for-byte unchanged** — the redesigned
|
|
48
|
+
output and automatic key/timestamp resolution apply to `verify-record` only.
|
|
49
|
+
|
|
50
|
+
- **Automatic verification.** `nexart verify-record <file>` now verifies integrity,
|
|
51
|
+
the receipt signature, **and** the RFC 3161 timestamp with no extra flags. The
|
|
52
|
+
`--verify-timestamp` gate is gone — timestamp verification always runs when a token
|
|
53
|
+
is present (the ASN.1 stack still loads lazily, only when a token exists).
|
|
54
|
+
- **Self-contained signing key resolution.** By priority: `--public-key` (explicit
|
|
55
|
+
override) → auto-fetched from the node's published key set at
|
|
56
|
+
`https://node.nexart.io/.well-known/nexart-node.json` (the key whose `kid` matches
|
|
57
|
+
the receipt's `attestorKeyId`) → a bundle-embedded key. The fetch is the **only**
|
|
58
|
+
network call, lives entirely in the CLI layer (the auditable `standaloneVerify.ts`
|
|
59
|
+
core stays `node:crypto`-only / no network), and fails gracefully. New **`--offline`**
|
|
60
|
+
flag disables it; the URL is overridable via `NEXART_NODE_WELLKNOWN_URL`.
|
|
61
|
+
- **Embedded DigiCert trust store.** Timestamps verify against a built-in trust store
|
|
62
|
+
(DigiCert Trusted Root G4 + DigiCert Assured ID Root CA) by default; **`--tsa-roots`**
|
|
63
|
+
overrides it with custom PEM roots.
|
|
64
|
+
- **Flexible input.** The bundle is read from `record.bundle` or, failing that, the
|
|
65
|
+
record root is treated as the bundle. The old "missing bundle" error is removed — a
|
|
66
|
+
malformed record now reports a clean **Integrity: FAIL** (exit `1`).
|
|
67
|
+
- **Timestamp precedence:** `verification.timestamp.token` → `timestamp.token` →
|
|
68
|
+
bundle-embedded (`trustedTimestamps[]` / singular `timestamp`).
|
|
69
|
+
- **Redesigned audit-grade report** (and `--json`): **Integrity**, **Signature**,
|
|
70
|
+
**Timestamp** (`VERIFIED` / `INVALID (<reason>)` / `PRESENT BUT NOT VERIFIED` /
|
|
71
|
+
`NOT INCLUDED IN RECORD` + generation time and authority), **Node** (node id, key id,
|
|
72
|
+
key source, operator), **Identity**, and **Execution** (protocol, provider, model,
|
|
73
|
+
input/output hashes, parameters, created-at). The report now ends with a single
|
|
74
|
+
headline line — **`FINAL STATUS: VERIFIED EVIDENCE RECORD`** or **`FINAL STATUS:
|
|
75
|
+
FAILED`**.
|
|
76
|
+
- **New `nexart verify-url <url>`.** One-command verification: fetches a CER record (or
|
|
77
|
+
a bare bundle) as JSON from a URL and verifies it through the **same** automatic
|
|
78
|
+
key/timestamp resolution and audit-grade renderer as `verify-record`. The payload is
|
|
79
|
+
auto-detected as a full record (`verification.receipt`) or a raw bundle — both route
|
|
80
|
+
through the rich verifier. The URL fetch is the record *source* and always runs;
|
|
81
|
+
`--offline` here only disables the separate signing-key auto-fetch. Bad HTTP status,
|
|
82
|
+
network errors, and non-JSON bodies fail cleanly with exit `1`.
|
|
83
|
+
- **New `nexart verify-hash <certificate_hash>`.** Resolves a record straight from its
|
|
84
|
+
certificate hash: constructs the public node lookup URL
|
|
85
|
+
(`https://node.nexart.io/v1/cer/public?certificate_hash=<hash>`) and verifies it via
|
|
86
|
+
`verify-url`, so the output is **identical** to `verify-url`. The hash must carry the
|
|
87
|
+
canonical `sha256:` prefix (a missing prefix fails cleanly with exit `1` before any
|
|
88
|
+
network call). Supports `--short`, `--offline`, `--json` (and the rest of the
|
|
89
|
+
`verify-url` options). Distinct from the existing `verify <snapshot>` command, which
|
|
90
|
+
is left untouched.
|
|
91
|
+
- **New `verify-record -` (stdin).** A `-` path reads the record from standard input,
|
|
92
|
+
enabling pipelines such as `curl -s … | nexart verify-record -`.
|
|
93
|
+
- **New `--short` flag** (`verify-record` and `verify-url`): prints only a single
|
|
94
|
+
verdict line — `✔ VERIFIED` or `✖ FAILED`.
|
|
95
|
+
- **Unchanged invariants:** the timestamp layer never modifies `certificateHash`, is
|
|
96
|
+
never canonicalised, and **never affects the Integrity/Signature result or the exit
|
|
97
|
+
code** (`0` only when integrity passes and the signature is not invalid, `1`
|
|
98
|
+
otherwise). Operational TSA errors (untrusted root, imprint mismatch, unreadable
|
|
99
|
+
`--tsa-roots`) are reported as a timestamp *status*, never a crash. `verify-bundle`
|
|
100
|
+
remains **byte-for-byte unchanged**.
|
|
101
|
+
|
|
3
102
|
## v0.15.1 (2026-06-17) — docs: correct README version header
|
|
4
103
|
|
|
5
104
|
Docs-only patch. No code or behaviour changes.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @nexart/cli v0.
|
|
1
|
+
# @nexart/cli v0.16.1
|
|
2
2
|
|
|
3
3
|
Command-line interface for NexArt — run, replay, and verify deterministic generative art, plus AI execution certification commands.
|
|
4
4
|
|
|
@@ -34,7 +34,9 @@ authenticity without relying on NexArt systems or source code.
|
|
|
34
34
|
### What this verifies
|
|
35
35
|
|
|
36
36
|
- **Integrity** — the record's `certificateHash` is recomputed from the record's
|
|
37
|
-
contents and compared to the stored value.
|
|
37
|
+
contents and compared to the stored value. A privacy-redacted public record
|
|
38
|
+
(input/output content stripped, hashes kept) is reported as **AUTHENTIC
|
|
39
|
+
(REDACTED)** rather than failed — see the [3-state model](#verifying-a-full-cer-record-nexart-verify-record).
|
|
38
40
|
- **Authenticity** — the Ed25519 signature over the node's receipt is verified,
|
|
39
41
|
when a signature is present.
|
|
40
42
|
|
|
@@ -691,13 +693,16 @@ nexart verify-bundle cer.json --verify-timestamp --tsa-roots tsa-roots.pem
|
|
|
691
693
|
not established.
|
|
692
694
|
|
|
693
695
|
3. **Timestamp (RFC 3161)** *(optional, additive — `--verify-timestamp`)* — when
|
|
694
|
-
the bundle carries an RFC 3161
|
|
696
|
+
the bundle carries an RFC 3161 timestamp (in `trustedTimestamps[]` or a
|
|
695
697
|
singular `timestamp`), this **offline** check confirms the token anchors the
|
|
696
|
-
bundle to a point in time.
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
698
|
+
bundle to a point in time. Both wire shapes are accepted: a full
|
|
699
|
+
`TimeStampResp` (the certification node's output) and a bare `TimeStampToken`
|
|
700
|
+
(e.g. OpenSSL's `-token_out`) — the response wrapper is detected and unwrapped
|
|
701
|
+
automatically. It verifies, in order: (1) the TSA's CMS signature over the
|
|
702
|
+
embedded `TSTInfo`; (2) that the token's message imprint equals the bundle's
|
|
703
|
+
`certificateHash` digest; (3) that the TSA signing certificate chains to a root
|
|
704
|
+
supplied via `--tsa-roots`; (4) that the certificate is valid at the timestamp;
|
|
705
|
+
and (5) that the timestamp is at or after the bundle's `createdAt`.
|
|
701
706
|
|
|
702
707
|
This layer is **strictly additive**: it never modifies `certificateHash`, is
|
|
703
708
|
never included in canonicalisation, and **never affects the Integrity or
|
|
@@ -798,35 +803,183 @@ optional externally anchored timestamp:
|
|
|
798
803
|
}
|
|
799
804
|
```
|
|
800
805
|
|
|
801
|
-
**
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
`verify-bundle` (it reuses the identical internal verifier — no duplicated crypto).
|
|
806
|
-
The record-level `timestamp.token` takes precedence over any token embedded in the
|
|
807
|
-
bundle; if the record omits it, an embedded `trustedTimestamps[]` / `timestamp` is
|
|
808
|
-
used instead. Everything else — flags, output, exit-code semantics — is identical
|
|
809
|
-
to `verify-bundle`.
|
|
806
|
+
**Fully automatic since v0.16.0.** Give it a record file and nothing else — the
|
|
807
|
+
command verifies integrity, the receipt signature, **and** the RFC 3161 timestamp
|
|
808
|
+
automatically, and prints an audit-grade report. It is self-contained: a third party
|
|
809
|
+
needs only the record and this CLI.
|
|
810
810
|
|
|
811
811
|
```bash
|
|
812
|
-
#
|
|
813
|
-
nexart verify-record record.json
|
|
812
|
+
# Full automatic verification: integrity + signature + timestamp
|
|
813
|
+
nexart verify-record record.json
|
|
814
814
|
|
|
815
|
-
# Machine-readable
|
|
816
|
-
nexart verify-record record.json --
|
|
815
|
+
# Machine-readable report
|
|
816
|
+
nexart verify-record record.json --json
|
|
817
|
+
|
|
818
|
+
# Read the record from stdin (use "-" as the path)
|
|
819
|
+
curl -s https://node.example/records/abc.json | nexart verify-record -
|
|
820
|
+
|
|
821
|
+
# One-line verdict only (✔ VERIFIED / ⚠ AUTHENTIC (REDACTED) / ✖ FAILED)
|
|
822
|
+
nexart verify-record record.json --short
|
|
823
|
+
|
|
824
|
+
# Fully offline, with an explicit signing key
|
|
825
|
+
nexart verify-record record.json --offline --public-key node-keys.json
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Flexible input.** The bundle is taken from `record.bundle` when present, or the
|
|
829
|
+
record root is treated as the bundle itself (a certified bundle with its receipt and
|
|
830
|
+
signature embedded works directly). There is no "missing bundle" error — a
|
|
831
|
+
structurally invalid record simply reports a clean **Integrity: FAIL**.
|
|
832
|
+
|
|
833
|
+
**Three-state model (since v0.16.1).** A public record served from a node may be
|
|
834
|
+
**privacy-redacted**: the snapshot's `input` / `output` *content* is stripped while the
|
|
835
|
+
`inputHash` / `outputHash` anchors are kept. Because `certificateHash` is sealed over the
|
|
836
|
+
full snapshot, it can no longer be recomputed from a redacted snapshot — but that is
|
|
837
|
+
**not tampering**. `verify-record` (and `verify-url` / `verify-hash`) detect this case and
|
|
838
|
+
report a third outcome:
|
|
839
|
+
|
|
840
|
+
| Outcome | When | Integrity line | Exit |
|
|
841
|
+
| --- | --- | --- | --- |
|
|
842
|
+
| **VERIFIED** | content present, `certificateHash` recomputes, signature not invalid | `✔ Integrity: PASS` | `0` |
|
|
843
|
+
| **AUTHENTIC (REDACTED)** | content stripped (hashes kept), recompute not possible, signature **passes** | `⚠ Integrity: NOT RECOMPUTABLE (redacted)` | `0` |
|
|
844
|
+
| **FAILED** | a real integrity mismatch (content present but hash differs) or an invalid signature | `✖ Integrity: FAIL` | `1` |
|
|
845
|
+
|
|
846
|
+
A redacted record is reclassified **only** when the snapshot is genuinely content-stripped
|
|
847
|
+
*and* the signature still verifies — a true content mismatch is **never** softened to
|
|
848
|
+
redacted, an invalid signature is always **FAILED**, and a redacted record whose hash still
|
|
849
|
+
recomputes simply stays **VERIFIED**. The `certificateHash` computation, canonicalisation,
|
|
850
|
+
and signature/timestamp logic are **unchanged**; this is purely a classification and
|
|
851
|
+
presentation layer.
|
|
852
|
+
|
|
853
|
+
> **What AUTHENTIC (REDACTED) does and does not prove.** It proves the record carries a
|
|
854
|
+
> valid node signature over the sealed `certificateHash` — i.e. it is authentic. It does
|
|
855
|
+
> **not** recompute the full snapshot, so it cannot prove the *content* behind the kept
|
|
856
|
+
> `inputHash` / `outputHash` was not altered before redaction. Redaction detection is
|
|
857
|
+
> structural (content absent, hashes present), so treat **AUTHENTIC (REDACTED)** as
|
|
858
|
+
> trustworthy only when the record comes from an authenticated node source (e.g. the
|
|
859
|
+
> node's public endpoint over TLS). For the strongest guarantee, obtain the full,
|
|
860
|
+
> un-redacted record and re-run `verify-record` to get **VERIFIED**.
|
|
861
|
+
|
|
862
|
+
**Signing key resolution (by priority).**
|
|
863
|
+
|
|
864
|
+
1. **`--public-key <file>`** — an explicit override (PEM, JWK, NodeKeysDocument, or
|
|
865
|
+
raw base64url).
|
|
866
|
+
2. **Auto-fetch** — otherwise the node's published key set is fetched from
|
|
867
|
+
`https://node.nexart.io/.well-known/nexart-node.json` and the key whose `kid`
|
|
868
|
+
matches the receipt's `attestorKeyId` is used. This is the **only** network call
|
|
869
|
+
in the verify path; it lives entirely in the CLI layer (the auditable core never
|
|
870
|
+
touches the network) and fails gracefully — a network error just falls through to
|
|
871
|
+
the next source. Override the URL with `NEXART_NODE_WELLKNOWN_URL`, or disable the
|
|
872
|
+
fetch entirely with **`--offline`**.
|
|
873
|
+
3. **Embedded key** — a key embedded in the bundle, if any.
|
|
874
|
+
|
|
875
|
+
**Automatic timestamp verification.** An RFC 3161 token at
|
|
876
|
+
`verification.timestamp.token`, `timestamp.token`, or embedded in the bundle is
|
|
877
|
+
verified automatically and offline against an **embedded DigiCert trust store**
|
|
878
|
+
(DigiCert Trusted Root G4 + DigiCert Assured ID Root CA). Override the trusted roots
|
|
879
|
+
with **`--tsa-roots <file>`** (PEM). The ASN.1 stack (`pkijs` / `asn1js`) loads
|
|
880
|
+
**only** when a token is actually present. The timestamp layer is **strictly
|
|
881
|
+
additive**: it never modifies `certificateHash`, is never canonicalised, and **never
|
|
882
|
+
affects the Integrity / Signature result or the exit code** (which is `0` only when
|
|
883
|
+
integrity passes and the signature is not invalid, `1` otherwise) — an
|
|
884
|
+
untrusted-root, imprint-mismatch, or unreadable-`--tsa-roots` outcome is reported as
|
|
885
|
+
a timestamp *status*, never a crash.
|
|
886
|
+
|
|
887
|
+
**Input from a file or stdin.** Pass a file path, or **`-`** to read the record from
|
|
888
|
+
standard input — handy for pipelines such as `curl -s … | nexart verify-record -`.
|
|
889
|
+
|
|
890
|
+
**Report.** The default output is an audit-grade summary rendered in a fixed section
|
|
891
|
+
order — **Signature**, **Integrity**, **Timestamp** (`VERIFIED` / `INVALID (<reason>)` /
|
|
892
|
+
`PRESENT BUT NOT VERIFIED` / `NOT INCLUDED`, plus generation time and authority),
|
|
893
|
+
**Node** (node id, key id, key source, operator), **Identity** (asserted identity, or
|
|
894
|
+
none), and **Execution** (protocol, provider, model, input/output hashes, parameters,
|
|
895
|
+
created-at). A present-but-unverifiable token (a parse failure, or verification not
|
|
896
|
+
attempted) is shown as a caution — `⚠ Timestamp: PRESENT BUT NOT VERIFIED` — never as an
|
|
897
|
+
error, and the timestamp **never** affects `FINAL STATUS`. The report ends with a single
|
|
898
|
+
headline line — **`FINAL STATUS: VERIFIED EVIDENCE RECORD`**, **`FINAL STATUS: AUTHENTIC
|
|
899
|
+
(REDACTED)`**, or **`FINAL STATUS: FAILED`**. For a redacted record the Signature line is
|
|
900
|
+
shown before the Integrity line and a tight context block follows the headline:
|
|
901
|
+
|
|
902
|
+
```
|
|
903
|
+
FINAL STATUS: AUTHENTIC (REDACTED)
|
|
904
|
+
Mode: Public Verification (Redacted)
|
|
905
|
+
Redaction: Applied by certification node (privacy mode)
|
|
906
|
+
! Full integrity verification requires access to original execution data
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
`--json` emits the same information as a structured object (with `status` of `verified` /
|
|
910
|
+
`authentic-redacted` / `failed`, `integrity` including `redacted`, and a `redacted`
|
|
911
|
+
boolean); `--short` prints only `✔ VERIFIED`, `⚠ AUTHENTIC (REDACTED)`, or `✖ FAILED`.
|
|
912
|
+
|
|
913
|
+
| Option | Description |
|
|
914
|
+
| --- | --- |
|
|
915
|
+
| `--public-key <file>` | Override the signing key. Default: auto-fetched from the node `.well-known` key set. |
|
|
916
|
+
| `--offline` | Do not contact the network; use `--public-key` or a bundle-embedded key only. |
|
|
917
|
+
| `--kid <id>` | Key id to select from the key set (default: the receipt's `attestorKeyId`). |
|
|
918
|
+
| `--tsa-roots <file>` | Override the default DigiCert TSA trust roots with custom root certificate(s) (PEM). |
|
|
919
|
+
| `--json` | Emit a machine-readable report. |
|
|
920
|
+
| `--short` | Print only a single verdict line (`✔ VERIFIED` / `⚠ AUTHENTIC (REDACTED)` / `✖ FAILED`). |
|
|
921
|
+
| `--verbose` | Show intermediate verification steps. |
|
|
922
|
+
|
|
923
|
+
> **`verify-bundle` is unchanged.** The redesigned output and automatic key/timestamp
|
|
924
|
+
> resolution apply to `verify-record` only;
|
|
925
|
+
> [`verify-bundle`](#independent-verification-nexart-verify-bundle) keeps its existing
|
|
926
|
+
> behaviour, flags, and byte-for-byte output.
|
|
927
|
+
|
|
928
|
+
---
|
|
929
|
+
|
|
930
|
+
## Verifying from a URL (`nexart verify-url`)
|
|
931
|
+
|
|
932
|
+
`nexart verify-url <url>` is the one-command verification experience: it **fetches** a
|
|
933
|
+
CER record (or a bare bundle) as JSON from a URL and verifies it with the exact same
|
|
934
|
+
automatic key/timestamp resolution and audit-grade renderer as `verify-record`.
|
|
817
935
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
936
|
+
```bash
|
|
937
|
+
# Fetch and fully verify a published record
|
|
938
|
+
nexart verify-url https://node.example/records/abc.json
|
|
939
|
+
|
|
940
|
+
# One-line verdict (✔ VERIFIED / ⚠ AUTHENTIC (REDACTED) / ✖ FAILED)
|
|
941
|
+
nexart verify-url https://node.example/records/abc.json --short
|
|
942
|
+
|
|
943
|
+
# Machine-readable report
|
|
944
|
+
nexart verify-url https://node.example/records/abc.json --json
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
The response is auto-detected as a **full record** (has `verification.receipt`) or a
|
|
948
|
+
**raw bundle**; both route through the same rich verifier (a bare bundle is accepted
|
|
949
|
+
directly). The URL fetch is the record *source* and always happens; **`--offline`**
|
|
950
|
+
here only disables the separate signing-key auto-fetch (so pair it with `--public-key`
|
|
951
|
+
or rely on a bundle-embedded key). A non-2xx response, a network error, or a
|
|
952
|
+
non-JSON body fails cleanly with exit `1`. All other options match `verify-record`
|
|
953
|
+
(`--public-key`, `--kid`, `--tsa-roots`, `--json`, `--short`, `--verbose`).
|
|
954
|
+
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
## Verifying by certificate hash (`nexart verify-hash`)
|
|
958
|
+
|
|
959
|
+
`nexart verify-hash <certificate_hash>` resolves a CER record straight from its
|
|
960
|
+
certificate hash. It constructs the public node lookup URL
|
|
961
|
+
(`https://node.nexart.io/v1/cer/public?certificate_hash=<hash>`) and then verifies it
|
|
962
|
+
through `verify-url`, so the **output is identical** to `verify-url`.
|
|
963
|
+
|
|
964
|
+
```bash
|
|
965
|
+
# Resolve and fully verify a record by certificate hash
|
|
966
|
+
nexart verify-hash sha256:abc123…
|
|
967
|
+
|
|
968
|
+
# One-line verdict (✔ VERIFIED / ⚠ AUTHENTIC (REDACTED) / ✖ FAILED)
|
|
969
|
+
nexart verify-hash sha256:abc123… --short
|
|
970
|
+
|
|
971
|
+
# Machine-readable report
|
|
972
|
+
nexart verify-hash sha256:abc123… --json
|
|
821
973
|
```
|
|
822
974
|
|
|
823
|
-
The
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
975
|
+
The hash **must** include the canonical `sha256:` prefix; a missing prefix fails
|
|
976
|
+
cleanly with exit `1` (before any network call). Network failures behave exactly like
|
|
977
|
+
`verify-url`. Options match `verify-url` (`--public-key`, `--kid`, `--tsa-roots`,
|
|
978
|
+
`--json`, `--short`, `--offline`, `--verbose`); as with `verify-url`, `--offline` only
|
|
979
|
+
disables the signing-key auto-fetch — the record itself is always fetched.
|
|
980
|
+
|
|
981
|
+
> Note: this is distinct from `nexart verify <snapshot>`, which verifies that a
|
|
982
|
+
> snapshot reproduces its expected output.
|
|
830
983
|
|
|
831
984
|
---
|
|
832
985
|
|
package/dist/index.d.ts
CHANGED
|
@@ -220,27 +220,62 @@ export declare function verifyBundleStandaloneCommand(file: string, opts: {
|
|
|
220
220
|
/**
|
|
221
221
|
* nexart verify-record — Verify a full NexArt CER RECORD offline and independently.
|
|
222
222
|
*
|
|
223
|
-
* A "record" is the full verification envelope
|
|
224
|
-
*
|
|
225
|
-
*
|
|
223
|
+
* A "record" is the full verification envelope. v0.16.0 makes this command fully
|
|
224
|
+
* automatic, audit-grade, and self-contained — a third party needs only the record
|
|
225
|
+
* file and this CLI:
|
|
226
|
+
* - FLEXIBLE PARSING: the bundle is taken from `record.bundle` when present, or the
|
|
227
|
+
* record root is treated as the bundle itself. There is no "missing bundle" error;
|
|
228
|
+
* a structurally invalid record simply reports a clean Integrity FAIL.
|
|
226
229
|
* - INTEGRITY + SIGNATURE: the record's `verification.receipt` /
|
|
227
|
-
* `verification.receiptSignature` are merged into a SHALLOW copy of
|
|
228
|
-
* (which never affects the recomputed certificateHash
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
230
|
+
* `verification.receiptSignature` are merged into a SHALLOW copy of the bundle
|
|
231
|
+
* (which never affects the recomputed certificateHash) and handed to
|
|
232
|
+
* verifyBundleStandalone(). The signing public key is, by priority:
|
|
233
|
+
* 1. `--public-key <file>` (explicit override), else
|
|
234
|
+
* 2. auto-fetched from the node's published key set at
|
|
235
|
+
* https://node.nexart.io/.well-known/nexart-node.json, selecting the key
|
|
236
|
+
* whose kid matches the receipt's attestorKeyId (skipped under --offline),
|
|
237
|
+
* else
|
|
238
|
+
* 3. key material embedded in the bundle, if any.
|
|
239
|
+
* - TIMESTAMP (automatic): an RFC 3161 token at `verification.timestamp.token`,
|
|
240
|
+
* `timestamp.token`, or embedded in the bundle is verified automatically and
|
|
241
|
+
* offline against the embedded DigiCert trust store (override with --tsa-roots).
|
|
242
|
+
* The ASN.1 stack loads only when a token is actually present. The timestamp
|
|
243
|
+
* NEVER affects Integrity, Signature, or the exit code.
|
|
244
|
+
*
|
|
245
|
+
* Exit code: 0 when integrity passes and signature is not a failure, 1 otherwise.
|
|
235
246
|
*/
|
|
236
|
-
export
|
|
247
|
+
export interface RecordVerifyOpts {
|
|
237
248
|
publicKey?: string;
|
|
238
249
|
kid?: string;
|
|
239
250
|
json?: boolean;
|
|
240
251
|
verbose?: boolean;
|
|
241
|
-
verifyTimestamp?: boolean;
|
|
242
252
|
tsaRoots?: string;
|
|
243
|
-
|
|
253
|
+
offline?: boolean;
|
|
254
|
+
wellKnownUrl?: string;
|
|
255
|
+
short?: boolean;
|
|
256
|
+
}
|
|
257
|
+
export declare function verifyRecordStandaloneCommand(file: string, opts: RecordVerifyOpts): Promise<StandaloneVerifyResult>;
|
|
258
|
+
/**
|
|
259
|
+
* Verify an already-parsed CER record (or bare bundle) object and render the
|
|
260
|
+
* audit-grade report. This is the shared core behind `verify-record` (file / stdin)
|
|
261
|
+
* and `verify-url` (network), so key resolution, timestamp verification, and the rich
|
|
262
|
+
* renderer behave identically across all input sources.
|
|
263
|
+
*/
|
|
264
|
+
export declare function verifyRecordFromObject(record: unknown, opts: RecordVerifyOpts): Promise<StandaloneVerifyResult>;
|
|
265
|
+
/**
|
|
266
|
+
* nexart verify-url — Fetch a CER record (or bare bundle) from a URL and verify it
|
|
267
|
+
* automatically, using the SAME rich renderer and key/timestamp resolution as
|
|
268
|
+
* `verify-record`. The single network fetch here is the record SOURCE; `--offline`
|
|
269
|
+
* still disables the (separate) signing-key auto-fetch, exactly as for verify-record.
|
|
270
|
+
*/
|
|
271
|
+
export declare function verifyUrlCommand(url: string, opts: RecordVerifyOpts): Promise<StandaloneVerifyResult>;
|
|
272
|
+
/**
|
|
273
|
+
* nexart verify-hash — Resolve a certificate hash to its public CER record URL and verify it.
|
|
274
|
+
* This is a thin convenience wrapper over `verify-url`: it constructs
|
|
275
|
+
* `${PUBLIC_CER_ENDPOINT}?certificate_hash=<hash>` and delegates, so the output is
|
|
276
|
+
* identical to `verify-url`. The hash must carry the canonical `sha256:` prefix.
|
|
277
|
+
*/
|
|
278
|
+
export declare function verifyHashCommand(certificateHash: string, opts: RecordVerifyOpts): Promise<StandaloneVerifyResult>;
|
|
244
279
|
/**
|
|
245
280
|
* Render the human-readable timestamp STATUS line WITHOUT importing the ASN.1 stack.
|
|
246
281
|
* This deliberately mirrors formatTsaLine() in tsaVerify.ts but lives here so the
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAcH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|