noqtis-check(1)
NAME
noqtis-check —
check a sealed capsule folder against its seal
SYNOPSIS
noqtis check |
[-p key.pub]
[-j | --json]
[--anchor der-file ...]
[--only path ...]
[--lineage input-dir ...]
[--keys file ...]
[--card file]
dir | url |
noqtis check |
[-p key.pub]
[-j | --json]
[--anchor der-file ...]
--proof file |
url [dir |
url] |
DESCRIPTION
noqtis check checks the capsule
dir
(noqtis-capsule(5)):
it reads and checks the seal noqtis.sig, checks that
the SHA-256 of noqtis.json is the one in the seal,
verifies the signature over the exact bytes of
noqtis.json, then lists the folder and checks every
listed file (present, a regular file, the size, the SHA-256 and the
SHA3-256) and that no file is present that the manifest does not list.
It prints one row per check: a status (‘ok
or
‘’), the check's
name and a detail, then a summary lineFAIL’
<dir>: OK (<n> checks
passed)<dir>: FAILED (<k> of
<n> checks failed)Without -p, the public key inside the seal
is accepted: the check then shows that the files are those the key holder
sealed, not who the key holder is. The
‘key’ row says so, and a note on
standard error says to pass -p. With
-p, the seal's key must be exactly the given key,
and the seal's algorithm the one the key file names (a
‘key.alg’ row fails otherwise). Each
option that takes a value may be given once.
Files are opened folder by folder from dir,
never through a link, so a folder swapped for a link while the check runs
fails it instead of being followed. A listed file whose size differs is
reported without being read, and no file is read past one byte more than its
listed size. File names and other text from the folder or a TSA are printed
with control characters escaped
(‘\x1b’), so they cannot rewrite the
terminal.
With --anchor, the seal must carry an RFC
3161 timestamp over its signature whose token verifies under one of the
anchors, with the TSA certificate's path valid at the token's genTime; the
‘timestamp’ row then shows the
genTime. Without --anchor, a seal that carries a
timestamp fails its ‘timestamp’ row: a
timestamp that cannot be checked is never passed silently.
Given a url
(‘http://, or ‘’) in place of a folder, the
capsule served there is checked: the URL of the folder that holds
noqtis.sig and noqtis.json
on the server, without a query or fragment. Both are fetched into a new
temporary folder and checked first, against https://
when noqtis was built with its TLS
client’-p and
the anchors when given; only when the seal, its signature and the manifest
check are the listed files fetched (with --only,
only those), each by its path under the URL, percent-encoded, and at most
its listed size, a file over 1 GiB or past the 10000th not at all. The
folder is then checked as any other, so a file the server lacks or changed
fails its row, and everything written to it is removed after. The report
names the URL. Redirects are followed, at most five, and only on the same
server (the same host and port, or
‘http://’ on port 80 to
‘https://’ on 443); never from
‘https://’ to
‘http://’. A redirect to another
server is refused, so a server cannot point the fetch at a host only this
machine reaches, such as localhost or the local network; give the new URL
itself if you trust it. A --proof may also be a
URL.
After the table, for each failed check, a line on standard error says what to do:
noqtis check:
check: what to do-pkey.pub,--pub=key.pub- The public signature key the capsule must be sealed with.
-j,--json- Print one line of JSON instead of the table:
{"capsule":"<dir>","ok":<bool>,"result":"<nqerr text>", "checks":[{"check":"<name>","ok":<bool>,"detail":"<text>"},...]}A failed check also has ‘
fix’, the text of the line on standard error. --anchorder-file- A TSA trust anchor: the root or issuing certificate of a time-stamping
authority, DER-encoded (convert PEM with ‘
openssl x509 -outform DER’). Up to 32; one that verifies the token is enough. --onlypath- Check only this listed file, up to 32 times: a partial check. The seal,
the signature and the whole manifest are still checked; files not named
are neither read nor needed, and files present but not listed are not
looked for. ‘
partial’ rows say so, so a partial pass is never mistaken for a full one. A named path the manifest does not list fails its ‘file’ row. --lineageinput-dir- The folder of a capsule that may be one of the inputs of
dir (a format 2 capsule sealed with
noqtis-seal(1)
--input), up to 32 times. Each input is found among them by its id, the SHA-256 of the bytes its seal signs, and checked in full without a pinned key; its own inputs are looked for in the same folders, to 64 levels. One ‘lineage’ row per input says whether it was found and checks. --prooffile- Check a proof made by
noqtis-prove(1)
instead of a folder: the seal in the proof, its signature over the
manifest's head, and each proven entry's Merkle audit path to the root the
head signs (‘
proof.entry’). With dir, the proven files there are also checked against their entries, and a ‘partial’ row says the capsule's other files are not in the proof.--only,--lineage,--keysand--carddo not apply. --keysfile- A key statement from
noqtis-rotate(1)
or
noqtis-revoke(1),
up to 32 times; it needs
-p. The statements are followed from the-pkey: a seal made with a key it was rotated to passes the ‘key’ row, and one made with a key since rotated away or revoked passes only when a verified timestamp (--anchor) shows it was sealed before then (‘key.time’). One ‘keys’ row per statement; two statements about one key, or a rotation back to an earlier key, trust no key. --cardfile- An identity card from
noqtis-idcard(1).
When it verifies and is for the key that made the seal, the
‘
card’ row names who sealed the folder; when it does not verify, or is for another key, the row fails and says why. The card is signed by its own key, so without-pit names the sealer only if the seal's key is the one they published. A card that cannot be read is a usage error.
Checks
The row names are stable, for scripts:
seal.read- noqtis.sig exists, is a regular file (not a link) and is at most 1 MiB.
seal.parse,seal.canonical- It is a JSON object in canonical form (RFC 8785).
seal.fields- It has exactly the fields of
‘
noqtis-seal-1’, with the right types; ‘manifest_sha256’ is 64 lower-case hex digits. seal.format- ‘
format’ is ‘noqtis-seal-1’. seal.alg- ‘
alg’ names a signature algorithm noqtis knows. seal.context- ‘
context’ is at most 255 bytes. seal.key,seal.signature- ‘
public_key’ and ‘signature’ are base64 of the algorithm's sizes. manifest.read- noqtis.json exists, is a regular file and is at most 512 MiB.
manifest.digest- Its SHA-256 is the seal's
‘
manifest_sha256’. key- Whether the key was pinned with
-p. signature- The signature verifies over the exact bytes of noqtis.json.
timestamp- Present only when the seal has a timestamp or
--anchorwas given. folder- The folder could be listed within the limits.
file- One row per listed file.
manifest.parse,manifest.canonical,manifest.fields- The manifest is canonical JSON with exactly the fields of
‘
noqtis-capsule-1’. manifest.format,manifest.created,manifest.order- ‘
format’ is ‘noqtis-capsule-1’ (or ‘noqtis-capsule-2’ under a ‘noqtis-seal-2’ seal), ‘created’ is an RFC 3339 UTC time, and the files are sorted by path with no repeats. manifest.merkle- Format 2: the Merkle tree over the file entries has the root and leaf count the signed head gives.
lineage- With
--lineage: one row per input of the capsule. keys- With
--keys: one row per statement, and its signatures. card- With
--card: the card verifies and is for the seal's key, and who it names. key.time- With
--keys, when the seal's key was rotated away or revoked: the verified timestamp's genTime is before then. partial- With
--onlyor--proof: what the check left out; never a failure. proof.parse,proof.canonical,proof.fields,proof.entry- With
--proof: the proof is canonical JSON with exactly the fields of ‘noqtis-proof-1’, and each entry's audit path leads to the signed root. path- One row per refused, non-normal, duplicate or too long path, and per two paths that differ only in case.
extra- One row per regular file present but not listed.
unsafe- One row per link, reparse point, special file or refused name in the folder.
ENVIRONMENT
SSL_CERT_FILE- For an ‘
https://’ url: a PEM bundle of the roots to trust, in place of the system's. NOQTIS_CT_LOGS- For an ‘
https://’ url: a Certificate Transparency log list in the form of Chrome's log_list.json (version 3), such as https://www.gstatic.com/ct/log_list/v3/log_list.json. When it is set, the server's certificate must carry SCTs (embedded, or sent in TLS) that verify and come from logs of at least two operators, usable when they signed; otherwise the fetch fails and says why. Unset or empty, nothing is checked: a list goes stale, so keep it current.
EXIT STATUS
- 0
- Every check passed.
- 1
- A check failed (the files changed, the seal or manifest is damaged or not signed by key.pub).
- 2
- A usage error, dir does not exist, a key that is not a public signature key, an anchor that cannot be read or is not DER, or a library error such as running out of memory.
EXAMPLES
$ noqtis check -p me.pub results
check detail
ok seal.read noqtis.sig
...
ok signature ML-DSA-65 signature over noqtis.json is valid
ok folder listed
ok file data/in.csv
ok file report.txt
...
ok manifest.order files sorted by path, no repeats
results: OK (22 checks passed)
After a file was changed and one added:
$ noqtis check -p me.pub results; echo $? ... FAIL file report.txt: size differs ... FAIL extra new.txt: not in the manifest results: FAILED (2 of 23 checks failed) noqtis check: file: a sealed file changed after sealing: restore it from the original, or seal the folder again if the change was intended noqtis check: extra: a file was added after sealing: remove it, or seal the folder again to include it 1
A timestamped capsule, against the TSA's root certificate:
$ noqtis check -p me.pub --anchor digicert.der results ... ok timestamp RFC 3161 token over the signature verifies under a TSA anchor; genTime 2026-10-29T09:34:03Z ... results: OK (23 checks passed)
A capsule published on a web server, without downloading it by hand:
$ noqtis check -p me.pub https://example.org/data/results/
Only one file of a format 2 capsule, and its inputs:
$ noqtis check -p me.pub --only data/in.csv results $ noqtis check -p me.pub --lineage raw results
In CI, keeping only the failed rows:
$ noqtis check --json -p me.pub results |
jq -r '.checks[] | select(.ok | not) | .detail'
report.txt: size differs
new.txt: not in the manifest
SEE ALSO
noqtis(1), noqtis-idcard(1), noqtis-prove(1), noqtis-revoke(1), noqtis-rotate(1), noqtis-seal(1), nqchk(3), nqchkt(3), nqtsput(3), noqtis-capsule(5)