DocsGuides

Attestation

Most of a real application's read API is filtered reads, and a filter's meaning is not in the code's structure. Attestation lets a named person assert that meaning in a committed, reviewed file. The tool carries the claim exactly, labels it on every surface, and still proves everything around it.

When to use it

Your survey refused a read you know is safe and useful: a filtered list, a read that returns a view object the structure cannot tie to an entity, a read that delegates to a remote service. Structure cannot prove what it selects or returns, so discovery will never expose it. A person who knows can say so, on the record.

Attestation shipped in 0.3.0. It adds to discovery and never changes it: every attestation phase measured byte-identical generated output on all three benchmarks.

Reads only

A write or an action cannot be attested. That boundary needs a policy gate that does not exist yet, and an assertion is not a policy gate.

What the tool still proves

An attestation replaces one missing fact, not the whole proof. Around every entry the tool still checks:

  • the target resolves to exactly one method declaration in your source, and the binding is structural;
  • the input types, and that your entry covers every modeled input exactly;
  • read-safety over everything it read: if it sees a write there, the entry is refused;
  • that the subject entity you name exists, and that your claim does not contradict what the code provably reads;
  • a by-primary-key load, wherever your entry claims an identifier.

If the structural evidence contradicts an assertion, at intake or after a code change, the attestation is refused loudly and nothing ships. The tool never silently prefers either side.

The four categories

CategoryWhat the human asserts
predicateWhat a filtered read selects.
shapeWhat a read returns, for a read structure cannot tie to an entity.
bothA filtered read whose return type also cannot be tied: one entry, one name, closing both holes.
boundaryA remote delegation, including that it is read-only, which is the one property the tool cannot check for that shape. It needs a named security owner and an explicit affirmation, and it is not exposed unless you opt in.

An entry

The record lives at tibyan/attestations.json in your application, a JSON array, committed beside the source it describes.

tibyan/attestations.json
[
  {
    "target": "com.example.billing.service.InvoiceServiceImpl#getInvoicesByBuilding(String, Integer, Integer)",
    "category": "predicate",
    "name": "invoicesByBuilding",
    "resultSemantic": "Invoices belonging to one building, ordered by date descending and returned one page at a time.",
    "parameters": {
      "buildingId": "The numeric identifier of the building whose invoices are returned, passed as a string.",
      "pageNum": "paging",
      "pageSize": "paging"
    },
    "provenance": {
      "assertedBy": "a.reviewer",
      "origin": "human-authored",
      "date": "2026-09-01",
      "fingerprint": ""
    }
  }
]
FieldRule
targetMust resolve to exactly one method: <fully qualified class>#<method>(<parameter types>). Zero or two matches refuse.
nameYours to mint. The tool adds the attested_ prefix itself (attested_boundary_ for a boundary entry), so do not include it.
resultSemanticThe sentence that reaches the agent, verbatim. See the next section.
parametersMust cover every modeled input exactly. Each value is a sentence, or one of two reserved marks: "paging", only on a structurally recognised paging or ordering parameter, and "id", only on an identifier input where the tool can prove a by-primary-key load.
subjectEntityThe entity you assert the read is about. It must exist in the model and must not contradict what the code provably reads.
securityOwner, readOnlyAffirmed, assertedAuthoritiesBoundary entries only. Silence is never an affirmation: an absent or false readOnlyAffirmed refuses.
provenanceWho asserted it, how the words were produced (human-authored, ai-draft, or ai-drafted-human-confirmed), the date, and the fingerprint. Leave the fingerprint empty: the CLI stamps it.

Writing the sentence

The tool description an agent reads is built from resultSemantic, behind the label "Human-attested, not tool-verified". A vague sentence produces confident wrong answers. A good one says what comes back and how many, what is silently excluded, what an omitted parameter defaults to, and whether the result is paged or capped.

Sentence
Too vagueReturns the costs for a building.
UsefulCollected, outstanding and overdue totals for one building's rental payments, computed over at most the first 25 matching rows because the read leaves the default page size in place; it is not a summary of all of a building's payments.

The workflow

  1. Write the entry in tibyan/attestations.json.
  2. Stamp it. This computes the fingerprint of the code the claim is about and writes it into the file.
  3. Re-scan. The survey reports the entry as attested, or refuses it with a located reason.
  4. Commit the file and review it like code. Your next build carries it into the manifest.
shell
tibyan attest /path/to/app --date 2026-09-01
tibyan scan /path/to/app

The command reference is in the CLI docs.

Fingerprints and drift

The fingerprint is a SHA-256 over the structural slice the claim covers. It is recomputed on every scan. When the code under an attestation changes, the attestation stops matching and is refused loudly rather than carried, so a claim can never silently outlive the code it was made about. Re-read the method, confirm the sentence is still true, and stamp again.

Drafts

An entry whose origin is ai-draft is a third thing, neither attested nor refused. It is queued in the survey, counted apart, and ships nothing: no capability, no manifest entry, no OpenAPI operation. Any drafter works, human or otherwise, so long as the entry carries that origin. Confirmation is a per-entry, named human act:

shell
tibyan attest /path/to/app --confirm "<target>" --by a.reviewer

There is deliberately no confirm-all, in the CLI or the API beneath it. If you want an AI agent to do the drafting, the repository has a guided adoption prompt (opens in a new tab) that keeps every entry a draft until a person confirms it.

What ships, and how it is labeled

  • The MCP tool name itself starts with attested_, so the class is readable before any description is.
  • Every description and document says human-attested. It is never presented as verified and never counted among the verified capabilities.
  • The OpenAPI document serves attested capabilities under /attested/<name>.
  • An attested capability executes exactly like a verified one, with the same access. The label changes what the tool claims about a call, never how the call is made.

Exposure controls

Three runtime properties decide what registers: tibyan.attested.expose (default true), tibyan.attested.exposeSecurityUnverifiable (default false), and tibyan.attested.exposeBoundary (default false). They are described in the runtime reference.

Who owns what

OwnerResponsibility
The toolFaithful carriage of your file: the claim you ship is exactly the claim you committed, attached to exactly the method the entry targets, labeled, and invalidated the moment the fingerprinted code changes.
The asserterThe claim's truth. If the assertion is false, the tool cannot know, will carry the false sentence exactly, and an agent will act on it.
Your review processThe integrity of the file. The tool does not authenticate who typed an entry, which is exactly why the record is a committed, reviewed file and not tool state.

The bound, at its true strength

Before accepting an assertion the tool refuses if it sees a write in what it read. A write hidden below what it read, in a hand-written repository or query-builder implementation, is not caught. The guarantee is "no write in what the tool read", not "no write anywhere". Review attestations with the same seriousness as code.

The authoritative text is docs/SECURITY.md and the changelog (opens in a new tab) in the repository. If this page and that document ever disagree, the repository wins.