Releases: askalf/truecopy
Release list
v0.10.4
Fixed
- Concurrent
adds on Windows no longer fail withEPERMon the lock guard.open(wx)on a guard that another process is unlinking at that instant returnsEPERMon Windows (delete-pending), notEEXIST;acquire()treated it as fatal, so one of several simultaneous pins exited 1 while the lock itself was correct (CI run 35579991225, windows-latest).EPERM/EACCES/EBUSYfrom the guard open are now contention and retried likeEEXIST. Because those two codes can also mean a permanent ACL denial, the retry window is bounded: a guard that can be neither created nor stat'ed is retried forwaitMsand then reports the originalEACCES/EPERMinstead of looping forever. Regression tests inject each code, cover a persistent denial and a recovering stat, and hammer one guard from six processes.
Changed
- The README npm and Glama render is the current one. Both mirror the copy
in the published tarball, so the restructured README (proof first, reference
moved todocs/), the hero art and the current agent-security stack
(redstamp · truecopy · plumbline) reach them only with a release. The watch
figure is refreshed to the 2026-09-25 run (314 plugins · 2,442 skills · 0
under review · 475 advisories).
v0.10.3
Security
- Bumped
@askalf/redstamp0.7.3 → 0.7.5. redstamp is truecopy's scanning
engine; 0.7.5 fixed a proven live bypass of its deterministic black gate —
PowerShell's-ArgumentListarray form ('-ExecutionPolicy','Bypass',...)
rated green while the semantically identical space-separated spelling rated
black, because both obfuscation lookaheads assumed whitespace between a flag
and its value (redstamp#124). Averify/scanrun against 0.7.3 could miss
a skill or MCP server using that evasion shape. 0.7.4 (also picked up) fixed
a false positive (writing about a dangerous command scored black) and
closed a gap caught during that same release's own review. Tarball
provenance verified viagh attestation verifybefore install.
v0.10.2
Documentation only — no code, CLI, or detection change. Released so the README
that npm and Glama render is the
current one: both mirror the copy shipped in the published tarball, so a
README-only merge never reaches them.
Fixed
- Corrected the scope of the 2,019-skill audit figure. The hero line read
"the full official Claude Code plugin directory (2,019 skills)", which
overstates what the official directory alone contains. That study covered the
official directory plus nine community marketplaces — as the detection
section further down already said correctly. The daily watch, by contrast, is
official-directory-only, so the two numbers describe different corpora and the
compressed phrasing blurred them. - Version references caught up to the shipped release — the pinned-install
example and thehook installsnippet both still showed0.10.0. The snippet
documents whathook installactually writes, and that command pins the git
ref to the running version, so a stale number there misrepresents real output. - Refreshed the watch figures to the 2026-08-02 run (276 plugins · 1,886
skills · 0 poisoned · 455 advisories · 19 accepted), each stated with its
date so it reads as a snapshot rather than a claim that silently rots, and
linked the live observatory from the watch section as well as the hero.
v0.10.1
Fixed
-
The redstamp dependency is a signed release tarball, not a git pin. It was
github:askalf/redstamp#<sha>, which npm resolves overssh://git@github.com.
npm v12 blocks git dependencies by default,
so once runners ship npm 12 every install route — registry, release
tarball, git — would fail one level down, regardless of how truecopy itself
was fetched. Now pinned to
https://github.com/askalf/redstamp/releases/download/v0.7.3/askalf-redstamp-0.7.3.tgz.Two things this buys beyond npm 12 compatibility:
- The lockfile now carries an
integrityhash. A git dependency has none —
that is what npm'sskipping integrity check for git dependencywarning
meant. A supply-chain gate installing its own scanner without an integrity
check was the wrong shape. - No
ssh://in the dependency graph, so it resolves on runners with no
SSH key.
Not a downgrade: redstamp v0.7.3 is 2 commits ahead of the SHA that was
pinned (0 behind), and contains the five classifier fixes (#107–#111) that the
SHA pin existed to reach in the first place. - The lockfile now carries an
v0.10.0
Added
- Standalone MCP mode — run
truecopy-mcpwith no downstream command and it
serves two read-only tools of its own,truecopy-verifyandtruecopy-status,
instead of exiting. The container image now runs standalone by default (no more
wrapping@modelcontextprotocol/server-everythingjust to have something to
advertise), pre-loaded with the repo's own self-dogfood lock — so an MCP
directory that introspects the image now grades truecopy's own tools. - Per-finding evidence —
scanresults carry anevidencearray
({ flag, text, file, line }) pointing at the exact matched fragment, located
by the detector's own match offset rather than a re-search of the text (more
reliable: a re-search can land on the wrong occurrence of a short match, or
mistake a JSON escape or a name-field hit for the real one). Surfaced in
--json; purely additive. - Per-flag acceptance granularity for the watch (#87) — an accept entry can
name the specific files and flags a human reviewed, rather than the whole
skill; a new flag, or a reviewed file disappearing, still fails closed, and
entries carry a mandatory 90-day expiry. - Publish-time confabulation self-check in the marketplace watch — every
published match is re-verified against the pinned source bytes before
publishing; anything that can't be located is dropped, never published.
Changed
- Bumped the
@askalf/redstamppin — case-sensitiveSECRET_ENV_RE(stops
flagging ordinary lowercase locals as credentials) and a widened
base64-to-shell detector (closes 7 of 12 evasive spellings).
Security
- Docker image hardened — base image pinned by digest, dependency tree
installed from a hash-pinned lockfile, drops root, and moved off a vulnerable
@hono/node-servertransitive (GHSA-frvp-7c67-39w9).
v0.9.0
Added
truecopy check-manifest <file>— compare every installed marketplace
plugin skill against a watch manifest (name → skill hash). An installed skill
whose bytes differ from what the watch scanned isdrifted, a watch-flagged
skill fails even byte-identical, and skills the manifest doesn't know are
unlisted(reported, never fatal). Exit 1 on any failure; takes--json.
Offline: you fetch the manifest, truecopy only reads it.- The weekly marketplace watch publishes
directory-manifest.jsonon the
watchbranch — name → hash for every scanned skill in the official Claude
Code plugin directory, plus the currently-flagged names. This is the manifest
check-manifestconsumes: our standing vetting of the directory, as a
drop-in check for any machine that installs from it. - Per-file acceptance granularity for the watch (#68): accept entries can
key a reviewed-benign finding to the finding-bearing files instead of the
whole-skill hash, so upstream docs churn no longer lapses a review. Skill
dirs now exposescanPieces(per-file scan text) to support subset re-scans;
scan targets, verdicts, and hashes are unchanged.
v0.8.0
Changed
- Default lock/trust filenames are now
truecopy.lock/truecopy.trust
(werecanon.lock/canon.trust), finishing the canon→truecopy rename inside
the tool: the CLI help,truecopy-mcp, and the Claude Code hook all say
truecopy now. Fully back-compatible — with no--lock, an existing
canon.lock(orcanon.trust) is transparently read, so a repo pinned before
the rename keeps verifying with zero changes; only a fresh pin writes the
branded name. Thecanon/canon-mcpbin aliases, the~/.canonglobal trust
store, and theCANON_*env vars are all unchanged. (Unblocks fixing the CI
action, whose docs already documenttruecopy.lock.)
v0.7.0
A security-hardening release following a full adversarial audit of the gate:
several fail-open paths are now fail-closed, plus signature enforcement,
machine-readable output, and lock lifecycle commands.
⚠️ Behavior change —CANON_SIGNING_KEYis sign-only. The CI signing key
is no longer implicitly trusted at verify time; it signs only. If averify
step relied on that implicit self-trust, commit the signing public key to
truecopy.trust(truecopy trust add <pub.pem> --repo), as the docs already
recommend — that is what verification checks the signature against. Local
--signwith a machine key is unchanged.
Added
--require-signedonverifyandguard— opt-in policy that rejects any
pinned entry lacking a valid signature from a trusted key, so a lock
substitution that strips the signature and swaps in other clean-scanning bytes
fails closed instead of verifying green.truecopy remove <name…>(aliasunpin) + libraryunpin()— un-pin a
skill without hand-editing the lock; idempotent and CI-safe.--jsononscan/verify/list/diff— one machine-readable JSON
document on stdout with unchanged exit codes, for dashboards and PR comments.- Detection provenance —
addrecords the detection engine + version in each
lock entry; when a clean-pinned skill re-flags on unchanged bytes,verify
explains it as "same bytes, newer detection" rather than a bare tamper.
Security / Fixed
- MCP gate fails closed on JSON-RPC batches and pre-
tools/listcalls. A
batchedtools/list/tools/callbypassed both gates, and a call before the
first gated list was forwarded unchecked. Both are now blocked. - Symlinks in a skill directory are no longer silently skipped. An in-dir
file symlink is hashed + scanned (poison behind it is caught; a repoint is
drift); an escaping / directory / broken link is pinned by its target string
without being traversed. - Lock hardened against prototype-keyed skill names. A skill named
__proto__/toString/… no longer silently drops onadd(which reported
success while writing nothing) or creates a lock on a no-opremove;verify
no longer throws on a hostileparts: nullentry. - Cross-OS deterministic hashes. Skill directories are hashed in
portable-path order, so the same bytes hash identically on Windows and POSIX
and a committed lock verifies across machines (and a.gitattributespins the
tree to LF). - Strict hook fails closed on an unreadable payload — a malformed hook stdin
no longer allows the skill under--strict. list/diff --jsonemit a JSON error object (not empty stdout) on a
corrupt lock or missing source, andlistno longer crashes on a partial
hand-edited entry.
Changed
CANON_SIGNING_KEYsigns only, not auto-trusted at verify time — see the
behavior-change note above.hook installwrites a version-pinned command
(npx -y github:askalf/truecopy#v<version> …, correct repo name, 20 s timeout)
instead of an unpinned ref refetched on every Skill invocation.
Internal
- Signing tests no longer touch the real OS keychain (fixes the macOS flake and a
contributor-key clobber); CI/workflow hardening (publish restricted to
master, E404-only registry gates, least-privilege checkouts); added CRLF and
UTF-16 decode coverage.