Your first verification, in TypeScript
In this tutorial you build @loquitur/verify, the TypeScript verifier, and run it over the test
vectors that ship in its repository. It is a library for Node.js, with no command line. Only the
first steps use the network; the verifications themselves run offline.
You need Git and Node.js 22 or later.
Get the material
Clone the repository at the release tag.
git clone --branch v0.1.0 https://github.com/loquitur-dev/loquitur-verify-ts.gitThe directory you are in now is the one that contains the clone. The last section of this page starts from it again.
Run the verifier over the test vectors
Move into the clone:
cd loquitur-verify-tsnpm ci installs the three runtime dependencies and the build tools; it is the last step of this
section that uses the network.
examples/verify-test-vectors.mjs verifies the bundles in test-vectors/ through the package’s
public entry point and compares each report with the outcome test-vectors/verify.json records
for it. From a clone:
npm cinpm run buildnode examples/verify-test-vectors.mjsIt prints one line per case and exits 0 when every report matches, 1 otherwise:
valid PASS exit 0 local_integrity_checks_passedtampered FAIL exit 1 p1_integrity_failedlink PASS exit 0 local_integrity_and_registry_link_checks_passedauthorization_valid REVIEW exit 2 trust_registry_root_not_pinned AUTHORIZED_ACTIVE_KEY VALID_SIGNATURE NO_PINNED_TRUST_ROOTauthorization_revoked FAIL exit 1 trust_registry_verification_failed ISSUER_KEY_REVOKED null ROOT_SIGNATURE_NOT_EVALUATEDall 5 reports match test-vectors/verify.jsonThe example passes the evaluation time test-vectors/verify.json records for the two
authorization cases, 2026-05-20T00:00:00Z. authorization_valid is REVIEW because no
trust-root document is supplied: the trust registry is authorized at that time and the signature
holds, but nothing authenticates the registry itself. The case names and outcomes are those
test-vectors/verify.json records. test-vectors/ holds local fixtures, not the corpus: the
pinned conformance corpus is the reference (see Conformance below).
Included from loquitur-verify-ts/README.md, “Try it”. MIT OR Apache-2.0.
The five cases cover most of what the Python tutorial shows on the command line: a PASS on the two
checks every run makes, a FAIL on tampered bytes, a PASS with the link check, and the
authorization check twice. authorization_valid stops at REVIEW with the reason
trust_registry_root_not_pinned, because no trust root is given. The test vectors of this
repository carry no trust-root document, so the PASS with a pinned trust root is shown in
the Python tutorial.
Call it from your own code
Go back to the directory that contains the clone, and create the project that will use the
package. npm init -y writes a package.json; the example below is saved as check.mjs, so it
is an ES module whatever the project says.
cd ..mkdir your-projectcd your-projectnpm init -ycd ..You are again in the directory that contains the clone and your-project. Install the package
into your project from the tarball:
Build a tarball from a clone of loquitur-verify-ts and install it in your project. The commands
are the same in PowerShell and in a POSIX shell:
cd loquitur-verify-tsnpm cinpm pack # builds dist/ and writes loquitur-verify-0.1.0.tgzcd ../your-projectnpm install ../loquitur-verify-ts/loquitur-verify-0.1.0.tgzIncluded from loquitur-verify-ts/README.md, “Installation”. MIT OR Apache-2.0.
The commands above end in your-project. Save this there as check.mjs. It verifies a bundle
that ships in the clone, with no registry entry, trust registry or trust root, which are all
optional:
import { verifyBundle } from "@loquitur/verify";
const report = verifyBundle("../loquitur-verify-ts/test-vectors/bundles/valid_bundle", {});console.log(report.status, report.verdict, report.exit_code);node check.mjsIt prints OFFLINE_OK PASS 0. This is the same PASS on the two checks every run makes that the
first case of the previous section shows.
The next block is the full form of the call. In it, registryEntry, trustRegistry and
trustRoots are documents the host has read, and each is optional: leave them out, as above, for
your first run. Replace ./my_bundle with the path of a bundle you have.
import { verifyBundle } from "@loquitur/verify";
// registryEntry, trustRegistry and trustRoots are documents the host has read,// for instance with readDocumentPath, which the package exports.const report = verifyBundle("./my_bundle", { registryEntry, // optional: requests the link check trustRegistry, // optional: requests the authorization check; FAIL without registryEntry trustRoots, // optional: without it, an authorization that holds is REVIEW // The operator's time; without it, authorization is not established (REVIEW). evaluationTime: "2026-05-20T00:00:00Z",});
console.log(report.status, report.verdict, report.exit_code);console.log(report.checks_requested, report.checks_established, report.review_reasons);console.log(report.outcome.reason, report.errors.map((e) => e.code)); // the reason surfaceconsole.log(report.trust_registry); // the three authorization statuses, or nullconsole.log(report.checks); // per-check results, same keys as loquitur-verifyThe documents are values, not paths: the host reads them and passes what it found. That is the
parsed document, or one of four markers this package exports: DOCUMENT_PATH_NAMES_NOTHING when
nothing is found at the path, DOCUMENT_PATH_NAMES_NO_REGULAR_FILE when what is found is a
directory or anything else that is not a regular file, DOCUMENT_READ_FAILED when reading the
file failed, and DOCUMENT_NOT_STRICT_JSON when the bytes are not strict JSON.
report-contract.md section 5.3 gives the first three conditions errors of their own for a
registry entry, different from a document that is not strict JSON, so a host that passed a
document for them would report what loquitur-verify, which reads the path itself, does not. A
file the host failed to read is a host failure for the trust registry and the trust roots too:
trust_registry_read_failed and trust_root_read_failed, HOST_FAILURE, exit 5.
Included from loquitur-verify-ts/README.md, “Quick start”. MIT OR Apache-2.0.
Next
- What a verdict asserts: What a PASS means.
- What the library does not check: the limits page.
- The report it returns: the report contract.
Project site: loquitur.dev
Written for this site; text licensed CC BY 4.0. Included sections keep the licence of their source, named under each.