Skip to content
Preview (Alpha), release 0.1. No independent cryptographic review of the protocol or its reference verifiers has been done.

Limits

This page collects, from each repository of the release, what it says it does not do. Read it before relying on a verdict.

What this preview does not provide

Protocol release 0.1 is a specification preview (Alpha). Its public API is not stable and may change before 1.0.0, and no independent cryptographic review of the protocol or of its reference verifiers has been done.

This repository does not provide:

  • a hosted registry or any other service;
  • a service runtime;
  • package source code;
  • a stable public API or SDK;
  • production signing or key-management operations;
  • production trust-registry governance;
  • the result of an external security audit;
  • regulatory approval;
  • conclusions about legal ownership;
  • correctness of any valuation;
  • finality guarantees of any external network;
  • service-level commitments.

What release 0.1 does not claim is stated in introduction.md section 12, “Non-claims”, and IMPLEMENTATIONS.md says which implementations known to the maintainer this copy lists. The requirements no conformance vector exercises, each with the reason or its known limit, are listed under “Rules without a vector” in traceability/README.md.

The schema package and the two verifiers live in their own repositories, loquitur-schemas, loquitur-verify-py and loquitur-verify-ts, and each states its own status and limits there. The conformance corpus comes from loquitur-conformance, which is not published; it reaches readers as the digest-pinned archive in this repository. This specification defines no HTTP API and no hosted service, so there is no service status to report.

Known limitations of the protocol in release 0.1

Each of these is stated where its rule is, with what an operator can do about it.

  • Pinned roots have no scope. A trust root names no issuer, operator or trust registry, so every pinned root can authorize a key for every issuer. Pinning the roots of two registry operators in one trust-root document merges their authority. Operators should pin together only roots they trust for every issuer, and keep separate trust-root documents otherwise. See trust-root.md section 1.
  • The envelope is not authenticated. Nothing the issuer signs commits to envelope_env1.json, so its subject, anchor and provenance members and any embedded registry entry can be rewritten without a key, and the run still passes. A relying party must not treat them as authorized by the issuer. See bundle-objects.md section 4.
  • The subject reference opening is not checked. A bundle may carry subject_ref_opening.json, but no check recomputes subject_ref_hash from it, so a run passes whether or not it opens the entry’s commitment. A relying party that relies on the subject it names should recompute the value itself. See registry-entry.md section 4.
  • A report does not name its trust material. No normative report member identifies the trust registry, the root that authenticated it or the trust-root document a run used. Operators should keep a record of that material with each report. See report-contract.md section 2.

Included from loquitur-protocol/docs/preview-limitations.md. MIT OR Apache-2.0.

What release 0.1 does not claim

This section is informative.

For release 0.1:

  • no independent security or cryptographic review of the specification or of either implementation has been done, and no audit of any kind; the maintainer’s own testing, carried out with the help of AI tools, the conformance corpus included, is internal verification and not an audit;
  • conformance to this specification, or to the conformance corpus, is not compliance with any law, regulation or standard, and no certification of any kind exists;
  • the release is a preview and is not presented as fit for production use or for decisions of real value;
  • the specification names no adopter, deployment, customer or partner, and claims none;
  • no registry, log, anchoring or other service is operated under this specification;
  • a verdict says what section 2 of threat-model.md says it asserts and nothing more: it does not say that the evidence is true, that a value is right, or that an issuer is who it claims to be;
  • the verdict path contains no AI: no machine-learning model or other learned component takes part in any outcome, which follows from the rules of this specification applied to the inputs.

Included from loquitur-protocol/docs/specification/introduction.md, section 12. CC BY 4.0.

What the Python verifier does not verify

Loquitur governs the evidence behind a valuation; it does not compute valuations.

