Repository navigation
chamnan 1.35.0 — protection and context that hold in real-world text
This release makes chamnan's protection hold in the shapes real text actually takes — commands in shell history, infrastructure state, keys cut off mid-file, names typed in another Unicode form — and makes its heavier steps cheap enough to stop noticing, rather than adding new features.
| What was measured | Before | After |
|---|---|---|
| Refreshing a 584,000-character map after a one-file edit (scrub step) | 5.9 s | 0.02 s |
| Editing a requirements.txt with 200 past revisions (removed-package check) | 18.9 s | 0.15 s |
Scrubbing one line of 8,000 repeated key- |
27 s | 0.94 s |
chamnan-recall median query |
90-100 ms | 52-55 ms |
curl -u user:PASS, mysql -pPASS, sshpass -p, docker login -p, ConvertTo-SecureString '…' -AsPlainText |
printed | redacted |
A private key cut off before its END line (a capped read, head, a truncated paste) |
body printed | redacted |
Terraform plan variables and state outputs ("db_password": {"value": …}) |
printed | redacted |
chamnan-impact café.py typed in the other Unicode form than the file was created in |
"nothing recorded" | found |
| An instruction file that quotes one path outside the repository | drift check silently off for the file | checked |
Together they say one thing: the protection and the context hold in the shapes text really arrives in, and the steps that ran on every edit no longer cost seconds.
What you will notice
- Secrets in commands are caught. A password passed as a flag or positional to
curl,mysql,sshpass,docker/helm login,redis-cli,smbclient,htpasswdor PowerShell'sConvertTo-SecureStringis redacted — the shapes shell history and pasted commands carry.ssh -p 2222,mkdir -pand$VARreferences are left alone. - Private keys are redacted in every form tried, including a PEM block cut before its end, a PuTTY
.ppk, and a JSON Web Key's private members. Public keys and certificates are untouched. - Terraform plans and state are safer to map. A value Terraform stores under a secret-named object — every input variable in a plan, every output in a state,
sensitiveor not — is redacted. - Accented and non-Latin file names are found however they were typed. A file created with a decomposed name (as macOS keeps some) is found by
chamnan-impact, the file pointer and the history join whether you type it composed or decomposed. - The map refreshes quickly after a small edit, and editing a dependency manifest with a long history no longer stalls.
chamnan-recallfinds a note whatever the case or accents, and the automatic pointer stops offering notes that match only common words.- Repeated advice is capped at two notices a session per kind, and
AGENTS.mdis no longer rewritten by the pre-commit hook when nothing it describes changed.
Most of the rest is deliberately invisible on the happy path; its purpose is to make unusual text and unusual setups fail less often.
Under the hood
Security
- Command-anchored rules for passwords given as flags or positionals; a bare
-p(a port, or "parents" to most tools) is never touched. - A PEM block with no
ENDline is redacted through its body, stopping at the first line that is not key material; PuTTYPrivate-Lines:and JWKd/p/q/dp/dq/qi/kinside an object withkty. - The
"value"member inside a secret-named object; header-style names (X-Api-Key:) open the list rules; a secret split by a shell line continuation is redacted on both lines; stripping invisible characters can no longer reassemble a credential; an uncaught traceback is scrubbed before it reaches stderr. - The commit guard also guards commits made through the PowerShell tool, scans every staged file git calls binary (including BOM-less UTF-16 and the string literals of
.pycfiles), says so when it runs out of time, and names the commits only the reflog still holds. - Injected lines lose Unicode tag characters and invisible operators; a commit subject quoted to the agent is fenced as repository text.
Performance
- The map re-scrubs only the sections an edit changed (5.9 s → 0.02 s on a 584,000-character map).
- A manifest's history is read with one batched git process (18.9 s → 0.15 s on a 200-revision fixture).
- Every secret-word name run is bounded, so a long value after a secret word scrubs in linear time (8,000 characters of
key-: 27 s → 0.94 s). - Recall stems each query term once (90-100 ms → 52-55 ms, identical results);
subprocessis no longer imported by the two hooks that run on every Bash call; the session start readsHEADoff.gitinstead of spawning git.
Reliability
- One quoted path outside the repository no longer switches off the instruction-file drift check for the whole file.
- Locks record their host, so another machine's live lock is not broken; a sync client's conflict copies are not read as notes; git's "dubious ownership" refusal and the file git will not keep are named with their fixes.
- A Python file chamnan cannot parse keeps its docstring and top-level names; a source file that declares a legacy encoding keeps its summary; a capped preview never ends in half a character.
- Shallow clones, sparse checkouts and linked worktrees of shallow clones are recognised as what they are.
Cross-platform
- Path lookups compare both sides in NFC and answer with the spelling on disk; an NFD-named module keeps its used-by edge; non-ASCII commit text arrives intact whatever
i18n.logOutputEncodingsays; a stamped date ages by the calendar beyond UTC+12.
Context and knowledge
- Recall folds case and Latin, Greek and Cyrillic accents (Thai untouched), drops words most notes share from scoring (MRR 0.727 → 0.780 on title words), and rebuilds an index scrubbed by an older redactor.
- The map honours AI-ignore files and lists the test and build commands a repository declares;
chamnan-impactshows why a file last changed;chamnan-wheresees__all__, entry points and Thai or differently normalised identifiers.
What we noticed on the way
- A redaction that announced itself and missed. When a key file was read only in part, the redactor replaced the
BEGINline and printed the body under it — a<REDACTED>saying the key had been handled, over the key. Every capped reader in the package could produce that input. - A drift check one path could turn off. The instruction-file check asks git about every quoted path in one call, and git refuses the whole call if any path lies outside the repository. Instruction files routinely quote
/var/log/…or../shared/…, so the check reported nothing for exactly the files people maintain most. - The same file under two names. APFS keeps a name in the form it was created; git and the keyboard hand over the other. Each was internally consistent, and the lookup between them compared bytes.
Research
Every research finding screened for this release ends in a verdict — tested, already there, or not worth it — and the tests led to most of the fixes above. The citations from code back to rounds are in the attached INDEX_CITED_IN_CODE.md; python3 .chamnan/tools/research_citations.py --write regenerates it.
Measured and not adopted: starting hooks with python3 -S — 1.5 ms of a 120 ms hook, within noise; shipping chamnan as a single zipapp — the outside measurement found a warm zipapp 2.76× slower than a directory layout; a near-duplicate detector for notes — on a real store of a few dozen notes no pair came within reach of any published threshold; indexing notebook outputs — a 400 KB notebook's base64 never reaches the map, which lists notebooks as unindexed source.
Verification
| Checks | Gotcha |
|---|---|
| 6746/6746 | 152/2,395 — 152 recorded since v1.34.0, 2,395 in total |
6746/6746 on the release gate (macOS), and CI green on all five legs — macOS · Linux · Windows on Python 3.8 and 3.13 — on the same commit. The corpus gate (608 files, 98 extensions, 7 writing systems) planted credentials and found 0 in the index; redaction checks 52/52. The count went from 5,401 to 5,618 check( calls in the source: 68 sections added and three shortened by six calls, where functions that only their own tests called were removed.
Known limitations
- The command-flag rules cover the named tools; a password passed positionally to a tool not on the list is not recognised by shape.
- The redactor still misses a bare AWS secret access key with no name beside it (98 of 99 corpus shapes caught), and a kubeconfig
token:with a random value. - Notebooks are listed as unindexed source; their code cells are not read.
- Windows behaviour is verified on CI, not on a Windows machine here.
Upgrade
/plugin update chamnan. Nothing to do afterwards; a recall index built before this is rebuilt on the next chamnan-recall --reindex.
This release is about protection and context holding in the shapes text really arrives in.