API Reference
Two surfaces:
- SDK — the
@hyperdag/trustshellnpm package, for code integrations. - Public REST API — keyless endpoints on
https://repid-engine-production.up.railway.app, for dashboards / agent cards / verification scripts.
All times below are illustrative — live counts increment continuously. Re-curl any endpoint to see current numbers. Unfamiliar terms? See the glossary.
Table of contents
- SDK
new TrustShell(config)TrustShell.init(config?)score(response, options?)verifyOutput(output, options?)getRepID(agentId)presentProof(agentId, opts?)getLeaderboard(board)subscribe(event, handler)- Other methods
- CLI
- Public REST API
GET /api/v1/statusGET /api/v1/receipts/heroGET /api/v1/hal/statsGET /api/v1/repid/:agentIdGET /api/v1/llm-trustGET /.well-known/agent.jsonGET /agent.jsonGET /api/v1/firecrawl/stats- Errors
SDK
Default engine: https://repid-engine-production.up.railway.app (override with apiUrl in the constructor, or the TRUSTSHELL_API_URL environment variable).
new TrustShell(config)
import { TrustShell } from '@hyperdag/trustshell';
const shell = new TrustShell({
apiKey?: string, // optional — score/verify/getRepID/presentProof are keyless
apiUrl?: string, // defaults to the production engine
timeout?: number, // ms, HAL requests (default 30000)
healthCheck?: boolean, // init() only: false skips the /health probe
healthTimeoutMs?: number, // init() only: /health deadline (default 3000)
readAllowance?: Function, // TrustKeys allowance reader for getAllowance (fail-closed when unset)
});
That is the whole config surface (TrustShellConfig). The read paths are keyless, and so are service discovery (listServices, getService) and register (POST /api/v1/agents/register). A key is needed to buy: executeA2A and the contract reads after it (getContractStatus, pollUntilSettled).
TrustShell.init(config?) → Promise<{ client, health }>
Constructs a client and probes engine health in one call, so a caller can degrade deliberately instead of discovering the engine is down on its first real request.
const { client, health } = await TrustShell.init();
if (!health.ok) { /* health.status / health.error */ }
score(response, options?) → Promise<ScoreResult>
Run text through the live HAL cross-provider quorum.
response: string— the text to evaluate.options?: ScoreOptions—{ prompt?, provider?, model? }.
Returns ScoreResult: verdict ('PASS' | 'FLAG' | 'VETO' | 'NOT_CHECKED'; NOT_CHECKED means HAL did not decide, and is never a pass), trustScore (0–100, 0 when NOT_CHECKED), halScore (0–1), the five signals (harmProbability, epistemicUncertainty, evidenceQuality, scopeAppropriateness, certaintyAtClaim), decisionReason, and evidence[] — one "provider:VERDICT (note)" line per provider, which is the why behind the verdict. On the quorum path it also carries mode, providersUsed, familiesUsed, agreement, and the SBFA fields (belief, ignoranceMass, confidence, tierDistribution, glassBox).
verifyOutput(output, options?) → Promise<VerifyOutputResult>
Gate-shaped wrapper over score. Returns { ok, verdict, trustScore, halScore, soft, ... } where ok is true for PASS and soft FLAG, false for VETO — the shape you want in an if around an agent action.
getRepID(agentId) → Promise<RepIDResult>
Live RepID and tier for an agent. Keyless.
await shell.getRepID('trinity-shofet');
// { agentId: 'trinity-shofet', repid: 2070, tier: 'ESTABLISHED', ... }
presentProof(agentId, opts?) → Promise<ProofPresentation>
Fetch an agent's ZK RepID range proof. opts: { verify?, tier?, allowExperimentalTiers? }.
Only the postcard tier has a live production endpoint. Other tiers exist in the prover but are not exposed as an API; requesting one without allowExperimentalTiers flags rather than fabricates — there is no stub on this path. With { verify: true } the proof is checked client-side by the bundled WASM verifier.
getLeaderboard(board) → Promise<Leaderboard>
board: 'agents' | 'models'. Overloaded — returns AgentLeaderboard or ModelLeaderboard.
subscribe(event, handler) → () => void
const off = shell.subscribe('verdict', (payload) => { /* ... */ });
event is 'verdict' | 'proof'. Returns an unsubscribe function.
TrustShelldoes not extendEventEmitterand has no.on()method — usesubscribe.
Other methods
Also available, same client: verify(agentId), getFactCheckCount(), getRepIDStake(agentId), audit(table?), executeA2A(params), register(params), registerHuman(opts?), listServices(options?), getService(serviceId), getContractStatus(contractId), pollUntilSettled(...).
CLI
npm install -g @hyperdag/trustshell
The bins package.json ships:
| Bin | What it is |
|---|---|
trustshell, hal | the CLI — two names, same program |
trustshell-mcp | the MCP stdio server |
verify, repid, proof, status, remember, recall, redact | deprecated bare-name shims, each forwarding to trustshell <command>. They print a deprecation note on a terminal and go away in 2.0 |
Environment: REPID_API_KEY (optional — verify/repid/proof are keyless), TRUSTSHELL_API_URL (override the backend origin). The full list is in getting-started.md §6. The CLI reads no config file; init writes .trustshell/profile.md, which only report reads.
Exit codes for verify / evaluate — the reason this works as a CI gate:
| code | meaning |
|---|---|
0 | HAL PASS, or soft FLAG — safe to proceed |
1 | HAL VETO — fail the build |
2 | HAL did not decide (NOT_CHECKED) — never a pass. Also usage / bad arguments |
3 | runtime error (network / backend / timeout) |
4 | ASK — only when TRUSTSHELL_LAYA is set: a person has to answer. HAL was not called |
Other commands keep their own codes, listed under each one below.
<a id="cli-verify"></a>
trustshell verify "<claim>"
Runs the claim through the live HAL cross-provider quorum. Options: --json.
$ trustshell verify "The capital of France is Paris."
✓ PASS trust 100/100
PASS — hal_score 0 via fact-check (full quorum)
evidence:
- cerebras:TRUE (Paris is the capital of France.)
- groq:TRUE (Paris is the capital of France.)
- zai:UNCERTAIN (NOT_CHECKED: late after 2-family agreement)
Recorded 2026-10-05 (the README's sample). Which providers answer, and how many, changes with the live quorum.
Use it as a gate:
trustshell verify "$(cat CHANGELOG_CLAIM.txt)" || exit 1
trustshell evaluate "<claim>"
Alias of trustshell verify. Same HAL quorum, same exits. Exists so every public surface names evaluate().
<a id="cli-repid"></a>
trustshell repid <agentIdOrSlug>
Print an agent's live RepID and tier. Keyless. Options: --json.
$ trustshell repid trinity-shofet
trinity-shofet
RepID 2236 (ESTABLISHED)
ERC-8004 ID #5863 (on-chain: the engine's ownerOf read found an owner for this token, and the recorded registry is the canonical IdentityRegistry 0x8004A818BFB912233c491871b3d84c89A494BD9e)
Recorded 2026-10-14 with 1.6.1. That is not the current score: the score moves, so gate on tier.
<a id="cli-proof"></a>
trustshell proof <agentIdOrSlug> [--verify]
Fetch the agent's ZK RepID range proof (postcard tier). With --verify, additionally verify it client-side with the bundled WASM verifier. Options: --json.
$ trustshell proof trinity-shofet --verify
trinity-shofet
tier postcard
scheme plonky3_range_check
createdAt 2026-10-10T12:01:55.555406+00:00
proof 14232 base64 chars
statement repid_score=2227 threshold=999 tier=ESTABLISHED
verified ✓ (client-side, 0.2.0)
Recorded 2026-10-14 with 1.6.1. The score in the statement is the one the proof was made at (createdAt), not the current score.
trustshell badge <agentIdOrSlug> [--markdown]
Fetch the agent's proof, verify it client-side, and emit a portable, self-contained SVG badge reading RepID ≥ <threshold> ✓ ZK-verified. Options: --markdown (a copy-pasteable data-URI snippet instead of raw SVG), --json.
The badge goes green only on a true local verification — it exits 3 in any other state rather than rendering an unverified badge. It never prints the score itself; the proof's own statement still carries the score as a public input, so the badge is not a privacy claim.
trustshell check <runUrl>
Ask the public GitHub API what it can confirm about an Actions run, and print what that does not prove. No account, no key, no TrustShell backend — it talks to api.github.com and nothing else. GITHUB_TOKEN is optional and only raises the rate limit; if one is set and rejected, the command retries anonymously rather than failing. Options: --json.
| Verdict | Meaning | Exit |
|---|---|---|
COMPLETE | the run succeeded and every job passed | 0 |
INCONSISTENT | GitHub calls the run a success, but a job did not pass | 1 |
FAILED | the run's own conclusion was not success | 1 |
INCONCLUSIVE | the run has not finished, or the jobs could not be fully read — NOT CHECKED, not "fine" | 3 |
A queued or in-progress run is INCONCLUSIVE, never FAILED: an unfinished build has not failed, it has not answered.
trustshell check "<sentence>"
When the operand is not a URL, check labels one sentence pass, veto or not-checked by asking the same public endpoint the TrustShell Chrome extension asks (POST <backend>/api/v1/classify, backend = TRUSTSHELL_API_URL or the live HyperDAG backend). The MCP server's check_claim tool calls the same function, so a terminal, an agent and the extension get the same label for the same sentence. Any operand with a URL scheme (or a bare github.com/...) still goes to the GitHub run check above, unchanged.
It prints the label on its own line, then one line of explanation. When the endpoint says what produced the label, the next line names it: who decided (for example Groq and Cerebras both said false.), Decided by exact calculation. No model was asked., Decided by approximate calculation, not exact. No model was asked. (an equation the engine could only compare as decimals, such as a fractional power), No checker was asked. or No answer in time. An answer naming a path this version does not know is not-checked, never a pass. If the endpoint asked a clarifying question, it follows on its own line, and a last line says so when text that looked like a key or personal data was removed before sending.
--json prints the result object: label and latency_ms, plus by, voters, deciders, votes and question when the endpoint sent them, reason when the label was decided locally, and scrubbed: true when something was removed. 1.6.0 drops deciders and votes; 1.6.1 keeps both.
The endpoint itself (POST /api/v1/classify, public) answers {label, latency_ms, by}, plus voters, deciders and votes (each decider's model family and verdict) when by is votes, and question only on a not-checked from the votes when the server has questions turned on (CLASSIFY_QUESTIONS=on; off by default). By default each visitor gets 100 checks per UTC day (CLASSIFY_DAILY_LIMIT); past that the endpoint answers 429, which the CLI reports as not-checked.
| Label | Exit |
|---|---|
pass | 0 |
veto | 1 |
not-checked | 2 — never a pass |
error (nothing was sent: empty sentence, invalid TRUSTSHELL_API_URL) | 3 |
A timeout (6 s), a network failure, a non-200 or a body outside the contract is not-checked. Known secret shapes are stripped from the sentence before it is sent.
trustshell receipt <receiptId>
Since 1.7.0. On 1.6.1 and older this command exits
2with
unknown command: receipt, and the SDK does not exportkernelReceipt().
Reads a policy-kernel receipt back from the engine (GET /api/v1/kernel/receipt/:id, which needs REPID_API_KEY; a key bound to one agent reads only that agent's receipts). It recomputes the receipt's keccak256 hash locally and prints the receipt only if the hash matches. Then it prints the receipt's six claims one by one (Integrity, Authenticity, Observation, Correctness, Authorization and Containment), each with its own status and what it does not establish, followed by what the receipt as a whole does not establish. There is no single verdict line on purpose.
The six claims are shown only when the engine's envelope names this exact receipt (its id and its hash). Otherwise the receipt still prints, with six claims not shown: <why>. --json prints the kernelReceipt() result from the SDK (src/lib/kernel-client.ts).
| Outcome | Exit |
|---|---|
| read, and the hash matches | 0 |
| the receipt does not match its own hash. Treat it as tampered | 1 |
| not read: no key (401), another agent's key (403), no such receipt (404), network, timeout | 3 |
trustshell inspect [<path>] [--from <format>]
Verify an append-only tool-call log and report what it can and cannot establish. Reads a local file and computes hashes; it opens no socket. Defaults to .trustshell/session.jsonl. Options: --from <format> (read a foreign log through an adapter — claude-code today), --json.
| Verdict | Meaning | Exit |
|---|---|---|
INTACT | every line's prev matches the previous line's hash — nothing removed or edited | 0 |
BROKEN | the chain does not verify — lines were altered or dropped | 1 |
UNCHAINED | a foreign log with no chain — NOT CHECKED, not "fine" | 3 |
NO_LOG | no log, or an empty one — NOT CHECKED. An empty chain is vacuously unbroken, which is not evidence | 3 |
What INTACT does not prove. The chain has no trusted anchor: its hashes are unkeyed and reproducible by anyone who can write the file. INTACT detects edits and deletions that left the later hashes untouched, and nothing more — it cannot rule out a log that was fabricated or rewritten wholesale. Every card says so in its own limits section.
An adapter can only ever report UNCHAINED. A log this tool did not chain proves nothing about its own integrity, whatever it says happened.
trustshell init [<dir>] [--force] [--pai]
Create .trustshell/ and write a blank profile.md. No network, no account, nothing collected — not your git config, hostname, username or email. It never overwrites an existing profile without --force. Options: --force, --json.
--pai runs scripts/init-pai.mjs (the PAI FACE: interview → live register). That path does reach the HyperDAG backend. Equivalent from a clone: node scripts/init-pai.mjs --name <n>. Options with --pai: --name <n>, --answers <job|cost|brain>, --force. If the script is not in this install, the CLI prints that path and exits 2 — never silent 0.
The generated profile has three sections — identity, agent context, and trust settings — and every share_* flag in it starts false, so report withholds identity and context until you turn them on yourself. The trust defaults are the most conservative of each: autonomy: ask_first, confidence_gate: 0.8, hitl_gate: 70.
The profile is yours and stays local; add .trustshell/ to your .gitignore if you do not want it in version control.
trustshell profile show | add | set | remove | interview
In this tree, not in the published 1.7.0. Read and edit the personal sections of .trustshell/profile.md. No network. Every entry is text you type; nothing is detected or inferred. These sections:
- Values: ranked, in your own words. Use
profile add value "<text>". To re-rank, edit the file. - People I help: use
profile add person "<who>" --why "<why>"(--whyis optional). - Style: use
profile set style <key> <value>, where the key istone,detail,riskorhours. - Constraints: use
profile add constraint "<text>", for example "client data never leaves my device". - Model priorities: use
profile set priorities cost,quality,speed(all three, most important
first, no duplicates) or profile set priorities --preset balanced|cost-first|speed-first|quality-first. These are stored only: nothing sends them to a model yet.
profile remove <section> <n> removes an entry: values, people and constraints by number, style by key, priorities with no number. profile show prints the file, or NO_PROFILE if there is none.
None of these sections appears in report unless you set share_personal: true in the file. That flag defaults to false, and anything other than true is read as false. Until a section holds something, none of it is written, so a fresh profile is unchanged from init. An edit rewrites only the section it touches. A file with a problem (an unknown style key, an unknown preset) is never rewritten: the command prints the problem, exits 1, and writes nothing. Text that is not one line, contains <!-- or -->, or looks like a credential is refused with exit 2.
profile interview asks three questions. Each one can be skipped and says why it is asked:
- Who do you most want to help? The answer goes to People I help.
- What is one thing you need done this week? This is printed back as a
learn job addhint and is
never stored in the profile.
- What must never happen? The answer goes to Constraints.
Without a terminal, pass the answers with --answers "who|this week|never" and leave a part empty to skip it.
trustshell remember <text>
Write one note into the local sqlite file ~/.trustshell/memory.sqlite (TRUSTSHELL_MEMORY overrides the path). No network. The row kind is note.
Since 1.7.0, the note is stored untrusted and quarantined unless you pass --trusted (you vouch you wrote it). --source <label> and --source-file <file> label untrusted text; --source-file records the document's sha256 for a later ground release. See trustshell quarantine below.
trustshell remember <key> <value> stores one value under that key in the same file. Remembering the same key again replaces the value. No network.
A note or value that looks like a credential is refused and nothing is written: API keys and tokens (AWS, Google, Stripe, GitHub, npm, Slack, Hugging Face, sk-…, sb_secret_…), JWTs, private keys (64-hex and PEM), database URLs, webhook URLs and labelled passwords. These are the same shapes the outbound scrubber removes. An email or a phone number is stored: the file is local.
Encryption at rest (opt-in). Set TRUSTSHELL_MEMORY_ENCRYPT=on and TRUSTSHELL_MEMORY_KEY=<passphrase>, and every new note and value is written with AES-256-GCM, under a key derived with scrypt from the passphrase and a random salt kept in that file.
- With the flag on and no passphrase,
rememberfails and writes nothing. It never falls back to plain text. - Reading needs only the passphrase. Turning the flag off later does not lock you out of what you already saved.
- Notes saved before you turned it on stay in plain text.
- Without the passphrase,
recallprints[encrypted note: set TRUSTSHELL_MEMORY_KEY to read it]in place of each sealed note.recall <key>fails rather than print a value it could not open. - It protects the text inside the file from someone who copies the file without the passphrase. It does not hide which kind of row exists or when it was written. A passphrase in an environment variable can be read by any process you run.
trustshell recall
Print saved notes, oldest first. A do_not_send row is not printed; the line is do_not_send COUNT N. No network.
trustshell recall <key> prints that value. A missing key, or a blank value, prints NOT_CHECKED, never an empty string.
Since 1.7.0, quarantined notes are left out, and a last line quarantined COUNT N (trustshell quarantine list) says how many. --include-quarantined prints them, each marked [QUARANTINED: reason].
trustshell quarantine list | explain <id> | release <id>
Since 1.7.0. On 1.6.1 and older this command exits
2with
unknown command: quarantine.
Immune system v0 for local memory. remember "<text>" (with or without --untrusted) and the MCP remember tool store the note with provenance: untrusted, quarantined. Only remember --trusted stores a trusted note. Notes saved before this existed are untrusted, source legacy, and quarantined when the file is first opened.
list: id, source and reason for each held note, never its text (also with--json, and over MCP). Endsquarantined COUNT N. No network.explain <id>: provenance, source, quarantined, reason, released_by, released_at, whether a source document was recorded, and the note's text. No network.release <id> --source <file> [--page N]:<file>must be the document recorded with the note (remember --source-file, or MCPremembersource_text; only its sha256 is kept). Then the whole note must be found,VERIFIED, in it (ground). No recorded document, or a different one, isNOT_CHECKED. Local, no network. The recorder supplied that document, so the note becomesgrounded, neververified.release <id> --check hal: released only ifPOST /api/v1/classifyon the default endpoint labels the notepassby votes, with two named deciders of two different model families that both saidTRUE. A customTRUSTSHELL_API_URLis not asked and releases nothing. The note gets the same scrub ascheck; a note the scrubber changed is not released.release <id> --trusted: the operator vouches for it. Not a check: the note becomestrusted, andreleased_bysays so.releasetakes exactly one of the three, and exits0released,1held (the check failed),2not checked or no such note. A HAL release setsprovenance: verified, a ground releasegrounded, and--trustedsetstrusted;released_byrecords which.- Injection-looking text is screened by the fixed instruction-smuggling patterns, which are not a model, when stored and again when read. A match is held with that reason, never released (not even with
--trusted), and never printed bylist,explainorrecall.remember --trustedis screened too. - There is no release over MCP: the MCP caller is the untrusted writer.
- What it does not do: no sandboxed execution, no adaptive learning, nothing outside this package's local memory. A release is not a truth verdict.
--trustedis the operator's word, and anything that can run your shell can say it.
trustshell learn <verb>
Not in the published 1.7.0. On 1.7.0 and 1.6.1 this command exits
2with
unknown command: learn.
The learning loop v1, local. Nothing leaves the machine. trustshell learn alone lists the verbs.
job add <slug> --mode delegate|cowork|learn "<When …, I want …, so I can …>",job list: the jobs are yours. An episode under a slug you did not create is stored and left out of every ruler.check add --job <slug> --id <check> --cmd "<command>";check run <check> [--episode N | --turn K]: the only writer of a check outcome, from the command's exit code (exit0pass,1fail,2the command did not exit and nothing was written). A fail retires the accepted lessons injected into that episode that are scoped to that check, or to no check (job-wide), with a receipt line each. A lesson scoped to another check stays accepted.record: episode metadata (model, family, tokens, the check id frozen at the start, confidence before the check runs).--outcomeis stored as the agent's and read by no ruler. A secondrecordwith the same--turnupdates the same episode.rate <episode> accept|edit|reject: your rating, reported, outside the agent rulers.correct --job <slug> "<your words>": writes an eval case, then a candidate lesson, verbatim, untrusted and quarantined untilaccept <lesson>.--derived-from-untrusted <label>marks text that is not yours (a page, another agent, a model): held for good, never injectable.--sourceis its deprecated old name and prints a warning saying so.--credit <label>records who prompted the correction and leaves the lesson yours.--notekeeps a model paraphrase as a quarantined note, never injected. Secret-shaped text writes nothing; injection-looking text is held.accept <lesson>(30 days, renewable),decline <lesson>,lessons [--job <slug>].acceptrefuses a retired lesson.reaccept <lesson>brings one back: it prints the retirement receipt, then needs an interactive terminal and the lesson id typed back before it writes, and records a re-accept receipt. Without a terminal it refuses and writes nothing. There is no MCP tool for either.declineputs the lesson's text back in quarantine.inject --job <slug> [--turn K] [--check C] [--model M] [--family F]: what a prompt hook prints. Only this job's accepted, unexpired lessons that are job-wide or scoped to the check this episode froze (no frozen check: job-wide only), verbatim, at most 5 and 400 tokens with the scaffold and the cloze included, recorded as the episode'sloop_tokens(a second inject on the same turn does not count them again). If the scaffold and the cloze alone would pass 400, the cloze is left out and the text says so.--modeland--familyare recorded on the episode; an episode with no family is left out of ruler 1. Inlearnmode: the scaffold only untilattempt --job <slug> "<your attempt>", then the lessons and one blank cut from your attempt. Incoworkmode: it asks for your prediction first. A missing memory file or an unknown job prints nothing and exits0.probe add --job <slug> --check <check> --criterion "<text>",probe run <probe>: an unaided attempt on alearnjob, criterion written first, scored 1 or 0 by the check. No lesson is printed.review,review <review> yes|no: spaced review on a 1/3/7/14/30-day ladder. A miss goes back to the 1-day step.report: the rulers, each with its job, check id, model family and eval-set hash, ornot measured.- MCP:
learn_lessons_for(the same text asinject) andlearn_record(metadata; its outcome is the agent's). No MCP tool runs a check, accepts a lesson or writes a correction. - Example hooks for Claude Code:
examples/learn-hook/user-prompt-submit.mjs(UserPromptSubmit: runsinject; envTRUSTSHELL_LEARN_JOB, optionalTRUSTSHELL_LEARN_CHECK(freezes that check on the turn and scopes the lessons shown),TRUSTSHELL_LEARN_MODEL,TRUSTSHELL_LEARN_FAMILY) andexamples/learn-hook/stop.mjs(Stop: fills in that turn's model, family and tokens from the transcript; it never opens a new episode and never writes an outcome). - The first
learncommand on a memory file made beforelearnexisted rebuilds its table once, in one transaction. It first copies the file to<file>.bak-<UTC time>(0600) next to it and prints that path. If the copy fails, nothing is rebuilt.
trustshell redact <key>
Delete the row for <key> from ~/.trustshell/memory.sqlite. Prints redacted. A missing key prints NOT_CHECKED. No network.
trustshell bind-status
Read GET /api/v1/after-create. Print can_bind from that body. A true can_stake prints shadow — not live, never live. A missing or failed read is NOT_CHECKED. No send.
trustshell traps
List the ten local fixture HAL claims shipped in-repo. Each row is NOT_CHECKED until a receipt id exists in .trustshell/traps.json. No scoreboard, no HAL wins, no live stake claims. Options: --json.
$ trustshell traps
surgeon NOT_CHECKED
missing-dollar NOT_CHECKED
monty NOT_CHECKED
trustshell ground --source <file> --quote "<text>"
Since 1.7.0. On 1.6.1 and older this command exits
2with
unknown option: --source, and the SDK does not exportgroundQuote(),groundClaims()or
LIMITATIONS.
Check a quoted claim against the document it cites, locally: no model, no network. Is the quote on the cited page, and (with --kind number|date) is the value in the quote? The source file is split into pages on form feed (pdftotext output); a file without one is page 1. Options: --page N, --kind text|number|date, --value V (with --kind number or date; a date is YYYY-MM-DD), --unit <u> (with --kind number), --echo "<text>" (repeatable: the document's own template or label text), --json (prints the receipt from groundClaims()). Prints the status, the reason, the matched line and what it does not establish.
| Status | Reasons | Exit |
|---|---|---|
VERIFIED | exact_match | 0 |
VETO | quote_not_found, page_mismatch (it says which page the quote is on), quote_too_short (under 4 non-space characters), value_not_in_quote | 1 |
NOT_CHECKED | no_evidence (no quote, or a source with no text), ambiguous_value, template_echo — never a pass | 2 |
The rules, in order, and the full list of what VERIFIED does not establish are in src/lib/ground.ts (groundQuote and LIMITATIONS). The two that matter most: a number needs one number in the quote, or with --unit exactly one number written right before that unit; and a date must read only one way (03/04/2026 is ambiguous_value). A quote that only repeats an --echo (in the SDK, opts.echoes) is template_echo.
trustshell status
Print the after-create table, one Honesty A line, and counted first_pass fields. A NOT_CHECKED body or a missing column is NOT_CHECKED, never 0. post-HAL is printed only when post_hal_verdict is in the JSON. can_bind is true only when GET /readiness exact_true.HUMAN_AGENT_BIND_ENABLED is true. can_stake stays shadow — not live unless SAYS_STAKE_LIVE is the exact string 'true'.
trustshell report [--session <path>] [--evidence <path>]
State what your session log and your saved GitHub evidence together support, and name where they disagree. No network — it has no fetch and no URL parameter; external evidence arrives as a file you produced with trustshell check --json. Options: --session <path> (default .trustshell/session.jsonl), --evidence <path>, --json.
| Verdict | Meaning | Exit |
|---|---|---|
CONFIRMED | an intact local chain and external evidence that agrees with it | 0 |
INCONSISTENT | the log and the external evidence disagree | 1 |
UNSUPPORTED | a claim with no evidence either way — NOT CHECKED | 3 |
The word is INCONSISTENT, never "contradicted". Two records disagreeing is a fact about the records; "contradicted" reads as a finding about the agent's honesty, which this tool cannot establish and does not imply.
CONFIRMED is the narrowest verdict on purpose, and it inherits the anchor limit above: the local chain is unkeyed, so CONFIRMED cannot rule out a fabricated session either. The card says so.
(No example card is printed for check, inspect or report. A rendered card is a claim about a run or a session that happened — run these against your own and read the real output.)
What each command talks to
--json gives machine-readable output on most commands; remember, recall, redact and bind-status print plain lines. No command exits 0 on NOT CHECKED. verify / evaluate and check "<sentence>" exit 2 for it; check <runUrl>, inspect and report exit 3.
| Command | Network egress | Auth |
|---|---|---|
verify | HyperDAG backend (TRUSTSHELL_API_URL) | keyless; REPID_API_KEY optional |
repid | HyperDAG backend | keyless |
proof | HyperDAG backend (--verify runs the verifier locally) | keyless |
badge | HyperDAG backend (rendering + verification are local) | keyless |
check | api.github.com only — no backend, no telemetry | none; GITHUB_TOKEN optional, rate limit only |
check "<sentence>" | HyperDAG backend (TRUSTSHELL_API_URL), /api/v1/classify only | keyless |
receipt | HyperDAG backend (TRUSTSHELL_API_URL), GET /api/v1/kernel/receipt/:id only; the hash check is local. Since 1.7.0 | REPID_API_KEY required |
inspect | none — reads a local file | none |
ground | none — reads the --source file. Since 1.7.0 | none |
init | none — writes one local file. --pai runs scripts/init-pai.mjs (live register) | none |
report | none — it has no fetch and no URL parameter | none |
profile | none — reads and writes .trustshell/profile.md. In this tree, not in the published 1.7.0 | none |
remember | none — writes the local memory file | none |
recall | none — reads the local memory file | none |
redact | none — deletes one row from the local memory file | none |
quarantine | none — reads and updates the local memory file; release --source reads that file | none |
learn | none — reads and writes the local memory file; check run and probe run execute the command you registered. In this tree, not in the published 1.7.0 | none |
quarantine release --check hal | the default HyperDAG backend only, /api/v1/classify, after the same scrub as check. A TRUSTSHELL_API_URL that points elsewhere is not asked, and nothing is released | keyless |
status | HyperDAG backend (TRUSTSHELL_API_URL): GET /api/v1/after-create, /api/v1/hal/honesty-a and /readiness. It sends no input of yours | keyless |
bind-status | HyperDAG backend (TRUSTSHELL_API_URL): GET /api/v1/after-create only. It sends no input of yours | keyless |
traps | none — reads the fixture bundled in the package and .trustshell/traps.json | none |
init and profile are the only commands that write to your working directory, and they write exactly one file: .trustshell/profile.md.
trustshell --help / --version
--version reports the installed package version.
Public REST API
All endpoints below are public — no API key required. CORS allows trustrepid.dev, trustshell.dev, www.trustshell.dev, and localhost:3000/3001. Base URL: https://repid-engine-production.up.railway.app.
GET /api/v1/status
Consolidated service health + last-24h economic activity + last cron telemetry.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/api/v1/status
Example response (a snapshot from 2026-05-27, not live; re-curl for the current state):
{
"service": "repid-engine",
"version": "1.0.0",
"network": "base-sepolia",
"timestamp": "2026-05-27T06:56:00.075Z",
"operational": { "supabase": true },
"metrics_24h": {
"onchain_attestations": 0,
"real_settlements": 0,
"score_events": 325,
"firecrawl": {
"enabled": true,
"calls": 0,
"cost_usd_24h": 0,
"by_agent": [],
"note": "rollout active, 0 calls in last 24h (research agents only: trinity-nexus, trinity-torch)"
}
},
"last_heartbeat": null,
"audit_status": {
"at": "2026-05-25T07:35:31.615+00:00",
"overall": "WARN"
},
"hero_receipt": "/api/v1/receipts/hero"
}
Possible errors: 500 if Supabase is unreachable (operational.supabase: false).
GET /api/v1/receipts/hero
The first end-to-end economic loop, settled 2026-05-24 on Base Sepolia: real USDC settlement → real on-chain reputation attestation. Every transaction hash is clickable on basescan. It is a historical receipt, not the result of anything you install or run: the pay path (executeA2A) needs an API key and a funded testnet wallet.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/api/v1/receipts/hero
Example response:
{
"label": "First live USDC settlement → on-chain reputation attestation (full economic loop)",
"network": "base-sepolia",
"chain_id": 84532,
"contract_id": "006e2416-d3de-431e-a83f-b6c488fc81bc",
"value_usdc": "0.10",
"operator_wallet": "0xf6eE1768868c3266868edcA78bC41C50309cb22A",
"provider": "trinity-shofet",
"provider_wallet": "0x15eB9A7427f1B54486926465d5895cD51eB8b052",
"repid_change": { "before": 2980, "after": 3040 },
"usdc_settlement": {
"tx": "0x2a7ac151c23983f59564fc3da5c7ea74fdbe390f9e97fcbf70c79be27089967a",
"block": 41917330,
"basescan": "https://sepolia.basescan.org/tx/0x2a7ac151c23983f59564fc3da5c7ea74fdbe390f9e97fcbf70c79be27089967a"
},
"reputation_attestation": {
"tx": "0xd362c1b0c819e2e1ee7bce601531afb0be1eef20c1be4ab8dc643e524d19e917",
"block": 41917386,
"registry": "0x8004B663056A597Dffe9eCcC1965A193B7388713",
"basescan": "https://sepolia.basescan.org/tx/0xd362c1b0c819e2e1ee7bce601531afb0be1eef20c1be4ab8dc643e524d19e917"
},
"settled_at": "2026-05-24T06:09:07Z"
}
Possible errors: none under normal operation (constant payload).
GET /api/v1/hal/stats
HAL (Hallucination Assessment Layer) production statistics across the full pipeline: lifetime + last-24h counts on each canonical source table.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/api/v1/hal/stats
Example response (live snapshot):
{
"total_inferences": 214,
"total_classifications": 214,
"audit_chain_length": 4628,
"peer_verification_queue_size": 149,
"last_24h_inferences": 120,
"last_24h_classifications": 120,
"breakdown": {
"hal_classifications": { "lifetime": 214, "last_24h": 120 },
"hal_audit_chain": { "lifetime": 4628, "last_24h": 537 },
"hal_production_events": { "lifetime": 5, "last_24h": 0, "sample": { "in_last_1000": 5, "caught": 0 } },
"peer_verification_queue": { "pending": 149 }
},
"isLive": true,
"avg_latency_ms": 312,
"network": "base-sepolia",
"last_updated": "2026-05-27T06:56:00.556Z"
}
Possible errors: Returns 200 with null-typed counts on a partial Supabase read failure (no surface is silently masked; empty state is honest).
GET /api/v1/repid/:agentId
Per-agent RepID lookup. :agentId may be the engine's agent UUID, an agent name or slug (trinity-shofet, or bare shofet), or a numeric ERC-8004 token id.
Path parameters:
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string | yes | Agent UUID, name/slug, or ERC-8004 token id. |
Query parameters:
| Name | Effect |
|---|---|
with=id | Adds agent_id, the UUID the id resolved to. Without it the body is only score and tier. |
Example requests:
curl https://repid-engine-production.up.railway.app/api/v1/repid/trinity-shofet
curl "https://repid-engine-production.up.railway.app/api/v1/repid/trinity-shofet?with=id"
Example responses (MEASURED 2026-10-06; the score moves):
{ "score": 2202, "tier": "ESTABLISHED" }
{ "score": 2202, "tier": "ESTABLISHED", "agent_id": "32e0e809-c1c4-4405-913f-135c8a2d6626" }
Tier scale: PROBATIONARY (0–499) → EARNING (500–999) → ESTABLISHED (1,000–4,999) → AUTONOMOUS (5,000–7,999) → VETERAN (8,000–10,000).
Unknown agent: an id that matches no agent answers 200 with {"score":"NOT_CHECKED","tier":"NOT_CHECKED"} — not a 404, and never a 0. The SDK's verify() / getRepID() throw RepID not checked on that body rather than return a number.
Possible errors: 404 AGENT_NOT_FOUND when the lookup itself fails for another reason.
GET /api/v1/llm-trust
Per-LLM hallucination-rate leaderboard. Filtered to exclude test/diagnostic entries and case-duplicate rows; sparse-sample rows (<10 decisions) are excluded by default to avoid statistical noise.
Query parameters (opt-outs for ops debugging):
| Name | Type | Default | Effect |
|---|---|---|---|
include_test | boolean | false | Re-include test-harness / diagnostic-test / manual / test providers. |
include_stale | boolean | false | Re-include rows whose most recent decision is >30 days old. |
min_decisions | integer | 10 | Lower bound on total_decisions per row. |
Example request:
curl https://repid-engine-production.up.railway.app/api/v1/llm-trust
Example response (live snapshot — only providers with ≥10 recent decisions show up):
[
{
"llm_provider": "anthropic",
"llm_model": null,
"total_decisions": 11,
"hallucinations_caught": 0,
"hallucination_rate_pct": 0,
"trust_score_pct": 54.55,
"avg_certainty": 0.844,
"agents_using": 2,
"last_decision": "2026-05-11T00:41:03.03768+00:00"
}
]
include_test=true raises the row count (e.g. to 3 at the time of writing) and surfaces the test-harness training rows.
Possible errors: Returns [] if the underlying leaderboard view is empty for the current filter.
GET /.well-known/agent.json
AGNTCY-style agent card. Lists capabilities, protocols, and trust attestations.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/.well-known/agent.json
Example response (excerpt — full body is ~2 KB):
{
"schema_version": "1.1",
"agent": {
"name": "HyperDAG RepID Engine",
"handle": "@hyperdag/repid-engine",
"description": "Stateful trust scoring and reputation infrastructure for AI agents. ERC-8004 identity oracle and x402 payment coordinator.",
"version": "1.0.0",
"homepage": "https://repid.dev"
},
"capabilities": [
{ "name": "register_agent", "method": "POST", "path": "/api/v1/agents/register" },
{ "name": "score_event", "method": "POST", "path": "/api/v1/agents-external/:id/score-event", "auth": "bearer" },
{ "name": "get_agent_card", "method": "GET", "path": "/api/v1/agents/:id/card" },
{ "name": "validate_erc8004", "method": "GET", "path": "/api/v1/erc8004/validate/:agent_id" },
{ "name": "request_x402_tip", "method": "POST", "path": "/api/v1/tip/request" },
{ "name": "deliver_x402_tip", "method": "POST", "path": "/api/v1/tip/deliver/:tipId" },
{ "name": "stake_deposit", "method": "POST", "path": "/api/v1/stake/deposit" },
{ "name": "complete_with_evaluation", "method": "POST", "path": "/api/v1/llm/complete" }
],
"protocols": [
"HyperDAG Trust Protocol v1",
"ERC-8004 Reputation Registry",
"x402 Agentic Payment Protocol"
],
"trust_attestations": [
{
"type": "ERC-8004",
"address": "0x8004A818BFB912233c491871b3d84c89A494BD9e",
"network": "base-sepolia"
},
{
"type": "Plonky3 ZKP",
"prover_type": "babybear-range-check",
"description": "Succinct proof of RepID score state"
}
],
"economic_parameters": {
"staking_token": "USDC",
"staking_network": "base-sepolia",
"min_stake_usd": "100.00"
},
"rate_limits": {
"public": "60 req/min",
"authenticated": "300 req/min"
}
}
Possible errors: none under normal operation (constant payload).
GET /agent.json
Convenience alias for /.well-known/agent.json. Byte-identical response.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/agent.json
Response: same as /.well-known/agent.json (verified byte-identical).
GET /api/v1/firecrawl/stats
Firecrawl research-tool rollout statistics (calls + USDC cost over the last 24h). Returns an honest empty-state note when the rollout has been active but unused.
Parameters: none.
Example request:
curl https://repid-engine-production.up.railway.app/api/v1/firecrawl/stats
Example response (current — rollout active, low traffic):
{
"enabled": true,
"calls": 0,
"cost_usd_24h": 0,
"by_agent": [],
"note": "rollout active, 0 calls in last 24h (research agents only: trinity-nexus, trinity-torch)"
}
When traffic exists the response includes a by_agent breakdown:
{
"enabled": true,
"calls": 3,
"cost_usd_24h": 0.0025,
"by_agent": [
{ "agent_id": "848da285-...-3d9e49ebed09", "calls": 2, "cost_usd": 0.00166 },
{ "agent_id": "9c0dc740-...-9ba3d515369d", "calls": 1, "cost_usd": 0.00083 }
]
}
Possible errors: Returns { enabled: false, note: "trinity_tool_usage read failed" } on a Supabase read error (graceful, no 500).
Errors
Common HTTP statuses across the SDK + REST surfaces:
| Status | Where | What it means |
|---|---|---|
200 | Any GET | OK |
201 | SDK score-event | RepID delta accepted |
400 | API key request | Validation failed (missing email, invalid use_case) |
401 | Authed routes | API key missing or wrong header format |
403 | Authed routes | Key revoked or wrong tier |
404 | /repid/:agentId | the lookup failed (an unknown id is 200 with NOT_CHECKED, not 404) |
429 | Score-event, API key request | Rate limit (1 key-request per email per hour) |
500 | Any | Engine error — retry once, then open an issue if persistent |
The SDK throws on every non-2xx; the public REST endpoints return JSON error bodies with shape { "error": "..." }.
Reflects the published
@hyperdag/trustshellv1.7.0 surface and the productionrepid-enginedeployment. Click-by-click CLI walkthrough:WALKTHROUGH.md.