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:
- the subject resolves to an artifact: a file, an endpoint, a record, a command's output
- the predicate is true or false without judgement
- 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 existsproves 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.
recheckis a promise about freshness;brainlyy verify --watchis what keeps it.brainlyy service installregisters 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.