v2.5.0
Fettler can now refuse to hand back regulated personal data.
v2.4.0 taught fettle to read Excel and Word and to search inside those
and PDFs — which is to say it taught it to open exactly the formats
health, consumer-report and education records live in. Everything it
returns lands in an assistant's context, and from there in a transcript,
a log, and whatever ships those onward. Nothing inspected that payload on
its way out.
Now a tree can declare a disclosure screen, and any detection in a
screened category refuses the whole response. It is off everywhere until
a tree asks for it, and most trees never will.
Added
-
A tree or a scope may declare
"screen".truescreens all four
categories —phi,pii,legal,sci. A bare list includes and a
-list excludes from the full set, so["phi", "legal"]screens
those two and["-sci"]screens everything else. Mixing the forms in
one list is refused when the file is read, naming the ambiguity:
["phi", "-sci"]reads either as "phi only" or as "everything but
sci", and those differ by two whole categories, so a rule picking one
would silently withhold a screen from somebody who believed they had
asked for it. A misspelt category is refused for the sharper version of
the same reason — it would quietly subtract one. -
A scope's
screenreplaces the tree's, exactly as itscandoes,
which is what lets a scope carve out an anonymised subdirectory. A
scope that says nothing about it inherits, because every scope
written before this existed says nothing, and reading that silence as
"screen nothing here" would punch a hole in the first tree anybody
switched it on for. -
It judges the disclosure, not the file. A document may hold a
record number on page 40; a read of page 2 that discloses none is
served. This is the same choiceSecretsmakes in judging what a write
adds rather than what a file contains, and for the same reason —
judging the whole file locks the very trees people most need help in,
and the way round that is to turn the screen off. Asearchis judged
as the hit records it is about to return, per file, including whatever
--contextbrings with them. -
Tier one needs nothing installed. Structural detectors run in
process with no model and no new dependency: a social security number
with the issuing authority's own invalid ranges excluded, a card that
passes Luhn, a phone number, an email address, and — under their own
labels, because no issuer agrees on a shape — a medical record number
and a date of birth. They are narrow deliberately. Nine bare digits are
an order number far more often than an identifier, and a check that
cries wolf is a check somebody switches off. -
burler, a second executable, hosts the models. The trade's word
for whoever picks the faults out of finished cloth before it leaves the
mill. It is a separate program for one reason: BERT inference needs
ONNX Runtime, which ships native per-RID binaries, and Fettler's
package allowlist admits only pure managed assemblies so thatfettle
can publish self-contained and single-file. The allowlist did not
change, and the test that enforces it did not change. Neither project
references the other in either direction — the line-delimited JSON wire
is the whole contract, and a protocol test on each side asserts it
independently, in theverify-twinsspirit: twins proven equivalent by
test rather than by sharing. -
The sidecar is lazy, warm and bounded. It starts on the first
screened disclosure, is held so a warm answer costs milliseconds rather
than a model load, and is killed once it has been idle — a tool sitting
in an editor all day should not hold hundreds of megabytes for a
document somebody read at breakfast. -
A new
screenedoutcome and exit code 13, besidecredentialand
12. They are mirror images — one is a write going in, the other a
payload coming out — and a script branching on "a secret was involved"
would take entirely the wrong action for each. -
rootsreports the screen where one is on, because a boundary a
caller cannot read is one they can only learn by being refused. -
doctornow reports what the deny list cannot see. The deny list
closes tool names, and two things sit outside any list of names.
2.17 names every other MCP server registered besidefettler—
their tools are not calledRead, so nothingsetupwrites says
anything about what they may reach. It reads the machine in front of
it, so a server installed tomorrow is reported tomorrow, and it names
rather than judges: most of them are wanted. 2.18 reports any tool
the configuration names that thisfettle's inventory does not
know — because check 2.8 saying nothing about a tool reads exactly
like a clean bill of health, and is not one. Every report now prints
the date the inventory was drawn, so a list that has fallen behind
the client is visible instead of silently passing.
Changed
-
The exclusion list gains three names —
Monitor,BashOutputand
TaskOutput— sosetupwrites eleven denies rather than eight.
Monitoris a shell: its own description says it runs the command it
is handed in the same shell environmentBashdoes, so denyingBash
and leaving it open denied the word and not the thing.BashOutputand
TaskOutputare readers, and were missed for being neither an editor
nor a shell — they start nothing and write nothing, but they hand back
the output of work already done, and those bytes reach the model
without passing the tree boundary, the secret scan or the disclosure
screen.TaskOutputsettles it by naming its own alternative:Read
on the task's output file, a tool already on the list. -
build.ps1 test/build.sh testrun both test projects.burler
has its own, so that each side of the pipe asserts the wire without
being able to see the other's types — a shared DTO assembly would make
the two agree by construction, and then the one day they should
disagree nothing would say so. -
build.ps1 publish/build.sh publishproduce two archives per
OS.burleris optional and only wanted by somebody who has switched
the screen on, so folding it intofettle's archive would make every
download pay for ONNX Runtime to get a feature most trees never use.
fettlelooks for it beside itself, so the two unpack into one
directory when both are wanted.
Notes
-
No model weights are distributed by this project, in any archive.
Four quantised models run about 400 MB against afettledownload
measured in single-digit MB. A person fetches the checkpoints they want
and names the directory as"models"— usually in
.fettler.local.json, since where they sit is a fact about a disk
rather than about a project. Each carries amanifest.jsonrecording
the checkpoint, its revision, its licence and where it came from, and
burlerrefuses to start a category without one.That is a licensing position as much as an engineering one. Several
clinical de-identification checkpoints derive from data-use agreements
restricting redistribution, and some capable legal-domain checkpoints
are ShareAlike. Redistributing none of them means no obligation
attaching to their redistribution is incurred. -
Naming that directory is the act that makes the second tier
load-bearing. Before it is named, tier one is the whole screen and a
clean payload is served — nobody has claimed a model is installed, so
there is no second tier to have failed. Once it is named, a screened
category whose model is missing refuses. -
Everything that can go wrong refuses. A sidecar that will not
start, a model that will not load, a malformed answer and an inference
that outran its clock all deny the disclosure and say which. An image
in a screened scope is refused too: nothing here can read one, and a
photographed discharge summary is precisely what the screen exists to
stop. Serving content because the check broke is the one outcome this
must never produce. -
The refusal never repeats what it found. A category and a count. A
refusal naming the patient it found would write that name into the log
and the transcript — the exact harm the screen exists to prevent,
delivered by the screen itself. Where the model found each entity
crosses the pipe and stops there.
Documentation
-
Screening
is a new page: deciding whether you need it, switching on the tier that
needs nothing installed, adding the sidecar, choosing and preparing
models, and proving a refusal refuses. It is the procedure; the
reference behind it is below. -
The disclosure screen
is a new section on the Fettler reference page, with the configuration
forms, what each tier catches, every refusal it can produce, and what
the screen deliberately does not do. The setup page gains a short
section on switching it on, and the exit-code table gains 13. -
Two limits are written down rather than left to be assumed.
FCRA and FERPA are not covered categories — a clinical model knows
clinical text and a scientific one knows scientific text; neither knows
a student transcript or a credit report, and only the structured
identifiers in those regimes are caught. And this is a safety net,
not a boundary: models miss entities, the patterns miss anything
written a way they do not expect, and a number split across a line
break defeats every one of them. A clean verdict is evidence of absence
and never a certificate of it. Anyone who believes a screened tree
cannot leak has been misled, and that belief is the actual hazard.