How Verify works.

This is the design of Provostry Verify, published as it is written for the people who build it. It says what a verdict proves, who is allowed to produce one, what the engine refuses to pretend, and where the edges are. A sample report shows the output end to end.

The gap The loop Trust model Time Limits The closed DSL Your first check API What it does not do

01 · The gap this closes

A ledger proves who said what — not that it was true

Provostry's ledger is tamper-evident: it proves who said what, and that nobody edited it afterward. It does not prove that what was said is true. An agent that appends evidence_bundle { verified: true } is grading its own homework — the chain will faithfully preserve a lie.

Verify adds the missing half: a system actor that checks claimed outcomes against reality and records what it actually observed, in the same append-only chain as the claims themselves.

02 · The loop

Contract, observation, verdict, gate, resolve, attest

  1. Contract. Before (or while) the work happens, someone posts a verification_contract: the verbatim goal plus 1–12 observable pass/fail clauses that define "done". Contracts are immutable, versioned revisions; amending one produces a new version that supersedes the old, and every verdict is bound to the exact content hash it judged — so a moved goalpost invalidates prior verdicts rather than silently inheriting them.
  2. Observation. The verify engine executes each clause's probe against a registered evidence source and appends a verification_observation with human-readable evidence: what was probed, what came back, how long it took, how many attempts. Engine-only — the generic ingest path refuses this type.
  3. Verdict. The engine folds clause outcomes into a verification_verdict: pass or fail, nothing else. Pass requires every critical clause to pass. An errored or inconclusive critical clause is a fail — missing evidence blocks, it never softens into "partial". Partial success is a fail with a clause-level breakdown attached.
  4. Gate. When an evidence_bundle claims verified: true on a work item that has contracts, the server appends an escalation for every contract that does not back the claim: no passing verdict, still awaiting approval, or rejected by a human. "Never approved" is a weaker basis for a completion claim than "failed", not a stronger one. Every contract on the item is checked, not the first, so a decoy contract with one trivially passing clause cannot mask a real one beside it. The claim still lands in the ledger — evidence is never refused — it just cannot pass silently.
  5. Resolve. Every metric that says "verified" asks one authority, the resolver, which applies the gate's own rule. A completion claim counts as verified work only when the work item has at least one contract and every such contract is approved with a passing verdict — or, for work with no contract, when CI attested it (below). Anything less is surfaced as what it is:
    StatusMeaningCounts as verified work
    verifiedContract(s) approved and passing, or a passing CI attestation on contract-less workYes
    self_reportedClaimed, but nothing independent backs itNo
    unverifiedClaimed against a contract that is unapproved, rejected, stale or failing, or against failing CINo
    not_claimedThe bundle made no completion claimNo
    Velocity counts distinct work items, not bundles, so repeating a claim does not repeat the credit. Health, quality, forecasts, milestone thresholds, the optimizer and the dashboard badges all read this one resolver — what the gate escalates and what the metrics count cannot diverge.
  6. Attest. CI as evidence, observed by the server. An evidence_bundle may name the commit and repo it describes. For a repository the organization has registered as a trusted CI source, the server fetches that commit's check runs from GitHub itself and appends a ci_attestation under a reserved actor — refused at generic ingest, like a verdict. The organization may name the checks that must succeed per repository; without a list, every completed check must succeed and at least one must actually have run, so a skipped-only run never certifies anything. A contract, when present, stays authoritative — CI passing does not rescue a failing or unapproved definition of done. An unregistered repository is never fetched: a green build somewhere the organization never vouched for proves nothing about its work.

    Nor does the poster's say-so bind a commit to work. A commit counts for a work item only if the repository declares that item or the commit message names it; anything else is recorded unbound and certifies nothing. And only the commit's author may claim it: a human session is matched to the author by the email the server verified at sign-in and nothing else (a name or login is free text to anyone with push access), an agent by the name or login the organization's own sync uses. A repository may list trusted posters (an organization's CI bot) who may cite any commit. Anyone else's claim is recorded foreign, certifies nothing, and reserves nothing — ledger arrival order never decides ownership. A commit certifies one evidence event, ever. "No check runs yet" is pending and re-examined for up to an hour before it is none, because the first look often lands before CI has registered a run, and a terminal answer there would be wrong forever.

03 · The trust model, stated plainly

Who is allowed to produce what

04 · Temporal semantics

Real systems are eventually consistent

Clauses carry time bounds instead of pretending a single instantaneous read is truth:

