Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
CodeTruss CLI follows semantic versioning. Release artifacts and their SHA-256
checksums are published at <https://codetruss.com/downloads/codetruss-cli-latest.json>.

The current public release is [v0.2.43 on GitHub](https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.43),
The current public release is [v0.2.44 on GitHub](https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.44),
distributed from <https://codetruss.com/downloads/codetruss-cli-latest.json>.
The npm `latest` tag is still
[`@codetruss/cli@0.2.41`](https://www.npmjs.com/package/@codetruss/cli/v/0.2.41):
Expand All @@ -16,6 +16,43 @@ were superseded before distribution.

No unreleased changes.

## 0.2.44 — 2026-08-07

- **The person you hand a receipt to can now check it.** Until this release a
receipt could only be verified by the repository that produced it: `codetruss
verify` measures a receipt against the signing keys the local `.codetruss.yml`
pins, so the client, auditor, or acquirer the evidence was written for got
`receipt signer <fp> does not match trusted key <fp>` and stopped there. That
is most of the point of handing someone a receipt, and it did not work. The
gap was concrete rather than theoretical: publishing one of our own receipts
publicly required shipping a bespoke standalone verifier alongside it, because
the CLI would not check another install's receipt. `codetruss verify-receipt
<receipt.json|dir>` is the supported path. It needs nothing but the files —
no checkout, no account, no configuration — and it reports two claims
separately, because they are two different facts and merging them would be a
lie. **Integrity** is that these bytes have not changed since they were
signed; it is established from the receipt alone, by checking the signature
under the key the receipt carries, reproducing the Markdown byte-for-byte from
the signed JSON, and matching the recorded digests. **Provenance** is that a
party you trust signed them, and it is established only against a
`--public-key` you obtained from that party some other way. A receipt vouching
for its own key proves nothing about who wrote it — forging one takes a
keypair and a minute — so a run without a supplied key can never print a
verified result or exit 0. That ceiling is the feature, not a missing half of
one. The exit codes carry the distinction into scripts: 0 for both claims, 1
for bytes that are intact but unattributed, 2 for bytes that are not what was
signed. When integrity fails, provenance is not evaluated at all and says so,
rather than printing a key match over altered bytes. Evidence a publisher
withheld — usually the patch, the only part of a receipt that quotes source —
is reported as unchecked next to the digest the signature does cover, and the
integrity line names the hole instead of reading clean. `codetruss verify` is
unchanged and still requires a trusted key; its refusal now names the command
that can check a foreign receipt instead of dead-ending. Both paths run one
shared check list against one shared set of accepted Markdown renderings, so
neither can drift into checking less than it claims, and every superseded
profile wording stays reproducible, so receipts signed by older releases keep
verifying byte-for-byte.

## 0.2.43 — 2026-08-07

- **A release can no longer reach you carrying code that does not compile.**
Expand Down
37 changes: 37 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ checksums are published at <https://codetruss.com/downloads/codetruss-cli-latest

## Unreleased

## 0.2.44 — 2026-08-07

- **The person you hand a receipt to can now check it.** Until this release a
receipt could only be verified by the repository that produced it: `codetruss
verify` measures a receipt against the signing keys the local `.codetruss.yml`
pins, so the client, auditor, or acquirer the evidence was written for got
`receipt signer <fp> does not match trusted key <fp>` and stopped there. That
is most of the point of handing someone a receipt, and it did not work. The
gap was concrete rather than theoretical: publishing one of our own receipts
publicly required shipping a bespoke standalone verifier alongside it, because
the CLI would not check another install's receipt. `codetruss verify-receipt
<receipt.json|dir>` is the supported path. It needs nothing but the files —
no checkout, no account, no configuration — and it reports two claims
separately, because they are two different facts and merging them would be a
lie. **Integrity** is that these bytes have not changed since they were
signed; it is established from the receipt alone, by checking the signature
under the key the receipt carries, reproducing the Markdown byte-for-byte from
the signed JSON, and matching the recorded digests. **Provenance** is that a
party you trust signed them, and it is established only against a
`--public-key` you obtained from that party some other way. A receipt vouching
for its own key proves nothing about who wrote it — forging one takes a
keypair and a minute — so a run without a supplied key can never print a
verified result or exit 0. That ceiling is the feature, not a missing half of
one. The exit codes carry the distinction into scripts: 0 for both claims, 1
for bytes that are intact but unattributed, 2 for bytes that are not what was
signed. When integrity fails, provenance is not evaluated at all and says so,
rather than printing a key match over altered bytes. Evidence a publisher
withheld — usually the patch, the only part of a receipt that quotes source —
is reported as unchecked next to the digest the signature does cover, and the
integrity line names the hole instead of reading clean. `codetruss verify` is
unchanged and still requires a trusted key; its refusal now names the command
that can check a foreign receipt instead of dead-ending. Both paths run one
shared check list against one shared set of accepted Markdown renderings, so
neither can drift into checking less than it claims, and every superseded
profile wording stays reproducible, so receipts signed by older releases keep
verifying byte-for-byte.

## 0.2.43 — 2026-08-07

- **A release can no longer reach you carrying code that does not compile.**
Expand Down
23 changes: 18 additions & 5 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,31 @@ dependencies. The shell installers resolve a versioned artifact and verify its
published SHA-256 digest before installation. Every release also includes a
deterministic CycloneDX SBOM, changelog, and security policy.

Deterministic `run`, `review`, `report`, `list`, `metrics`, `init`, `verify`, and hook
`codetruss verify latest` re-checks a receipt against the signing keys this
repository trusts. Anyone you hand a receipt to has no such key, so
`codetruss verify-receipt <receipt.json|dir>` checks it for them, outside any
repository, and reports two claims it never merges: **integrity** — these bytes
have not changed since they were signed — established from the receipt alone, and
**provenance** — a party you trust signed them — established only against a
`--public-key` obtained from that party out of band. A receipt vouching for its
own key proves nothing about who wrote it, so a run without a supplied key exits
`1` (intact but unattributed); altered bytes exit `2`.

Deterministic `run`, `review`, `report`, `list`, `metrics`, `init`, `verify`,
`verify-receipt`, and hook
checks run on-machine without contacting CodeTruss. Installation fetches release
metadata and package bytes from CodeTruss. `auth login` contacts CodeTruss
device/session endpoints but uploads no source, patch, or receipt. `auth status`
contacts the session endpoint to verify the saved credential, and `auth logout`
contacts it to revoke the credential before deleting the local copy; neither
sends source, patches, or receipts. `--llm --provider anthropic|openai|claude`
opts into provider review using your API key or authenticated local Claude Code.
Local receipts identify the 15-pass `local-registry-v3` profile and show hosted
Health scores as N/A. The hosted symbol graph and the full SAST rule pack run
only in the hosted full audit, so the CLI never infers a complete score from its
smaller local pass set.
Local receipts identify the 15-pass `local-registry-v4` profile and show hosted
Health scores as N/A. Its local security pass covers JavaScript, TypeScript, and
TSX, plus Python when the optional grammar pack is installed; each receipt states
which of those actually ran rather than assuming. The hosted symbol graph and the
full SAST rule pack run only in the hosted full audit, so the CLI never infers a
complete score from its smaller local pass set.
CodeTruss supplies a bounded task, reviewed diff prefix, and fixed review schema;
the provider client may add its own runtime instructions or metadata. The receipt
discloses reviewed versus total diff bytes, and truncation prevents `PASS`.
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@codetruss/cli",
"version": "0.2.43",
"version": "0.2.44",
"description": "Local-first scope, quality, and verification receipts for coding agents",
"license": "SEE LICENSE IN LICENSE",
"type": "module",
Expand Down
18 changes: 17 additions & 1 deletion packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { CLI_VERSION } from './version.js'
import { assertLocalEvidencePathsIgnored, ensureLocalEvidenceProtected } from './local-evidence.js'
import { guidedSetup } from './setup.js'
import { runGrammarsCommand } from './grammar-command.js'
import { runVerifyReceiptCommand } from './verify-receipt-command.js'

interface Parsed { command: string; positionals: string[]; values: Map<string, string[]>; booleans: Set<string>; agent: string[] }

Expand All @@ -75,6 +76,7 @@ const COMMAND_OPTION_SCHEMAS: Readonly<Record<string, CommandOptionSchema>> = {
list: { booleans: ['json'], maxPositionals: 0, agent: 'forbidden' },
metrics: { booleans: ['json'], maxPositionals: 0, agent: 'forbidden' },
verify: { maxPositionals: 1, agent: 'forbidden' },
'verify-receipt': { values: ['public-key'], maxPositionals: 1, agent: 'forbidden' },
'verify-policy': { maxPositionals: 1, agent: 'forbidden' },
sync: { booleans: ['dry-run'], maxPositionals: 1, agent: 'forbidden' },
hooks: { maxPositionals: 2, agent: 'forbidden' },
Expand Down Expand Up @@ -312,13 +314,19 @@ Usage:
codetruss setup [--allow GLOB] [--deny GLOB] [--hooks all|pre-commit|claude|codex|none] [--trust-verify] [--yes]
codetruss init [--allow GLOB] [--deny GLOB] [--force]
codetruss verify [id|latest]
codetruss verify-receipt <receipt.json|dir> [--public-key FILE]
codetruss sync [id|latest] [--dry-run]
codetruss auth login|status|logout
codetruss verify-policy [status|trust|trust-key|revoke]
codetruss hooks install|status|doctor|uninstall [pre-commit|claude|codex|all]
codetruss grammars list|status|install|uninstall [python]

Exit codes: PASS=0, REVIEW_REQUIRED=1, FAILED=2, usage/environment=3.`
verify checks a receipt this repository's own trusted key signed. verify-receipt
checks one you were handed: it establishes integrity from the receipt itself, and
provenance only against a --public-key you obtained from the signer out of band.

Exit codes: PASS=0, REVIEW_REQUIRED=1, FAILED=2, usage/environment=3.
verify-receipt: integrity and provenance=0, intact but unattributed=1, not intact=2.`
}

async function executeReview(parsed: Parsed, root: string, liveConfig: CliConfig): Promise<number> {
Expand Down Expand Up @@ -730,6 +738,14 @@ async function main(argv = process.argv.slice(2)): Promise<number> {
if (parsed.command === 'grammars') {
return runGrammarsCommand(parsed.positionals[0] ?? 'status', parsed.positionals[1])
}
// Checking a receipt someone handed you is not a repository operation: the
// reader may hold nothing but the receipt, and requiring a checkout would put
// them back where they started.
if (parsed.command === 'verify-receipt') {
const target = parsed.positionals[0]
if (!target) throw new Error('verify-receipt requires the path to a receipt .json file (or the directory holding it)')
return runVerifyReceiptCommand(target, many(parsed, 'public-key', []))
}
const root = findRepoRoot()
if (parsed.command === 'setup') {
if (parsed.positionals.length || parsed.agent.length) throw new Error('setup does not accept positional arguments or a command after --')
Expand Down
Loading