The verifier does not verify:

  • trust registry membership outside the supplied local trust registry;
  • issuer signatures without a supplied local registry_entry and trust_registry;
  • issuer authorization without a supplied local registry_entry and trust_registry;
  • trust-registry root authenticity without a root you supply: root-signature verification is implemented, PINNED_TRUST_ROOTS ships empty and empty means refuse, so a trust registry is authenticated only against a trust-root document passed with --trust-root;
  • registry inclusion or checkpoint proofs (the transparency check is reserved in verification-checks.md);
  • chain anchors or their finality (the anchoring check is reserved; see anchor-commitment.md);
  • revocation or supersession beyond the fields of the supplied trust registry;
  • anything online: it queries no registry and no service;
  • valuation correctness;
  • KYC, AML or compliance status;
  • whether evidence is economically sufficient for an investment decision.

Each report lists, in outcome.not_implemented_capabilities, the capabilities its run did not have; threat-model.md section 9 names the risk each one leaves with the relying party.

Included from loquitur-verify-py/README.md, “What it does not verify”. MIT OR Apache-2.0.

What the TypeScript verifier does not verify

This verifier does not verify anchor finality or registry inclusion, does not verify valuation correctness or regulatory compliance, and does not verify an issuer’s real-world identity. It verifies the trust registry’s root signature only against roots the caller supplies, and it establishes nothing about who holds a root key: obtaining the trust-root document through a channel the issuer does not control is the operator’s task (trust-root.md section 7). A PASS is a statement about evidence integrity and issuer authorization on local material. It is a precondition for relying on the underlying verdict, not a verdict on the underlying valuation.

Included from loquitur-verify-ts/README.md, “What it does not verify”. MIT OR Apache-2.0.

Here integrity means the internal consistency of the supplied material: the digests recompute to the values the material declares. It does not mean that the material is unchanged since issuance, and it does not mean that the evidence is true.