Evidence strings record attempt counts and elapsed time, so a verdict shows when reality was sampled, not just what it looked like. A whole run shares a 120-second wall-clock budget across its clauses; evidence is truncated (bounded entries, bounded length — observed values are excerpted, never dumped) and response bodies are read through a streaming size cap. The verdict also records the origin each named source resolved to at run time, so a later re-pointing of a source name is visible across verdicts rather than silently inheriting their credibility.

05 · Limits

Every bound is in the code, not in policy

A clause author cannot raise one, and a contract that asks for more is refused or errored rather than quietly granted.

BoundValueWhy
Per-request timeout10 sTightened further when the run budget has less left than that.
Response body256 KBRead as a stream with a running byte counter; the transfer is aborted mid-flight, not measured after the memory is spent.
Poll interval1 sBetween re-probes while a withinMs deadline is open.
withinMs≤ 60 000 msSchema ceiling per clause.
stableForMs≤ 30 000 msA window the remaining budget cannot honour is an error, never a pass on partial evidence.
Whole run120 000 msOne shared budget across every clause. A clause the budget never reaches is recorded error with that stated as its evidence.
Evidence≤ 20 entries × 400 charsObserved values are excerpted at 200 chars. One clause may never dominate the ledger.
Redirectsnever followedA 3xx is a result, not a hop, so a source cannot drag the probe to a host the registration guard never saw.
Plaintext http://the platform operator's loopback onlyAn organization's sources are https, and never the server's own loopback by any spelling.
Contract versionsjump warns, backslide ignoredv1 → v3 is accepted with a warning; a lower or equal version is ignored as immutable.
Gate escalationdeduped on structured fieldsNever on prose, so an agent cannot pre-silence the gate by copying its wording.
Clause outcomespass, fail, error, unknownunknown on a critical clause fails the verdict like error.

06 · The probe DSL is closed

Data over a closed vocabulary — never code, never a caller-supplied URL

Probes and assertions are data: one probe kind today (http GET/HEAD against a registered source's base URL plus a path) and three assertions (status_equals, json_path_equals, json_path_exists over a bounded dotted path). Every check is deterministic — the engine compares what came back to what the clause said, and no model reasons about whether it "looks right". New probe kinds are added server-side, deliberately, with their own evidence schemas — not by widening what callers may pass.

07 · Your first check, without a founder

An organization registers its own sources

An organization's owner or admin registers an evidence source of its own — an https base URL, optionally with a credential header whose value is encrypted at rest with the same key as every other customer secret and never returned by any API. The server proves it can reach the source before storing it. Every request to a customer-named host — the reachability check and every later probe — is resolved first, every address it resolves to is checked against the private, loopback, link-local and cloud-metadata denylist, and the connection is pinned to those addresses, so a hostname the organization controls, or DNS rebinding, cannot point the server at an internal host. A refusal says only that the destination is not allowed; a failed reachability check says only that the source did not answer. The route is not a scanner.

Contracts come from templates over the same closed DSL — "the endpoint answers 200", "a JSON field has the expected value" — bound to a work item and authored by the human in the session. The dashboard walks exactly this path: a source you own, a check from a template, a run, a verdict the server wrote.

Hosted, the records live in the hosted service, under your organization's tenant, and the hosted server runs the probes. Self-hosted, the ledger and the probes stay on your infrastructure. The assurance label on each observation says which.

08 · API surface

The routes

RouteWhat it does
POST /api/v1/events (type verification_contract)Register a contract through normal ingest.
POST /api/v1/verify/contractsBuild a contract from a template, bound to a work item, authored by the human in the session.
POST /api/v1/verify/:contractId/decisionHuman approve or reject — required before the engine will run a contract flagged requiresApproval. A rejection parks it; a later approval reinstates it. Recorded under the caller's own identity.
POST /api/v1/verify/:contractId/runExecute the probes; append observations and a verdict. Rate-limited; whole-run budget; 409 while a contract awaits approval or after a rejection.
GET /api/v1/verifyMaterialized contracts with status, latest bound verdict, and clause results.
PUT /api/v1/verify/sources/:name · GET /api/v1/verify/sources · GET /api/v1/verify/templatesAn organization's own sources (owner/admin; reachability-tested; credential encrypted, never returned), the list every member may read (names and hosts, never a credential), and the templates.
PUT /api/v1/integrations/githubRegister the repositories whose CI may attest the organization's work, the checks that must succeed, and any trusted posters.

The integration page has the event shapes and a worked example; the sample report shows what comes out.

09 · What Verify deliberately does not do

Stated so nobody has to discover it

Integrate your agents → See a sample report →