Skip to content

Claims

Declaring a claim, where checks live, the three computed states, and what counts as checkable.

File evidence checks now read only bounded regular files. A marker check refuses a file larger than 256 KiB, even when its opening text contains the marker. The limit counts bytes. A directory or symbolic link is not regular file evidence. This prevents a check from loading an entire large log before applying its limit.

A claim is something your notes assert that a machine can go and check.

The rule

The evaluator owns the status. The note never does.

A note declares a claim and the evidence it rests on. It does not get to say how it is doing. If you write status: VERIFIED in frontmatter it is ignored, and the note is flagged as having tried to certify itself.

That rule exists because of a bug in this project. Until recently the dashboard decided a note was VERIFIED by checking whether the text contained the word "VERIFIED", and rendered a green badge for it. Nothing verified anything, in the one tool whose whole premise is catching claims that carry no check.

Declaring one

---
claim: KYC is complete for onboarding path B
evidence: [kyc_mode, sanctions_clear, approver_recorded]
recheck: 1d
---

evidence holds names, never commands, URLs or paths. A note is untrusted, because your assistant writes notes, so a name can only ever resolve to something you put in config yourself.

The checks live in config

checks:
  kyc_mode:
    kind: http
    url: http://127.0.0.1:9000/health/kyc
    contains: fail_closed
  approver_recorded:
    kind: file
    path: ~/.brainlyy/records/approvals.json
    contains: approved_by
  disk_encrypted:
    kind: cmd
    argv: [manage-bde, -status]
    contains: "Fully Encrypted"
Kind Passes when
file the path exists, and contains the string if contains is set
http the response is under 400, and contains the string if set
cmd the command exits 0, and its output contains the string if set

cmd takes an argv list, never a string, and never runs through a shell.

The three states

State Means
VERIFIED every check passed, within the recheck window
UNVERIFIED the claim is checkable, and something failed, is missing, or expired
ASSERTED nothing here is machine-checkable, so no check will ever run

ASSERTED is not a failure. Most of what a knowledge base holds is legitimately in it: judgements, decisions, context, things that are true but not mechanically testable. Pretending otherwise is how you get a system that lies more confidently than the notes did.

What is checkable

All three must hold:

  1. the subject resolves to an artifact: a file, an endpoint, a record, a command's output
  2. the predicate is true or false without judgement
  3. it is falsifiable now, not eventually

"The sanctions screen returned clear on this account" qualifies. "Our approach to onboarding is sound" does not, and never will. Declare the second one anyway. It just stays ASSERTED.

Running it

brainlyy verify              # re-check everything, record what was observed
brainlyy verify --strict     # exit 1 if anything is unverified, for CI

Every run appends to $BRAINLYY_HOME/evidence/<year>-<month>.jsonl. Nothing is ever rewritten, so you can reconstruct what evidence supported a claim on a given date.

What the evidence log guarantees, and what it does not

Every line carries a hash over its own content and over the previous line's hash. brainlyy verify walks the log on every run and, if the chain breaks, names the first file and line, loudly and on stderr, without failing the run. A mangled line from last month does not make this morning's checks wrong, and an exit code here would only teach people to drop the flag.

The guarantee is: you cannot quietly change one line.

You do this It shows up
edit a line at that line, whose content no longer matches its own hash
delete a line at the line that followed it, whose link names a hash that is gone
append a line whose link names nothing in the file at that line

It is not tamper-proof, and please do not repeat that it is. This project exists to stop people overstating what was checked, so here is the whole list of what the chain does not do:

  • It proves consistency, not authenticity. There is no signature and no external anchor. Anyone who can write the file can recompute every hash forward from the line they touched and hand you a log that verifies clean. What the chain costs them is the quiet single edit.
  • It cannot see the last line of a chain go. Nothing points at it, so removing it leaves a log that still verifies.
  • It cannot see a whole month's file go. Chains do not span files, because history is archived by moving one and a chain across months would report itself broken during routine housekeeping.
  • It does not protect anything written before it existed. Those lines are reported as unchained, which is exactly what they are.
  • It ignores formatting. The hash covers the parsed content, not the raw bytes, so re-indenting a line is not tampering. Nor can it be used to smuggle a change past the hash.

Two runs can overlap: the --watch timer and a verify you started by hand. Appends are serialised by a file lock so they cannot write over each other. On Windows open(..., "a") is a seek-then-write rather than an atomic append, and without the lock four overlapping processes lost about half their lines. A lost line is one nothing points at, so the chain would not have reported it. The lock has a one-second deadline and then appends anyway: failing to record is better than refusing to finish a check.

Two things look like tampering and are not, so neither is reported as it: two lines linking to the same predecessor, which is what an interleaved append from two runs looks like; and a line with an empty link, which is how a chain starts, including part-way down a file that predates the format.

The number that matters

brainlyy verify reports how long the longest-lived false claim read VERIFIED before a run caught it.

That is the point of the whole thing. In the deployment this was extracted from, a knowledge base asserted a false regulatory status and fed it into daily reports for thirteen days. No check failed, because there was no check.

Creating a claim from the dashboard

The dashboard can do everything the frontmatter workflow does: declare a claim, pick its evidence from the checks config already defines, set a recheck window, and add new file and http checks to the allowlist, with a test-before-saving step, so a typo'd check fails in front of you rather than silently in the next verify run.

One thing is missing from that list on purpose: cmd checks cannot be created, or even test-run, from the dashboard. The API refuses kind: cmd outright, at every layer, with a 400.

The reason is the boundary this whole feature sits on. Config is the only place allowed to define something executable, and the dashboard is a network-reachable surface that writes config. If it could write a cmd entry, anyone who could reach the page could line up an arbitrary command and wait for the next verify to run it, and the guarantee "a browser cannot cause execution" would then rest entirely on the loopback gate never being relaxed or misjudged. Refusing cmd in the API keeps that guarantee structural rather than situational.

Adding a cmd check therefore stays what it should be: a hand edit to config.yaml, made on purpose, by someone who already controls the machine.

When one stops being true

The point of a claim is not that it passed once. It is that you find out when it stops passing, without going to look.

brainlyy verify --watch compares each run to the last and announces the one transition worth interrupting for: VERIFIED to UNVERIFIED. Something you believed has stopped being true and a check has just proven it.

Nothing else is announced. A claim that was already failing is not news, a promotion is good news that can wait, and an ASSERTED note has nothing to say. A notifier that fires on all of them gets muted within a week. Then the one that mattered is muted too.

You find out three ways, and they are the same event:

A desktop notification Windows toast, macOS notification, Linux notify-send. No dependency, because each platform already ships one.
A line in the terminal ! ops/backups.md stopped being true, evidence failed: backup_receipt
A banner on the dashboard Above every screen until dismissed, per alert

The banner matters most. A toast is a moment, and it is missed by anyone who was away from the machine, asleep, or on a headless box. The record in notifications.jsonl is still there when they come back.

Turn the toast off with notify.desktop: false in config; the record and the banner remain. Turn off re-checking entirely with verify.interval_minutes: 0 and nothing is announced, because nothing is checked.


Honest caveats

  • A claim is only as good as its check. file exists proves a file exists, not that its contents mean what you think.
  • Nothing here detects a claim that has become false when no check covers it. That is the harder problem and it is not built yet.
  • recheck is a promise about freshness; brainlyy verify --watch is what keeps it. brainlyy service install registers that as an autostart unit, so on a set-up machine there is nothing to remember. Turn it off (verify.interval_minutes: 0) and windows simply expire. Claims then read UNVERIFIED with "last checked N days ago", which is honest but is not the same as noticing.