Scope and limits of the TypeScript verifier

  • Bundle input is a directory. ZIP loading is the host application’s responsibility, which keeps this library free of archive dependencies.
  • Node-oriented I/O (node:fs) is confined to verifyBundle and readDocumentPath. The I/O-free entry, @loquitur/verify/browser (src/index.browser.ts), exposes verifyBundleFiles over a reader, such as bundleReaderFromMap. npm run build:browser also bundles that entry into one script, dist/loquitur-verifier.browser.js, which defines the global LoquiturVerifier; that script is not part of the package.
  • No bundle member, registry entry or trust registry makes verifyBundle or verifyBundleFiles throw: JSON that is not valid UTF-8, repeats a member name, nests deeper than 64 levels, or carries a non-finite number, an integer beyond 2^53-1 or a lone surrogate is a failed check, whether or not the value is hashed. The operator’s own inputs are the exception: a trust-root document or vocabulary document that is not strict JSON is refused with TrustRootError or VocabularyError before any evidence is read, as the trustRoots and vocabularies items say.
  • Containment is checked on every path component immediately before each read and links are refused, but a directory that another process modifies during verification can still change between the check and the read; verify bundles from a location no other process writes to. A hard link is indistinguishable from a regular file and is verified as one.
  • Both verifiers refuse every reparse point in a directory bundle, whatever its tag: the Node reader reads it from the type the host’s listing gives each entry. Placeholders of a synchronized folder are reparse points; do not verify bundles from one.
  • Alternate data streams attached to a member are not bundle members and are neither inventoried nor verified.
  • A file in the bundle that manifest.json does not declare (other than the manifest, its sidecar, envelope_env1.json, its sidecar and the receipt), and whose name is a portable member name, makes the result REVIEW (bundle_extra_files). A member under attestations/, or named document_index.json or claims_openings.json, is a name this bundle version reserves: REVIEW (bundle_reserved_member), declared or not. verifyBundleFiles learns what the bundle holds from the reader’s listMembers(); the filesystem reader and bundleReaderFromMap implement it, and a host’s own reader that does not (or returns null) cannot show that nothing is undeclared, so its result is capped at REVIEW.
  • Every member’s path from the root must be a portable member name (bundle-objects.md section 2): each segment holds only A-Z, a-z, 0-9, ., _ and -, is not . or .., does not end with . or begin with -, and is not a Windows device name such as CON or nul.txt, and the whole name is at most 255 bytes. A name outside the rule (a file named a b.json, a lone surrogate, bytes that are not UTF-8), or two names of one directory that conflict under ASCII case folding, whether the filesystem reader lists it or a host’s reader does, cannot be declared by any manifest: it is never opened, and the run fails with bundle_member_name_invalid. A link in the same bundle is not reported beside it, because an inventory that fails takes the place of unsafe_bundle_member. Every other error is still reported: a refused name beside a manifest.json that is not JSON gives bundle_member_name_invalid and manifest_verification_failed. A host that fails to list a directory of the bundle is bundle_inventory_failed, a host failure.
  • bundleReaderFromMap takes each member as a string or a Uint8Array. A host that passes already-decoded strings to bundleReaderFromMap has already replaced invalid UTF-8; pass Uint8Array values to get the same refusal as the Node reader. Build the map with JSON.parse or Object.defineProperty; assigning files[name] = value cannot create a member named __proto__.
  • trustRoots takes the trust-root document of trust-root.md section 3 ({"object_type": "trust_root_document", "schema_version": "v1", "roots": [...]}, each root with its key_type), never roots read from the registry it authenticates. The whole document is validated when it is read, every root of it whether or not the registry names it, before any evidence is read: a document that breaks section 3 anywhere throws TrustRootError and returns no report, from verifyBundle, verifyBundleFiles and evaluateTrustRegistry alike, and whatever the other inputs. A document of a well-formed later version is not malformed: the run is UNSUPPORTED_VERSION, and evaluateTrustRegistry, which returns no run, throws TrustRootError. With well-formed roots, an authorization run that holds at the operator’s evaluation time is PASS when the registry authenticates (ROOT_SIGNATURE_VALID) and FAIL otherwise; without roots it is REVIEW (NO_PINNED_TRUST_ROOT). A root window that does not hold the evaluation time is REVIEW (trust_root_outside_validity_window). A matched root whose secp256k1 point another pinned root also carries, in any SEC1 encoding and whatever its status or key type, is AMBIGUOUS_ROOT_KEY, as two roots under one key_id are. A document given without a trust registry is validated and then unused. parseTrustRoots, authenticateTrustRegistry and trustRegistrySigningMessageBytes are exported for hosts that read or sign registries.
  • evaluationTime is the operator’s evaluation time of trust-registry-authorization.md section 2, a calendar instant of the form YYYY-MM-DDTHH:MM:SSZ: the registry’s and the key’s windows and root authentication are judged at it, and at nothing else. The entry’s own effective_at, issued_at and anchored_at are never read as a time. An empty value or any other form throws EvaluationTimeError before any evidence is read. Without it, the trust registry is still evaluated with the rows that do not read the time, a revoked or suspended key still fails, and a run that would otherwise hold is REVIEW (evaluation_time_not_provided). A registry or key window that does not hold the time is REVIEW (trust_registry_not_current, issuer_key_outside_validity_window), not FAIL. The report states the time in evaluation_time.
  • vocabularies takes, by kind (asset_type, workflow, anchoring_method), a registered vocabulary document of verification-vocabularies.md section 6 that the operator supplies; a kind not given is judged against the version built into this package (REGISTERED_VOCABULARIES, the protocol’s files as they are), and two documents of one kind are never merged. A document that is not a strict JSON object, breaks section 6 or registers another kind throws VocabularyError before any evidence is read. A receipt’s asset_type, or an entry’s asset_type or workflow, that is an identifier the vocabulary held does not register makes the run REVIEW (vocabulary_value_unknown), and withholds no check; an unknown anchor.method changes nothing. The report records each document held in vocabularies: its version, digest and source. This package takes a supplied document already parsed, not as the file’s bytes, so the digest it records for one is the SHA-256 of the document’s RFC 8785 canonical bytes. loquitur-verify records the SHA-256 of the file it reads, so for a file that is not in canonical form the two verifiers record different digests in this informative member.
  • Verdict narrative rendering (explanations for several audiences) lives in the Python loquitur-verify[narrative] extra and is not replicated here.

