Skip to content

API Reference

Two surfaces:

  1. SDK — the @hyperdag/trustshell npm package, for code integrations.
  2. 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

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.

TrustShell does not extend EventEmitter and has no .on() method — use subscribe.

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:

BinWhat it is
trustshell, halthe CLI — two names, same program
trustshell-mcpthe MCP stdio server
verify, repid, proof, status, remember, recall, redactdeprecated 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:

codemeaning
0HAL PASS, or soft FLAG — safe to proceed
1HAL VETO — fail the build
2HAL did not decide (NOT_CHECKED) — never a pass. Also usage / bad arguments
3runtime error (network / backend / timeout)
4ASK — 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.

VerdictMeaningExit
COMPLETEthe run succeeded and every job passed0
INCONSISTENTGitHub calls the run a success, but a job did not pass1
FAILEDthe run's own conclusion was not success1
INCONCLUSIVEthe 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.

LabelExit
pass0
veto1
not-checked2 — 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 2 with

unknown command: receipt, and the SDK does not export kernelReceipt().

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).

OutcomeExit
read, and the hash matches0
the receipt does not match its own hash. Treat it as tampered1
not read: no key (401), another agent's key (403), no such receipt (404), network, timeout3

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.

VerdictMeaningExit
INTACTevery line's prev matches the previous line's hash — nothing removed or edited0
BROKENthe chain does not verify — lines were altered or dropped1
UNCHAINEDa foreign log with no chain — NOT CHECKED, not "fine"3
NO_LOGno log, or an empty one — NOT CHECKED. An empty chain is vacuously unbroken, which is not evidence3

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>" (--why is optional).
  • Style: use profile set style <key> <value>, where the key is tone, detail, risk or hours.
  • 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 add hint 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, remember fails 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, recall prints [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 2 with

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). Ends quarantined 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 MCP remember source_text; only its sha256 is kept). Then the whole note must be found, VERIFIED, in it (ground). No recorded document, or a different one, is NOT_CHECKED. Local, no network. The recorder supplied that document, so the note becomes grounded, never verified.
  • release <id> --check hal: released only if POST /api/v1/classify on the default endpoint labels the note pass by votes, with two named deciders of two different model families that both said TRUE. A custom TRUSTSHELL_API_URL is not asked and releases nothing. The note gets the same scrub as check; a note the scrubber changed is not released.
  • release <id> --trusted: the operator vouches for it. Not a check: the note becomes trusted, and released_by says so.
  • release takes exactly one of the three, and exits 0 released, 1 held (the check failed), 2 not checked or no such note. A HAL release sets provenance: verified, a ground release grounded, and --trusted sets trusted; released_by records 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 by list, explain or recall. remember --trusted is 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. --trusted is 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 2 with

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 (exit 0 pass, 1 fail, 2 the 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). --outcome is stored as the agent's and read by no ruler. A second record with the same --turn updates 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 until accept <lesson>. --derived-from-untrusted <label> marks text that is not yours (a page, another agent, a model): held for good, never injectable. --source is its deprecated old name and prints a warning saying so. --credit <label> records who prompted the correction and leaves the lesson yours. --note keeps 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>]. accept refuses 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. decline puts 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's loop_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. --model and --family are recorded on the episode; an episode with no family is left out of ruler 1. In learn mode: the scaffold only until attempt --job <slug> "<your attempt>", then the lessons and one blank cut from your attempt. In cowork mode: it asks for your prediction first. A missing memory file or an unknown job prints nothing and exits 0.
  • probe add --job <slug> --check <check> --criterion "<text>", probe run <probe>: an unaided attempt on a learn job, 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, or not measured.
  • MCP: learn_lessons_for (the same text as inject) and learn_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: runs inject; env TRUSTSHELL_LEARN_JOB, optional TRUSTSHELL_LEARN_CHECK (freezes that check on the turn and scopes the lessons shown), TRUSTSHELL_LEARN_MODEL, TRUSTSHELL_LEARN_FAMILY) and examples/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 learn command on a memory file made before learn existed 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 2 with

unknown option: --source, and the SDK does not export groundQuote(), 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.

StatusReasonsExit
VERIFIEDexact_match0
VETOquote_not_found, page_mismatch (it says which page the quote is on), quote_too_short (under 4 non-space characters), value_not_in_quote1
NOT_CHECKEDno_evidence (no quote, or a source with no text), ambiguous_value, template_echo — never a pass2

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.

VerdictMeaningExit
CONFIRMEDan intact local chain and external evidence that agrees with it0
INCONSISTENTthe log and the external evidence disagree1
UNSUPPORTEDa claim with no evidence either way — NOT CHECKED3

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.

CommandNetwork egressAuth
verifyHyperDAG backend (TRUSTSHELL_API_URL)keyless; REPID_API_KEY optional
repidHyperDAG backendkeyless
proofHyperDAG backend (--verify runs the verifier locally)keyless
badgeHyperDAG backend (rendering + verification are local)keyless
checkapi.github.com only — no backend, no telemetrynone; GITHUB_TOKEN optional, rate limit only
check "<sentence>"HyperDAG backend (TRUSTSHELL_API_URL), /api/v1/classify onlykeyless
receiptHyperDAG backend (TRUSTSHELL_API_URL), GET /api/v1/kernel/receipt/:id only; the hash check is local. Since 1.7.0REPID_API_KEY required
inspectnone — reads a local filenone
groundnone — reads the --source file. Since 1.7.0none
initnone — writes one local file. --pai runs scripts/init-pai.mjs (live register)none
reportnone — it has no fetch and no URL parameternone
profilenone — reads and writes .trustshell/profile.md. In this tree, not in the published 1.7.0none
remembernone — writes the local memory filenone
recallnone — reads the local memory filenone
redactnone — deletes one row from the local memory filenone
quarantinenone — reads and updates the local memory file; release --source reads that filenone
learnnone — 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.0none
quarantine release --check halthe 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 releasedkeyless
statusHyperDAG backend (TRUSTSHELL_API_URL): GET /api/v1/after-create, /api/v1/hal/honesty-a and /readiness. It sends no input of yourskeyless
bind-statusHyperDAG backend (TRUSTSHELL_API_URL): GET /api/v1/after-create only. It sends no input of yourskeyless
trapsnone — reads the fixture bundled in the package and .trustshell/traps.jsonnone

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:

NameTypeRequiredDescription
agentIdstringyesAgent UUID, name/slug, or ERC-8004 token id.

Query parameters:

NameEffect
with=idAdds 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):

NameTypeDefaultEffect
include_testbooleanfalseRe-include test-harness / diagnostic-test / manual / test providers.
include_stalebooleanfalseRe-include rows whose most recent decision is >30 days old.
min_decisionsinteger10Lower 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:

StatusWhereWhat it means
200Any GETOK
201SDK score-eventRepID delta accepted
400API key requestValidation failed (missing email, invalid use_case)
401Authed routesAPI key missing or wrong header format
403Authed routesKey revoked or wrong tier
404/repid/:agentIdthe lookup failed (an unknown id is 200 with NOT_CHECKED, not 404)
429Score-event, API key requestRate limit (1 key-request per email per hour)
500AnyEngine 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/trustshell v1.7.0 surface and the production repid-engine deployment. Click-by-click CLI walkthrough: WALKTHROUGH.md.