{"name":"main","description":"Agent-facing surfaces for main.","url":"https://help.eloquenttest.sh","version":"1.0.0","capabilities":{"streaming":false,"pushNotifications":false},"authentication":{"schemes":["bearer"]},"defaultInputModes":["application/json"],"defaultOutputModes":["application/json"],"skills":[{"id":"auth_whoami","name":"auth_whoami","description":"Resolve the caller — principal id, email, auth method — from the router-resolved principal, enriched with the auth.principals row when one exists.","tags":["auth_whoami"]},{"id":"auth_provision","name":"auth_provision","description":"Mint a bare principal (no identity attached) and issue its first API key, in one call. No credit grant of any kind — funding an account is treasury's job (SPEC §1.2, §9).","tags":["auth_provision"]},{"id":"auth_claim","name":"auth_claim","description":"Attach a first identity (email, OAuth subject, or wallet address) to a principal that has none yet. NEW-IDENTITY-ONLY — an identity already bound to any other principal is rejected, never merged.","tags":["auth_claim"]},{"id":"auth_create_key","name":"auth_create_key","description":"Issue a new API key for the calling principal. The plaintext is returned exactly once.","tags":["auth_create_key"]},{"id":"auth_issue_key","name":"auth_issue_key","description":"Issue a key on behalf of another principal, or (when principal_id is omitted) a service key with no principal owner. Internal-gated — the caller is a trusted service, not the key's own subject.","tags":["auth_issue_key"]},{"id":"auth_list_keys","name":"auth_list_keys","description":"List the calling principal's own keys, masked (key_prefix only — plaintext is never returned again).","tags":["auth_list_keys"]},{"id":"auth_revoke_key","name":"auth_revoke_key","description":"Revoke one of the calling principal's own keys. A key id that does not belong to the caller, or is already revoked, 404s.","tags":["auth_revoke_key"]},{"id":"auth_list_service_keys","name":"auth_list_service_keys","description":"List all service keys (principal_id IS NULL), masked.","tags":["auth_list_service_keys"]},{"id":"auth_revoke_service_key","name":"auth_revoke_service_key","description":"Revoke a service key by id. A principal-owned key id here 404s indistinguishably from a missing id.","tags":["auth_revoke_service_key"]},{"id":"auth_email_initiate","name":"auth_email_initiate","description":"Start email verification. With no key, CREATE mode (a fresh principal will be minted on confirm). With a key, CLAIM mode (the email binds to the key's principal on confirm). Sends exactly one confirmation email; fails closed 503 when no sender is configured.","tags":["auth_email_initiate"]},{"id":"auth_email_confirm","name":"auth_email_confirm","description":"Redeem a single-use email-confirmation token. CREATE mode mints a bare principal and claims the email onto it. CLAIM mode (token + matching key) claims the email onto the key's existing principal with no new key.","tags":["auth_email_confirm"]},{"id":"auth_disable","name":"auth_disable","description":"Disable a principal. Sets disabled_at if not already set; idempotent (already-disabled returns already:true). Every key resolving to this principal fails auth once the front door reads auth.principals (SPEC §9 item 6).","tags":["auth_disable"]},{"id":"auth_enable","name":"auth_enable","description":"Re-enable a principal. Clears disabled_at if set; idempotent (already-enabled returns already:true).","tags":["auth_enable"]},{"id":"mcpsim_put","name":"mcpsim_put","description":"Create an environment or mint a new version of one (SPEC, Immutable versions and The environment document). Full replace is the only write: post the whole document. Omit `slug` to create — mcpsim generates one (word pair plus a short unambiguous suffix, SPEC, Slugs and addresses) and returns the URL that serves it; answers 201. Carry `slug` to replace that environment; answers 200, and a byte-identical repost is a no-op on the content hash rather than a version churn. Documents exceeding a configured bound are REFUSED, never truncated (SPEC, Bounds).","tags":["mcpsim_put"]},{"id":"mcpsim_get","name":"mcpsim_get","description":"Read an environment's document — its spec, world, stored tool definitions, settings, version, content hash, spend to date, and retention deadline (SPEC, The environment document and Data model). Omit `version` for the current one, or name a prior version to read it — prior versions stay readable so that a transcript pinned to one stays interpretable. `settings` comes back fully materialised, with every default filled in, never echoing the posted subset. Answers 200. Owner-only, merged with not-found.","tags":["mcpsim_get"]},{"id":"mcpsim_list","name":"mcpsim_list","description":"List the caller's own environments, newest first, and answer 200 with an empty array when it owns none. Never lists another principal's environments, and is not an existence oracle for slugs the caller does not own.","tags":["mcpsim_list"]},{"id":"mcpsim_calls","name":"mcpsim_calls","description":"Read an environment's call transcript in seq order, oldest first, and answer 200 with an empty array when nothing has been emulated (SPEC, Data model and The conformance record). Conformance is an OBSERVER: it records whether a declared outputSchema was satisfied and which members were missing, extra, or mistyped, and it never altered what was returned. Owner-only, merged with not-found.","tags":["mcpsim_calls"]},{"id":"mcpsim_delete","name":"mcpsim_delete","description":"Delete an environment (SPEC, Retention). Removes its versions, its sessions, and its call transcripts, and its address stops resolving. Environments are scratch objects and also expire on their own retention deadline; this is the explicit path. Answers 200 with `deleted` true when it removed one and false when the slug was already gone — idempotent, and the false answer discloses nothing a caller did not already know from having deleted it. Owner-only, merged with not-found.","tags":["mcpsim_delete"]},{"id":"tools_list_estates","name":"tools_list_estates","description":"Every estate that has ever declared into this registry (SPEC §6). An estate is keyed by specialist_id and appears the first time anything declares under it, never disappearing; the record carries when it was first and last seen. The base estates (`mainnet`, `testnet`) sit in this list beside real specialist uuids and are not distinguished — they are simply ids that match no principal, which is not an error. On an EMPTY registry this answers an empty list, not a 404.","tags":["tools_list_estates"]},{"id":"tools_list_domains","name":"tools_list_domains","description":"Every domain an estate's declared tools belong to, with how many tools each groups (SPEC §6). `domain` is a PROPERTY of a tool — a grouping statement — not a level in a hierarchy and not a deployment fact, so there is no availability state here and no reporter. `at` reads the registry as of an RFC 3339 instant. The response echoes the specialist_id and the instant it was answered at; an EMPTY registry answers an empty list, never a 404.","tags":["tools_list_domains"]},{"id":"tools_get_domain","name":"tools_get_domain","description":"One domain's tools within an estate (SPEC §6.2). A name that no observation in that estate names answers 404 not_found — the same answer a populated registry gives for an unknown name, so an empty registry is not a special case. The answer carries no reporter, no host, and no availability member: none of those exist in this model.","tags":["tools_get_domain"]},{"id":"tools_list","name":"tools_list","description":"The main query (SPEC §6.1): every tool an estate declares, filtered by domain, effect, and auth, and searched with a case-insensitive substring `q` over name and description. Search is this parameter and NOT a separate tool — a /api/tools/search path would collide with /api/tools/:name. Each entry carries {name, domain, effect, auth, signature, specialist_id}. There is deliberately NO availability filter: nothing here records deployment. The response echoes {specialist_id, observed_at, tools} and a next_cursor when more remain; an EMPTY registry answers an empty list, never a 404.","tags":["tools_list"]},{"id":"tools_get","name":"tools_get","description":"One tool's full declared contract by its globally-unique name (SPEC §6.2): {name, domain, specialist_id, effect, auth, signature, method, path, description, inputSchema, outputSchema}. A name no observation in that estate carries answers 404 not_found. Nothing in the answer says whether the tool is concrete: that is a lookup in the container at call time.","tags":["tools_get"]},{"id":"tools_observations","name":"tools_observations","description":"The observation log (SPEC §2.1, §6.3) — history IS this log, so there is no revision or version model. Filter by specialist_id and a since/until window. Every row is a CHANGE: re-declaring an unchanged estate advances its last_seen_at and writes nothing, so the log never grows at declaration rate. Newest first, cursor-paginated. An EMPTY registry answers an empty list.","tags":["tools_observations"]},{"id":"tools_diff","name":"tools_diff","description":"What changed between two points (SPEC §6.4). Both `from` and `to` are selectors of the form <specialist_id>@<instant|latest>, e.g. `mainnet@2026-08-01T00:00:00Z` or `9f1c…@latest`. ONE grammar covers both drift-over-time (same estate, two instants) and estate-versus-estate (two estates, same instant) — there are deliberately not two modes. Returns {from, to, added, removed, changed}; each `changed` entry names the tool and BOTH signatures. A malformed selector is refused 400.","tags":["tools_diff"]},{"id":"tools_validate","name":"tools_validate","description":"Check a manifest or a single tool document (SPEC §6.5) and report {valid, errors, signatures, collisions}. `kind` is REQUIRED and is NOT inferred from the document's shape: loadManifest chooses its reader by FILENAME and explicitly refuses to sniff content, because a malformed document that sniffs wrong comes up silently missing its wire rules. `specialist_id` names the estate whose declared tool names the collision check runs against. This CHANGES NOTHING — it is effect `read` despite being a POST, because effect classifies consequence, not method.","tags":["tools_validate"]},{"id":"tools_list_bindings","name":"tools_list_bindings","description":"The review surface for resolution bindings (SPEC §4). A binding is the recorded decision for one (specialist_id, call shape): resolved to a concrete tool, resolved virtual, or REFUSED. Refusal is a value, not the absence of a row — `resolution=refused` is exactly the query that answers `this keeps happening`, and the hits each binding carries are what make that a count rather than an anecdote. A binding is DEBT: it records a correction for a wayward call, and a healthy one ends in deletion. Bindings never appear in the tool list; this is where they are read. There is no scope filter — a binding at a base estate's specialist_id IS the default one. Filter by specialist_id, resolution, and the tool a binding resolved to. Newest decision first, cursor-paginated.","tags":["tools_list_bindings"]},{"id":"tools_get_binding","name":"tools_get_binding","description":"One binding by the id a refusal carries (SPEC §4.6). The refusal the caller saw and the record someone reviews are the SAME object, so the id in an error message reads back the decision, its reason, what it nearly matched, and when it was decided. An unknown id answers 404 not_found.","tags":["tools_get_binding"]},{"id":"tools_observe","name":"tools_observe","description":"The estate write (SPEC §5). A specialist_id declares the flat list of tools its estate has, each tool carrying its own `domain` as a property. The change unit is the SET OF TOOL SIGNATURES: re-declaring an unchanged set advances the estate's last_seen_at and writes no row, or the log would grow at declaration rate. The response's `changed` says which it was. A declaration that knows it is partial MUST set scan_complete false and name what it missed — because the change unit is the whole set, a partial declaration posted as complete is indistinguishable from tools having been removed, and would flip live bindings to virtual. auth: internal — service-to-service, so the router runs its internal-call gate rather than resolving a principal.","tags":["tools_observe"]},{"id":"tools_resolve","name":"tools_resolve","description":"What does this call MEAN in this estate (SPEC §4). Returns the BINDING for (specialist_id, call shape): resolved to a concrete tool, resolved virtual, or refused with a reason. The key is the argument SHAPE — key names, casing, nesting, types — never the values, so the same call with different values is the same binding and pays the fuzzy hop once. Written once and read forever: a binding is NEVER silently recomputed, because a caller whose call worked on Tuesday and refused on Thursday with nothing changed is a bug. The caller's own estate is consulted first, the base estate second. THE LADDER: an exact name match dispatches, every effect included, because an exact name is exact and nothing is being guessed — AND IT RECORDS NOTHING, because a binding is a remembered correction for a WAYWARD call and an exact name is not a correction. Failing that an existing binding is used, with no scoring and no model; an explicit one wins even over an exact name, because a person decided it deliberately and a virtual pin must survive the tool it was pinned against appearing. An exact name beats a MATCHER binding, which is by then dead debt — the estate was rewritten to match what callers were asking for — so it is reclaimed and the reclamation logged. Failing all that the call is SCORED semantically against ONE cutoff over every tool in the estate, and what cleared it decides. Nothing above it means VIRTUAL, the default rather than an error. Exactly one `read` tool above it binds; more than one and a cheap LLM picks, with `virtual` ALWAYS among its options, so a name easily confused with a concrete tool can be pinned to virtual permanently and never drift. ANYTHING above it that is not a `read` — a write, a payment, a destructive, or an effect word this domain does not recognise — is REFUSED, even when read-only tools also cleared: the caller may have meant the real thing, so they choose, not us, and letting a near miss on a write fall through to virtual would hand back a fabricated success about state that never changed. A refusal is a MENU, not a dead end: it carries every tool that cleared the cutoff, in score order, each with its inputSchema, plus the nearest one's expected shape and the binding id — and a person can then bind it with tools_bind, which is the only way any non-read target ever resolves. With no model configured nothing can clear the cutoff, so every unbound call is virtual — degradation in the safe direction, never a 500. This is a `write` because it MAY insert a binding the first time it sees a wayward shape, and it counts a hit on every later call for that shape — the usage record is not part of the decision, which is never recomputed.","tags":["tools_resolve"]},{"id":"tools_bind","name":"tools_bind","description":"Create the binding a matcher is not allowed to create (SPEC §4.9). Automatic bindings are READ-ONLY — the matcher may resolve a call to a `read` tool or pin it to virtual, and nothing else — because a fuzzy match cannot prove itself correct, and `correct enough` is not a standard anything that moves money should be held to. A binding onto a `write`, `payment` or `destructive` tool therefore exists only when a person deliberately made it, after reading the refusal's menu of similar tools and deciding which one the caller actually meant. The key is the same as everywhere else: the call name plus the SHAPE of the arguments, values never stored. The target must be declared in the estate, because the estate is what says which tools exist. This REPLACES whatever the matcher recorded for that shape, including a refusal, which is the normal case since a refusal is a value and already occupies the row — write-once binds the matcher, never the operator. The result is marked as made by a person, so review can tell, and the UNUSED sweep leaves it alone — but it is swept like any other when the estate declares a tool by the name it corrects, because that rule is about the name existing and not about who wrote the row. This is NOT promotion: promotion adopts an existing binding into the base estate, this one creates a decision no matcher was ever going to be allowed to take. auth: internal.","tags":["tools_bind"]},{"id":"tools_clear_binding","name":"tools_clear_binding","description":"Remove ONE binding by id (SPEC §4.10), whatever its origin and however many times it has been used. A binding stays fixed until the name it corrects becomes a real tool or a person clears it — so `unless manually cleared` has to name an operation that does it, or a binding somebody no longer wants, and that is still being called, has no exit at all. The sweep cannot serve here: it is deliberately restricted to what was never used and to what the matcher wrote, and widening it to delete one named row would make a bulk heuristic do a surgical job. Returns the row it removed, so the answer is a record of what went rather than an assurance that something did; an id that names nothing answers 404. auth: internal.","tags":["tools_clear_binding"]},{"id":"tools_promote_binding","name":"tools_promote_binding","description":"Adopt a binding proven at one caller into the BASE ESTATE (SPEC §4.4), so every identical shape resolves the same way without being re-decided per caller. The same promotion path as virtual -> concrete: prove at the edge, adopt into the shared estate. Promotion is an INSERT of the same decision at the base estate's specialist_id — not a state transition — and it leaves the caller's own binding exactly as it was, because a binding is written once and never rewritten. There is no scope column: specialist_id already says whose binding it is. Promoting an already promoted shape is a no-op that returns the existing base binding. auth: internal — adopting into the shared estate is an operator act. There is no `owner` tier in this domain.","tags":["tools_promote_binding"]},{"id":"tools_sweep_bindings","name":"tools_sweep_bindings","description":"Delete UNUSED bindings (SPEC §4.10). A binding is DEBT — a remembered correction for a wayward call — and every healthy outcome ends in its deletion: either the wayward call stops, or the estate's tool is rewritten to match what callers actually ask for and the binding goes dead. A binding that lives forever is unpaid debt, not a feature. With no window this deletes the bindings decided and never needed again (hits = 0); with `unused_since` it deletes the bindings not used since that instant, counting a never-used binding from when it was decided. It can be narrowed to one estate. THIS IS A TOOL, NOT A SWEEPER: there is no background loop in this domain on purpose, because a loop would be the first step toward the registry having an opinion of its own — being asked to sweep is not having one. Do not convert it into a timer, a cron, or a boot job; if sweeping must be periodic, the periodicity lives outside this domain. It reports the count AND the rows it deleted, so the answer is a record rather than a number to be trusted. Deleting nothing is a normal, successful answer: a registry with no unpaid debt is the goal state. auth: internal, like every other binding write.","tags":["tools_sweep_bindings"]},{"id":"help_list_domains","name":"help_list_domains","description":"List every domain in the manual, with its domainName, claim, and tool count.","tags":["help_list_domains"]},{"id":"help_get_domain","name":"help_get_domain","description":"Fetch one domain's full configuration object — profile, surfaces, and tool list.","tags":["help_get_domain"]},{"id":"help_list_tools","name":"help_list_tools","description":"List every tool across the whole network, each with its domain, effect, and computed signature.","tags":["help_list_tools"]},{"id":"help_get_tool","name":"help_get_tool","description":"Fetch one tool by its globally-unique name — full schema, effect, consequence, and signature.","tags":["help_get_tool"]},{"id":"help_search_manual","name":"help_search_manual","description":"Apropos — free-text search across every domain and tool in the manual.","tags":["help_search_manual"]},{"id":"ledger_deposit","name":"ledger_deposit","description":"Fund the caller's own primary account over x402 / HTTP-402 (SPEC §4.2, §4.3): with no X-Payment header answer 402 with the paywall; with one, verify the settlement through the facilitator to the configured confirmation depth, then transfer the x402 source -> the primary account for the SETTLED amount, idempotent on the settlement tx id. On testnet the faucet source funds immediately. The credited amount is derived from the verified settlement, never from the caller (SPEC §4.2).","tags":["ledger_deposit"]},{"id":"ledger_checkout","name":"ledger_checkout","description":"Open the fiat on-ramp for amount_nano (SPEC §4.2). Mainnet: bind the session to the caller's participant at creation, ensure the Stripe customer, and create a hosted Checkout session for the GROSS (amount x (1 + SERVICE_FEE_RATE)); the credit lands later, when the signature-verified checkout webhook transfers the stripe source -> the primary account and books the operator fee and processor fee into the configured operating accounts (SPEC §4.2, §5.4). Testnet is not a fiat rail. Presentment currency MUST be USD. amount_nano is a decimal string in [$1, $1000].","tags":["ledger_checkout"]},{"id":"ledger_ensure_customer","name":"ledger_ensure_customer","description":"Idempotently ensure the CALLER's own primary account has a Stripe customer id and return it (SPEC §4.2). The participant is derived from the authenticated caller — never taken from the body (SPEC §7). No money moves. 503 when no Stripe client is configured (testnet).","tags":["ledger_ensure_customer"]},{"id":"ledger_grant","name":"ledger_grant","description":"Admin-only conjured credit (SPEC §7.3). A positive amount_nano mints from the `mint` source -> account_id; a negative amount_nano reverses, moving account_id -> `mint`, subject to the same overdraft refusal as any movement (a reversal of already-spent value fails AM04). Never targets the faucet source. Bounded by the mint ceiling; idempotent on identifier; writes a mint/reversal audit event.","tags":["ledger_grant"]},{"id":"ledger_get_balance","name":"ledger_get_balance","description":"The caller's spendable balance (SPEC §9.2): the caller's own primary account's available (balance_nano − held_nano), returned as available_nano — distinct from ledger_get_account's balance_nano, which is gross. Self-scoped only; auto-provisions the primary account at zero on first touch.","tags":["ledger_get_balance"]},{"id":"ledger_open_account","name":"ledger_open_account","description":"Open an additional account for the caller (SPEC §2, §7), optionally funded (a nested transfer from the caller's primary account on a savepoint in the SAME transaction). The new account's principal_id is the caller's, derived server-side — never caller-supplied — and immutable. overdraft_limit_nano may be set ONLY by a caller holding the admin capability and defaults to \"0\"; a non-zero value from a non-admin is rejected (SPEC §7.3).","tags":["ledger_open_account"]},{"id":"ledger_get_account","name":"ledger_get_account","description":"One account's balance, owner, status, and overdraft limit. Ownership-gated (SPEC §7.2) — an account belonging to a DIFFERENT participant 404s, indistinguishable from a missing id; source accounts (no principal_id) are never resolvable through this tool.","tags":["ledger_get_account"]},{"id":"ledger_list_accounts","name":"ledger_list_accounts","description":"A participant's own accounts (SPEC §9.2) — every row denormalized with principal_id = caller, an O(1) indexed scan.","tags":["ledger_list_accounts"]},{"id":"ledger_transfer","name":"ledger_transfer","description":"Move nano from one account to another (SPEC §5). THE ONLY MOVEMENT PRIMITIVE. Owner-scoped: the caller MUST own the source account (unless it holds the admin capability), and the source MUST NOT be a reserved source account; the destination may be any ordinary account. One conditional decrement per side in one READ COMMITTED transaction, rows locked in ascending id order; the debit WHERE clause (balance - amount >= -overdraft_limit) IS the atomic overdraft refusal. The transfer kind is set server-side, never by the caller. Idempotent on (owner, identifier); AM04 on insufficient balance; AC02/AC03 on an unknown or unowned account.","tags":["ledger_transfer"]},{"id":"ledger_close_account","name":"ledger_close_account","description":"Sweep the remainder to a named account of the same participant, then close (SPEC §7). Refuses to close a participant's PRIMARY account; the sweep destination MUST be an active ordinary account of the caller — never a source, a closed account, or another participant's. Ownership-gated (404 for a different principal's account or a source). Idempotent: an already-closed account returns already:true with no re-sweep. The sweep, when balance > 0, is a nested transfer on a savepoint in the SAME transaction as the close.","tags":["ledger_close_account"]},{"id":"ledger_hold_place","name":"ledger_hold_place","description":"Reserve an owned account's available balance for a named capturer (SPEC §5.6). An optional destination_account_id binds every capture immutably to that active ordinary account; omission preserves capturer-chosen payees. Placement changes held_nano, never balance_nano, auto-releases at expiry, and is idempotent on (placer, identifier), with the destination included in the replay fingerprint.","tags":["ledger_hold_place"]},{"id":"ledger_hold_capture","name":"ledger_hold_capture","description":"Capture a hold chunk (SPEC §5.6). Capturer-only, active-only, bounded by the remaining amount, and idempotent on (capturer, identifier). A bound hold requires to_account_id to equal its immutable destination; mismatch is non-disclosing AC03 with no mutation. An unbound hold may credit any active ordinary account. Capture atomically moves the money, reduces held_nano, advances captured_nano, and writes one hold_capture transfer.","tags":["ledger_hold_capture"]},{"id":"ledger_hold_release","name":"ledger_hold_release","description":"Capturer-only release of a hold's uncaptured remainder (SPEC §5.6). It lowers held_nano, leaves balance_nano unchanged, and is idempotent; the account owner cannot release the grant early.","tags":["ledger_hold_release"]},{"id":"ledger_hold_extend","name":"ledger_hold_extend","description":"Capturer-only monotonic extension of an active hold (SPEC §5.6). A request that is not later is a no-op success; an inactive hold cannot be revived, and the configured maximum lifetime cannot be crossed.","tags":["ledger_hold_extend"]},{"id":"ledger_solvency","name":"ledger_solvency","description":"The operator's read of SPEC §2.2's relation: claims_nano (Σ positive ordinary balances), backing_nano ((−Σ reconciling sources) + Σ named operating accounts), the ids of both contributing sets, and the outstanding conjured total the faucet and mint have issued — which backs NOTHING and is reported beside B rather than added to it. fully_explained_by_conjured says whether claims − backing is covered by that conjured total, which is what distinguishes a faucet-funded testnet operating normally from value that appeared without a source. Admin-only, and it ANSWERS while insolvent rather than refusing. Reads only: it never reconciles, never halts ingress, and writes no audit row.","tags":["ledger_solvency"]},{"id":"messages_key_put","name":"messages_key_put","description":"Publish or rotate the authenticated caller's public key. Senders seal direct messages to this key. Republishing the same key is a no-op. A rotation applies to new messages; messages already sealed to a previous key stay sealed to it, and each message records the fingerprint it was sealed to. A request carrying a private key is refused with `private_key_submitted`.","tags":["messages_key_put"]},{"id":"messages_key_get","name":"messages_key_get","description":"Read a principal's current public key. Open to anonymous callers. `me` resolves to the authenticated caller. A principal that has published no key answers 404.","tags":["messages_key_get"]},{"id":"messages_post","name":"messages_post","description":"Post one message. `visibility` selects the shape of the rest of the request, and a request mixing the two shapes is refused.\nPUBLIC — `body` is required and stored as plaintext. `to_principal_id`, `ciphertext`, and `sealed_to_fingerprint` must be absent (`ciphertext_on_public_message`).\nDIRECT — `to_principal_id`, `ciphertext`, and `sealed_to_fingerprint` are required and `body` must be absent (`plaintext_direct_message`). The sender seals the plaintext to the recipient's published key before calling. A recipient with no published key is refused with `recipient_has_no_key`.\n`sender_ciphertext` is the same plaintext sealed to the sender's own key. It is optional, and it is what makes the sender's outbox readable.","tags":["messages_post"]},{"id":"messages_get","name":"messages_get","description":"Read one message. A public message returns its `body` and is readable by anyone, including anonymous callers. A direct message is readable by its sender and its recipient; every other caller gets 404, the same answer an unknown id gets.\nA direct message returns a null `body` and a `ciphertext`: the recipient's sealed copy for the recipient, `sender_ciphertext` for the sender. A sender that posted no `sender_ciphertext` gets a null `ciphertext`.","tags":["messages_get"]},{"id":"messages_list","name":"messages_list","description":"List messages the caller may see, newest first. `box` selects which: `public` needs no authentication, `inbox` is direct messages addressed to the caller, and `outbox` is direct messages the caller sent. An anonymous caller requesting `inbox` or `outbox` is refused with `authentication_required`. Entries have the shape messages_get returns.","tags":["messages_list"]},{"id":"moderator_review","name":"moderator_review","description":"Open a priced review of a referenced subject under a published policy (SPEC §5). The caller supplies a policy id and a `subject_ref` owned by another domain — never the material; moderator fetches the evidence itself inside the trusted boundary, under exactly the evidence classes the policy declares (SPEC §3.2). The requester MUST stand in the policy's declared relation to the subject (`self`, `party`, or `holder`, SPEC §5.3), and a review of anyone but the requester writes a disclosure the subject can read. A destination-bound ledger hold for the policy's price is placed at open and captured at settle; a roster that cannot evaluate releases it uncaptured (SPEC §6). Settles in-band when a trusted evaluator answers inside the window, otherwise returns `pending` and is polled with moderator_get_review. Idempotent on (requester, identifier).","tags":["moderator_review"]},{"id":"moderator_escalate","name":"moderator_escalate","description":"Re-review the same subject at a strictly HIGHER evaluator tier than the decision being appealed (SPEC §5.5). This is the domain's only due-process path and its only price ladder — an escalation is priced by the escalation policy. The prior decision remains the current one until the escalation settles, and then is superseded; a settled escalation at the roster's top tier can go no further. The requester must stand in the same relation to the subject that the original review required. Idempotent on (requester, identifier).","tags":["moderator_escalate"]},{"id":"moderator_cancel_review","name":"moderator_cancel_review","description":"Requester-only cancellation of a review that has not settled (SPEC §5.4). It releases the uncaptured hold and closes the review as `cancelled`. A settled review cannot be cancelled: supersede its decision with an escalation (SPEC §5.5), since there is no delete path. Idempotent — cancelling an already-cancelled review returns already:true and moves no money.","tags":["moderator_cancel_review"]},{"id":"moderator_get_review","name":"moderator_get_review","description":"One review's status and, once settled, its full decision document plus the detached signature over it (SPEC §5.2). Visible to the requester and to the subject principal; anyone else receives 404, indistinguishable from an unknown id. It carries the policy version and roster version the decision was made under, since a decision is interpretable only against the rules and roster in force when it was made. It carries no evidence, no excerpt, no score, and no free text.","tags":["moderator_get_review"]},{"id":"moderator_list_reviews","name":"moderator_list_reviews","description":"The caller's own reviews — those it requested, and optionally those where it is the SUBJECT (SPEC §5.2). Filterable by policy, subject kind, status, decision, and an RFC 3339 window. Enumerating a third party's reviews through this tool is impossible; what a caller can see about being reviewed is the disclosure log.","tags":["moderator_list_reviews"]},{"id":"moderator_get_clearance","name":"moderator_get_clearance","description":"The current unexpired decision for a (policy, subject_ref) pair, or the absence of one (SPEC §5.6). This is what makes the toll bearable: a participant who transacts repeatedly buys a clearance with a policy-declared lifetime rather than a fresh review per transaction. Answers to the subject, and to a caller standing in the policy's relation to it. `standing: none` means no review has settled under this policy for this subject; it carries no information about the subject, and consumers must read no signal into it.","tags":["moderator_get_clearance"]},{"id":"moderator_verify","name":"moderator_verify","description":"Check a decision document a counterparty presented (SPEC §8). A POST classified as a `read`: effect follows what a call does to state (SPEC §7.1). It answers two separate questions. `authentic` says the signature is a genuine moderator signature over exactly this document, which a verifier can also establish OFFLINE against moderator_signing_keys. `current` says the decision has not expired, been superseded, or been made under an evaluator since retired — all facts that arise after the signature is made, so the signature cannot carry them. Stay offline for `authentic`; call this tool for `current`.","tags":["moderator_verify"]},{"id":"moderator_signing_keys","name":"moderator_signing_keys","description":"The public keys decisions are signed with, current and previous, each with its validity window and the roster version it corresponds to (SPEC §8.2). The ONE `auth: none` tool in this domain, and deliberately so — a verifier that must hold a credential to fetch the key it verifies with cannot verify offline at all, which would make every decision a round trip to the operator. Publishing the public half discloses nothing; the trust rests on confining the private half.","tags":["moderator_signing_keys"]},{"id":"moderator_list_policies","name":"moderator_list_policies","description":"Every published policy at its current version — what it reviews, what relation it requires of a requester, what evidence classes it opens, its decision and reason vocabularies, its price, and its clearance lifetime (SPEC §3). Published in full BEFORE payment, so a caller can read the terms and the price before committing to either. Retired versions remain readable through moderator_get_policy, which keeps an old decision interpretable.","tags":["moderator_list_policies"]},{"id":"moderator_get_policy","name":"moderator_get_policy","description":"One policy at a named version, or its current version when none is named (SPEC §3.1). Versions are immutable and are never deleted. A settled decision cites the version it was made under, and it stays auditable only while that version remains readable.","tags":["moderator_get_policy"]},{"id":"moderator_put_policy","name":"moderator_put_policy","description":"Operator door (SPEC §3.1). Publishes a NEW immutable version of a policy, leaving existing versions untouched, since settled decisions cite the version they were made under. A body identical to the current version is a no-op success; a different body always creates the next version. The evidence classes a policy declares are the ONLY material a review under it may fetch, so widening them is the most consequential edit in this domain and lands as a version bump an auditor can see. Admin-only: the wire tier is `owner` because the router has no `admin` member, and MODERATOR_ADMIN_PRINCIPALS is the authoritative gate.","tags":["moderator_put_policy"]},{"id":"moderator_list_evaluators","name":"moderator_list_evaluators","description":"The trusted roster at its current version — each evaluator's id, tier, the attestation under which it was admitted, and its admission and retirement instants (SPEC §4). Published so a caller paying for a trusted judgment can see who they are trusting. Publication covers identity and terms; routing stays private, with no endpoint, no credential, and no provider key. Evaluator credentials are held outside this domain and appear on no moderator wire.","tags":["moderator_list_evaluators"]},{"id":"moderator_put_evaluator","name":"moderator_put_evaluator","description":"Operator door, and the most consequential one in the network (SPEC §4.2): it decides who may see sensitive material. Admitting or retiring an evaluator bumps the roster version, which every subsequent decision cites and which `moderator_verify` checks a presented decision against. Retirement applies going forward — decisions already made stand, and `current` reports `evaluator_retired` so a relying party can decide for itself. The record holds no credential: the evaluator's key material lives outside this domain and is referenced by an opaque id. Admin-only via MODERATOR_ADMIN_PRINCIPALS; the wire `owner` tier is a coarse filter and nothing more.","tags":["moderator_put_evaluator"]},{"id":"moderator_get_evidence","name":"moderator_get_evidence","description":"The operator's audit read of one settled review (SPEC §9). It answers what was fetched, from which domain, under which evidence class, by which evaluator, at what instant, and the digest of each item. It cannot answer what the material said, because moderator discards it (SPEC §2.3). Classified as a `write` despite the name: opening an evidence record is a disclosure, and the tool records one in the subject's disclosure log BEFORE answering. The operator auditing a decision is therefore visible to the subject on the same terms as anyone else.","tags":["moderator_get_evidence"]},{"id":"moderator_list_disclosures","name":"moderator_list_disclosures","description":"The subject's own view of who has looked at their sensitive material — which requester, under which policy, opening which evidence classes, at what instant, and whether the review settled (SPEC §10). Scoped to the caller as SUBJECT, and suppressible by nobody: the operator's own audit reads appear here beside everyone else's. A decision is a one-bit oracle and one-bit oracles leak under repetition, so this tool makes the repetition visible to the subject it is aimed at.","tags":["moderator_list_disclosures"]},{"id":"specialists_catalogue","name":"specialists_catalogue","description":"The dimension/scalar/suppressed registry loaded from spec/specialist_dimensions.yaml (SPEC §4) — the single source of truth for valid characterisation metadata. dimensions are binary behaviours (yes/no/null, some abstract parents with applies_when children); scalars are numeric measures (condition-tagged percentile bundles or points) carrying unit/half_life/derives; suppressed are intentionally-refused ids (is_llm, is_agent, is_model, is_aggregate, …) the write path rejects.","tags":["specialists_catalogue"]},{"id":"specialists_select","name":"specialists_select","description":"Filter (SPEC §8.2.1) — resolve a capability/property target to a candidate specialist-id set; the v1 eligibility primitive lib/evals calls to resolve task eligibility (ADR 0006). A side-effect-free read despite the POST verb: it moves no money and returns ids only. Provider/lineage is never a filter — only meaningful-at-our-abstraction facets (is_human, require_tags, quality_tier, max_blended_cost, min_usable_context, policy, min_confidence).","tags":["specialists_select"]},{"id":"specialists_write_cell","name":"specialists_write_cell","description":"Write one cell — a (specialist, entry) → {value, confidence, provenance, measured_at, explanation} record (SPEC §6.1). Accepts supplied (self-declared claim) and observed (authentic work telemetry) provenance; inherited and verified are not yet postable (v1.1 / the verification economy, §8.1). Rejected 422 if entry_id is unknown or suppressed, or the value does not match the entry's type. Maintainer (master) only.","tags":["specialists_write_cell"]},{"id":"specialists_register","name":"specialists_register","description":"Register a specialist — id is the SUPPLIED account principal id (account.specialists.id, §1.1), never server-minted (ADR 0006); label/model_binding/address/metadata/members are all optional. A duplicate id 409s rather than 500ing. Maintainer (master) only.","tags":["specialists_register"]},{"id":"specialists_list","name":"specialists_list","description":"List / filter the directory — q (substring on label/id), is_human, status, has_binding, keyset-paginated via cursor/limit (default 50, max 200) — to summaries (id, label, status, has_binding, model_binding, timestamps).","tags":["specialists_list"]},{"id":"specialists_get","name":"specialists_get","description":"Read one specialist's registry row, its membership edges, and its stored cells with decay-adjusted confidence_eff alongside the raw confidence (SPEC §2.5); hoists quality/cost_per_token/median_latency_ms from metadata — the exact shape pools' facets provider reads over this same alias. Full resolution (precedence ledger, applies_when gating, derived entries, inheritance, aggregation) is v1.1 (§5.2).","tags":["specialists_get"]},{"id":"specialists_update","name":"specialists_update","description":"Update mutable registry fields — label, status, model_binding, address, metadata. id is the immutable principal id (§1.1) and addresses the row; a body id that disagrees with the path id 400s. Catalogue cells are written via write_cell, not here. Maintainer (master) only.","tags":["specialists_update"]},{"id":"specialists_retire","name":"specialists_retire","description":"Retire a specialist — soft-delete (status → retired, cells retained but excluded from default reads) by default, or hard DELETE with force=true (cascades to cells and membership edges). Maintainer (master) only.","tags":["specialists_retire"]},{"id":"task_create","name":"task_create","description":"Create or attach the authenticated poster's canonical live task. An explicit identifier replays only with the same spec. Optional assignment terms route through task_assign's exact destination-bound funding saga.","tags":["task_create"]},{"id":"task_get","name":"task_get","description":"Read a visible task and its public assignments without economic identifiers.","tags":["task_get"]},{"id":"task_assign","name":"task_assign","description":"Resolve the selected worker payout account, commit a non-claimable intent, and place a destination-bound hold before offering.","tags":["task_assign"]},{"id":"task_cancel","name":"task_cancel","description":"Make an unclaimed offer non-claimable, then recoverably release its hold.","tags":["task_cancel"]},{"id":"task_claim","name":"task_claim","description":"Named-worker guarded transition from a confirmed offer to claimed.","tags":["task_claim"]},{"id":"task_release","name":"task_release","description":"The named worker returns a claim to the same funded offer; no ledger operation occurs.","tags":["task_release"]},{"id":"task_reassign","name":"task_reassign","description":"Confirm release of the old offer, resolve the replacement worker, and place a new bound hold.","tags":["task_reassign"]},{"id":"task_close","name":"task_close","description":"Enter closing, recoverably unwind offers, and finalize only after every assignment drains.","tags":["task_close"]}]}