Included from loquitur-verify-ts/README.md, “Scope and limits”. MIT OR Apache-2.0.

What an operator must do that the tool cannot

The verifier answers questions. These are decisions, and no flag makes them:

  1. Obtain a trust root out of band and pin it. Without --trust-root, the trust registry proves that some key signed the entry and that the registry the verifier was given vouches for it. Whoever supplied that registry can make both true. Pinning is the difference between section 5 case A and case B. Obtaining the root through a channel the issuer does not control is the operator’s problem entirely.
  2. Supply the evaluation time. --evaluation-time is the only clock that judges a key, a registry and a root. Without it, authorization is not established and the run is REVIEW.
  3. Refresh trust material. A registry copy is a snapshot. Decide how old is too old, and enforce it; the tool checks the window written in the file, not whether the file is current.
  4. Treat REVIEW as a stop, or name what you accept. --accept-review converts an OFFLINE_REVIEW run to exit 0 only when every review reason of the run is in the list the operator gives (report-contract.md §4). Accepting registry_entry_verdict_requires_review does not accept trust_registry_root_not_pinned: each reason accepted is a decision the operator takes on, and a gate that accepts the unpinned root has given up section 5.
  5. Read more than the exit code. At minimum outcome.reason, checks_requested, checks_established, review_reasons, evaluation_time and, when the trust registry was evaluated, trust_registry.authorization_status, trust_registry.signature_status and trust_registry.trust_registry_root_signature_status. These are normative members (report-contract.md §2), so every conforming verifier reports them.
  6. Bind issuer identifiers to real entities. Out of scope for the protocol and unavoidable for the relying party.
  7. Decide what a REVIEW from an undeclared bundle member means. The verifier reports the names in manifest.extra_files; whether the content is material cannot be decided by hashing (bundle-objects.md §1).
  8. Handle disclosed evidence. Retention, access control and destruction of a bundle after verification are entirely the operator’s.
  9. Keep the verifier honest about itself. Verify the package you run, from a source you trust; a verifier is a trusted computing base, and this document assumes the one executing is the one described.

Included from loquitur-protocol/docs/specification/threat-model.md, section 10. CC BY 4.0.

Platforms

Python verifier

  • Python: 3.11 or newer (requires-python = ">=3.11"). Continuous integration runs the suite on Linux with Python 3.11 to 3.14.
  • Platforms: the verifier is pure Python. Continuous integration runs its suite on Linux; the suite also passes on Windows locally. macOS is not tested.
  • Dependencies: rfc8785 and cryptography; the narrative extra adds loquitur-schemas>=0.1.0,<0.2.0.
  • Protocol: release 0.1 of the Loquitur protocol, as pinned by the conformance corpus above.

Included from loquitur-verify-py/README.md, “Compatibility”. MIT OR Apache-2.0.

TypeScript verifier

  • Node.js 22 or later (engines in package.json). CI runs the suite on Node 22 and 24, on Linux; the suite also passes on Windows locally. macOS is not tested. JSON is decoded by the package’s own tokenizer, never by the host’s JSON.parse, so a value the verifier hashes depends on the document’s text alone.
  • An ES module consumer: the package is ESM only ("type": "module"), with no CommonJS build and no require condition in its exports map, so import is the supported way to load it, and it ships its own type declarations. The types condition comes first in each entry of the map, as TypeScript resolves them in order.
  • A library only: version 0.1 has no command line and the package declares no bin. The command-line contract of report-contract.md (exit code 64 for a usage error) belongs to the command of the Python package, loquitur-verify; this library raises its own faults and has no usage error.
  • Runtime dependencies: @noble/curves, @noble/hashes and canonicalize, each held to the patch line CI tests (~ in package.json). npm ci in a clone installs exactly the versions package-lock.json records; a tarball carries no lockfile, so installing one resolves each dependency within its patch line.

Included from loquitur-verify-ts/README.md, “Requirements”. MIT OR Apache-2.0.

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.