Limitations
Unpublished text in a file. This is not a legal opinion, not an audit report, not a safety case, and not an independent attestation. It is a list of things Touchstone does not do, written before anyone has to ask.
The project's hardest standard is that a document must not claim more than
the code does. Where a fact has not been verified against this repository,
it is written not_configured, not_deployed, or stated as unknown.
Nothing here asserts that any observed asset is safe, solvent, compliant, or suitable.
Day-to-day operations, including the live testnet registry, are
docs/OPERATIONS.md. The security residuals this file summarises are
sourced from docs/THREAT-MODEL.md. Completion metrics are sourced from
ROADMAP.md.
1. What Touchstone is trusted to say
Touchstone claims one class of thing: that it retrieved exact bytes from a first-party endpoint reached from an allowlisted URL at the recorded retrieval time, that a stated control was evaluated against those bytes deterministically, and that the result was signed and — if a publisher was authorised to — published.
It does not claim the issuer is honest. It does not claim the published figures are accurate. It does not claim an asset is sound.
The retrieval time is Touchstone's own. It is supplied by the caller or
read from the local clock (touchstone/sources.py). It is not an
authenticated publication timestamp from the issuer. No source in the
portfolio provides one.
2. Hackathon completion metrics — what was missed
The targets in ROADMAP.md were not restated downward. Misses are
recorded as misses. Standing in that table is dated 2026-08-16; the
testnet registry landed the next day and does not change the two
missed rows.
| Metric | Target | Actual |
|---|---|---|
| Accepted controls | ≥6 | 9 — met. The current count is derived from the signed approval ledger; 7 candidates remain declined with their reasons retained. |
| Assets documented | 3 | 4 — met. The count is derived from manifests/sources/*.json; fixture availability remains asset-specific and is not implied by the manifest count. |
| Fully autonomous live adapters | ≥2 | 1 — missed. USTB has an unattended production publication record. FOBXX now has an adapter and signed reports on both chains, but sustained scheduled operation is not proven. USDY remains suspended because its attestation route is one unbounded 260 MB archive with no verified bounded route; OUSG remains research and is not promoted. Publication status is rendered from the manifests above and is not used as a synonym for autonomous operation. |
| Live consumer contract gating on state | 1 | 1 — met on testnet, 2026-08-18. AssetGate at 0xAac48DC261B04737FDCB101D5049395121034a83 on X Layer testnet, block 38602126, pointed at the live registry. check() on USTB returns (false, "status not allowed") — the gate refuses the asset, because the latest report is UNVERIFIABLE and the mask admits CONFIRMED only. That refusal is the point rather than a failure: a gate widened until it returned allowed would be a consumer contract accepting an unverified asset. The deployment script now requires a nonzero requiredControlSetRoot; the historical gate was deployed before that pinning guard and remains a legacy testnet artifact. Publisher lineage and freshness are still enforced. On mainnet the reasoning that kept a gate off — an immutable pin against a still-moving control set — expired when the owner signed the approval release: AssetGateV2 at 0x8641CF6d40524AC55aBd0a02601AfBd374EFB059 (2026-08-20, block 68427105) pins policy id, policy root, control-set root and the signed approval-ledger digest, and returned (true, "allowed") for the pinned policy key on deployment day. |
| Production canary epoch | 1 | 1 — met on mainnet, 2026-08-18. USTB sequence 1 on X Layer mainnet (chain 196), registry 0xc9d58e44…D30d — note this address is a superseded registry on chain 1952, so only the chain id identifies it — epoch ustb-2026-08-18, observed 14:04:21Z, state UNVERIFIABLE. It was later restated by sequence 2, a correction; at that point mainnet held two reports and testnet three. The testnet canary of 2026-08-17 (block 38526525) stands as its own record. Both abstained, and for the same reason: a fresh workspace has no capture from ≥24h earlier, so the NAV value controls cannot confirm a row and the engine refuses to assert one. This row previously read "unmet for mainnet, which remains unscheduled". |
| Claims span-cited and hash-bound | 100% | Met for every accepted control. |
The living dossier (PLAN-T9) shipped 2026-08-18 and is live at
https://touchstone.gudman.xyz — 26 routes, an offline verifier, a live
Policy Terminal at /app (the one page with JavaScript: the vendored
ethers bundle, no external hosts), a coverage page, and this
documentation rendered from the repository. This paragraph previously
read "there is no public page", and was being served from the public
page. A project whose whole claim is that it does not assert more than
the evidence supports cannot leave a page telling its reader that page
does not exist.
PLAN-T12 (release-candidate matrix) is not marked done in
docs/PHASE-1-PLAN.md. A CI workflow exists at
.github/workflows/ci.yml. Whether every case that document named has
been added is not claimed here.
3. Parser isolation is process isolation
Normalisation of USTB payloads runs in a spawned worker
(touchstone/normalize/ustb.py, multiprocessing.get_context("spawn"))
with a hard wall-clock timeout (DEFAULT_ISOLATED_TIMEOUT = 2.0
seconds). A worker that never returns is terminated and, if it ignores
that, killed.
That bounds a misbehaving parser and a runaway one. It is not a kernel sandbox. There is no seccomp, container, namespace, or capability restriction. The worker retains the privileges of the service account.
The result crosses back over a multiprocessing connection
(receive.recv()). The parent deserialises whatever arrives. A worker
that an adversary has genuinely compromised can act on the parent across
that channel. Closing this would need a restricted transport carrying
only plain data, plus OS-level confinement. That is residual R-3 in
docs/THREAT-MODEL.md.
There is no PDF or unbounded archive parser in Phase 1. USDY's archive path remains cut. FOBXX's SEC N-MFP3 XML path is bounded by source bytes, XML depth and unsafe-marker checks and has produced signed reports; sustained scheduled production operation remains unproven.
4. Steering by injected evidence is a retained residual
The compiler is given no tool surface. The request body carries only
model and messages (touchstone/compiler.py, HTTPProvider). There
is no shell, network, wallet, or contract capability for injected text
to invoke.
A candidate declaring any approval_state other than proposed is
refused. A fabricated citation is refused. A control redirected to
another adapter is refused.
This constrains impact. It does not prevent steering. A well-formed
injected candidate — correct adapter, exact citation, proposed,
maximum confidence — is accepted as a proposal, because nothing detects
that a human never intended it. That limit is pinned by tests
(tests/test_compiler.py).
Only the approval gate stops it. Since 2026-08-19 the live approval ledger is version 2: every entry, declined entries included, carries the approver's EIP-712 signature over the decision, reason, timestamp, control digest and compilation digest; validation refuses any unsigned entry, recovers the named approver, and binds each signed decision to the exact compiler proposal so a signature cannot be repurposed for a different control. Reports published earlier commit by digest to the version-1 unsigned ledger and their bundles verify unchanged forever — those decisions stay readable but unattributed, because signing them now would manufacture history. There is still no four-eyes requirement: one person proposes, operates and approves. The compiler's confidence value is supplied by the model itself, so it cannot substitute for that gate. The role separation is the remaining part of residual R-9 (and threat T9).
The serving runtime does not call a model. scripts/compile_controls.py
is the only place a model is invoked; it runs at proposal time, on the
operator's machine, and does not approve anything.
5. Value controls abstain without a qualifying earlier capture
A value control on the USTB NAV source observes only a row whose whole
normalised record is identical in a qualifying earlier capture.
"Qualifying" means retrieved at least
CONFIRMATION_INTERVAL_SECONDS = 86_400 earlier
(touchstone/evidence.py). Two captures taken minutes apart, including
either side of midnight, never confirm each other.
run_ustb_epoch resolves that predecessor before appending this
epoch's own capture, so a fetch can never confirm itself
(touchstone/epoch.py). If there is no qualifying predecessor,
_confirmed_nav_row returns nothing, the control evaluates
UNEVALUABLE, and — while evidence is still fresh — the asset state is
UNVERIFIABLE (touchstone/controls.py, touchstone/evaluate.py).
An unseeded workspace therefore reports UNVERIFIABLE. That is the
honest result, not a defect. The canary packet accepts it.
The confirmation window is empirical, not proven (residual R-2). A
row revised and restored between the two captures is indistinguishable
from one never touched. One approved USTB control now declares
minimum_row_age_business_days: ustb-nav-per-share-present, at two
business days. Earlier sets carried none — the compiler did not propose
the field, and approval may change only approval_state and
compilation_sha256, so it could not be added afterwards. The 0.3.0
compiler prompt requests it and the evaluator accepts it only on the NAV
source and never for fresh_within. Note the deterministic gate
validates a non-negative integer, not the constant two: this candidate
declares two because the model proposed two, not because the compiler
could produce nothing else.
Presence controls on the yield and holdings sources do not use this window. They prove only that the issuer returned a named scalar in these hash-bound bytes.
6. Keys, hosts, and what is absent
There is no HSM, no KMS, no passphrase at rest, no multisig, and no
threshold signing. Runtime keys are environment variables on their host.
Anything that can read the process environment can publish. The
publisher key can only append reports; it cannot revoke, rotate, or
rewrite. Recovery is the deployer calling rotatePublisher. The
deployer key itself is a single key. The registry has no owner-rotation
path, so loss or theft of the deployer is unrecoverable. See
docs/KEY-MANAGEMENT.md and residual R-5.
There is no multi-region failover, no leader election, and no second
publisher. The shared VPS runs touchstone-observer@xlayer-mainnet, the
status timer, and — since 2026-08-20 — the enabled publisher unit, which
has produced exactly one unattended mainnet publication day (its first
slot failed closed on a parse timeout; the corrected next slot
published). The observer is recording all three USTB sources every 15
minutes. Detection and recovery timings are proven in a local subprocess
harness; restart and recovery on the production host itself remain
undemonstrated, and two unattended publication days are not yet a sustained measured window.
Compromise detection does not exist. Nothing watches for a publication from an unexpected publisher or a report signed by a retired key.
TLS is trusted without pinning (residual R-7). Evidence integrity in transit rests on the platform certificate store.
The publisher supports fail-closed reads from two independent HTTPS RPC
hosts and refuses disagreement or partial availability. Production
configured TOUCHSTONE_RPC_QUORUM in the publisher unit's environment
when the unit was enabled on 2026-08-20 (verified on the host), and the
Registry v2 publication path refuses to run without it. Residual
R-12 is operationally closed for the publishing path; reads made by
other tools still use single endpoints.
Time is taken from the host clock (residual R-10). The chain rejects
a future observedAt; that check is one-sided and delay-sensitive.
The production observer now retrieves all three USTB sources from the shared VPS every 15 minutes. Cross-region retrieval and a second host are still unverified (residual R-8).
7. Evidence, bundles, and citations
The evidence index is a hash chain. Every append re-verifies the chain and re-hashes referenced objects. That detects modification. An actor with write access who rewrites objects and recomputes the entire chain is not detected. The transparency log is another local JSON-lines file with the same trust properties. The only genuinely external record is what has been published on chain, and that is a root, not the evidence. Residual R-4.
A verification bundle carries the signed report, control records, evidence references, compilation artifacts, the approval ledger, and the published key. It does not carry evidence bytes, registry state, or the transparency log. An offline verifier can confirm the signature, recompute roots, and repeat the compilation-to-control binding. It cannot confirm the report was published, cannot verify the log, and cannot replay normalisation against artifacts it does not hold. Residual R-6.
Byte-span provenance proves the cited bytes occur in the artifact. It does not prove uniqueness, or that they denote the field the adapter consumed. Residual R-1.
8. Retrieval failures, incidents, and scheduling
A source outage is not asset inconsistency. The transition rule
preserves the previous state under SOURCE_ERROR until the evidence
deadline expires. No runtime caller currently produces that event.
A fetch or normalisation failure leaves run_ustb_epoch and the failed
slot opens an incident instead. That residual is not owned by any open
plan item (docs/THREAT-MODEL.md T14, T22).
A missed slot is recorded and never backfilled. Running yesterday's slot today retrieves today's evidence and files it under yesterday.
Alerts are one HTTPS webhook. Delivery is not guaranteed. There is no retry-until-success, no paging escalation, and no failover.
Gas runway is UNKNOWN whenever any operand is missing, and unknown
fails the gate. Top-up is manual.
9. Claims this project does not make
These are not residuals to be closed later. They are outside what Touchstone is.
- Legal.
docs/BRAND-CLEARANCE.mdis a record of searches. It is not a clearance opinion.legal_statusisnot_assessedfor every entry. Superstate API terms of use are recordedunresolvedinmanifests/sources/ustb.json. - Solvency, reserves, collateral. An issuer API is issuer disclosure. USDY's third-party attestation, if it were retrieved, would still cover Ondo USDY LLC only and would still not be an audit. It is not retrieved.
- Compliance, suitability, credit. Touchstone does not rate. It does not recommend. It does not decide listings.
- Safety of an asset or of a consumer protocol.
AssetGatereacts to verification freshness. A permissive policy admits stale state. Gates are live on both chains — the original testnet gate at0xAac48DC261B04737FDCB101D5049395121034a83(which refused USTB for two days and has admitted it since the first CONFIRMED state on 2026-08-19) and policy-pinned gates on testnet and mainnet — but every deployed consumer is this project's own; no third party consumes any of them yet. - Independent attestation, audit, or formal verification of Touchstone itself. The Phase 3 formal threat model, independent contract audit, and external pipeline review are not claimed. This document is not a substitute.
- Production reliability. USTB has a short unattended publication record, not a sustained reliability measurement. Test-harness timings are not a production SLO.
- Endorsement. Nothing here implies OKX, X Layer, Superstate, Ondo, Franklin Templeton, or any issuer endorses this project.
10. Explicitly out of scope for Phase 1
From docs/PHASE-1-PLAN.md and ROADMAP.md Phases 2–5, and not claimed:
legal review; external contract and pipeline audits; a public verification API; paid
rescans; a second autonomous live adapter; an OUSG production route; PAXG;
design partners; formal specifications; multi-publisher quorum;
accreditation; staking or a token; institutional governance; HSM or
multisig custody; multi-region deployment.
The public dossier is live at https://touchstone.gudman.xyz. A public
verification endpoint is still not_deployed — verification is offline, by
running the verifier against a bundle.