Skip to content

Boundary terms by digest, so the term itself never has to be stated - #56

Merged
Silentpartnercoding merged 2 commits into
mainfrom
agent/boundary-digest-secret
Aug 9, 2026
Merged

Boundary terms by digest, so the term itself never has to be stated#56
Silentpartnercoding merged 2 commits into
mainfrom
agent/boundary-digest-secret

Conversation

@Silentpartnercoding

Copy link
Copy Markdown
Owner

BL-050 gap 2.

The public blocklist matches by SHA-256 so the file stops naming what it protects — but the digests are themselves public, and a digest of a short guessable phrase falls to a wordlist immediately. Obscurity, not secrecy, as that file already admits.

MP_BOUNDARY_TERMS was the escape hatch and it is not quite sufficient:

Putting plaintext into a repository secret means typing it — into a shell that keeps history, or a transcript that persists. The act of protecting the word states the word.

MP_BOUNDARY_DIGESTS

Takes the hash instead. scripts/add_boundary_term.py reads the term with getpass (no echo, no argv), hashes it in memory, prints only 64 hex characters. Those go in the repository secret.

The term then exists in no file, no git history, no CI configuration, no shell history, no transcript. And because the digest lives in a secret rather than in the public checker, it cannot be wordlisted either — you cannot test guesses against a digest you cannot see.

That closes the honest limit documented in the checker, for the terms that need it closed.

Details

  • The helper refuses a command-line argument — a term in argv is in the shell history and process list before the program starts
  • Refuses terms longer than the matcher’s 3-word n-gram window, rather than returning a digest that silently blocks nothing
  • Malformed secret entries are ignored, not crashed on — a boundary check that crashes is a boundary check that gets disabled
  • Both CI boundary steps receive the secret; absent secret = empty string = no extra terms, so forks and clones are unaffected

Deliberately not done here

The secret is not set. Setting it requires typing the term, and doing that through an agent would recreate the exact leak this prevents. One-command owner action; instructions in the script docstring.

322 tests pass.

claude added 2 commits August 9, 2026 10:21
BL-050 gap 2. The public blocklist matches by SHA-256 so the file stops naming
what it protects, but the digests are themselves public and a digest of a short
guessable phrase falls to a wordlist immediately. Obscurity, not secrecy -- as
that file already says.

MP_BOUNDARY_TERMS was the escape hatch, and it is not quite sufficient for a term
that genuinely must not be recoverable: putting plaintext into a repository secret
means typing it, into a shell that keeps history or a transcript that persists.
The act of protecting the word states the word.

MP_BOUNDARY_DIGESTS takes the hash instead. scripts/add_boundary_term.py reads the
term with getpass -- no echo, no argv, and it refuses a command-line argument
because a term in argv is in the shell history and the process list before the
program starts -- hashes it in memory and prints only 64 hex characters. Those go
in the repository secret.

The term then exists in no file, no git history, no CI configuration, no shell
history and no transcript. And because the digest is in a secret rather than in
the public checker, it cannot be wordlisted either: guesses cannot be tested
against a digest an attacker cannot see. That closes the honest limit documented
in the checker, for the terms that need it closed.

The helper refuses terms longer than the matcher's 3-word n-gram window rather
than handing back a digest that silently blocks nothing, and malformed secret
entries are ignored rather than crashing -- a boundary check that crashes is a
boundary check that gets disabled.

Both CI boundary steps receive the secret; an absent secret is an empty string and
no extra terms, so nothing breaks for a fork or a clone.

NOT DONE HERE, deliberately: the secret is not set. Setting it requires typing the
term, and doing that through an agent would recreate the exact leak this prevents.
Instructions are in the script docstring; it is a one-command owner action.

322 tests pass.
…ing them

A secret input makes this check unreproducible by a third party. A fork runs it
without the secret and can go green where main would go red. That is a real cost
of the digest mechanism and the answer is not to pretend it is absent.

The success line now reports how many vocabulary rules were in force and how many
of them came from outside the repository, and says plainly that the result is not
reproducible without them. A reader learns that an unaudited input existed and can
ask for it. They learn nothing about what it was.

    3 shipped vocabulary rule(s).
    3 shipped vocabulary rule(s) + 2 supplied at runtime and not present in this
    repository, so this result is not reproducible without them.

The count is deliberately not bucketed or rounded. Rounding would be a second,
quieter piece of obscurity, and this line exists to reduce obscurity.

This does not touch any finding. The boundary checker is a publication guard, not
a measuring instrument: no result in LIN-000 or KL-001 depends on its output, and
deleting it entirely would leave every number unchanged. The reproducibility cost
is confined to CI's own verdict, which is exactly why it is worth stating there.

Also corrects the header comment, which still named the plaintext variable as the
answer for terms that must not be recoverable.

323 tests pass.
@Silentpartnercoding
Silentpartnercoding merged commit ce059e7 into main Aug 9, 2026
4 checks passed
@Silentpartnercoding
Silentpartnercoding deleted the agent/boundary-digest-secret branch August 9, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants