Skip to content

v2.5.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 22:23
· 17 commits to main since this release

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". true screens 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 screen replaces the tree's, exactly as its can does,
    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 choice Secrets makes 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. A search is judged
    as the hit records it is about to return, per file, including whatever
    --context brings 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 that fettle
    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 the verify-twins spirit: 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 screened outcome and exit code 13, beside credential and
    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.

  • roots reports the screen where one is on, because a boundary a
    caller cannot read is one they can only learn by being refused.

  • doctor now 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 beside fettler
    their tools are not called Read, so nothing setup writes 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 this fettle'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, BashOutput and
    TaskOutput — so setup writes eleven denies rather than eight.

    Monitor is a shell: its own description says it runs the command it
    is handed in the same shell environment Bash does, so denying Bash
    and leaving it open denied the word and not the thing. BashOutput and
    TaskOutput are 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. TaskOutput settles it by naming its own alternative: Read
    on the task's output file, a tool already on the list.

  • build.ps1 test / build.sh test run 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 publish produce two archives per
    OS.
    burler is optional and only wanted by somebody who has switched
    the screen on, so folding it into fettle's archive would make every
    download pay for ONNX Runtime to get a feature most trees never use.
    fettle looks 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 a fettle download
    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 a manifest.json recording
    the checkpoint, its revision, its licence and where it came from, and
    burler refuses 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.