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
14 changes: 13 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.62 on GitHub](https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.62),
The current public release is [v0.2.63 on GitHub](https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.63),
distributed from <https://codetruss.com/downloads/codetruss-cli-latest.json>.
npm publication is a separate, manually dispatched step, so the npm `latest`
tag can trail the website and the GitHub release; the dispatch for this
Expand All @@ -13,6 +13,18 @@ were superseded before distribution.

## Unreleased

## 0.2.63 — 2026-08-09

- **`codetruss sync` no longer relabels who produced a receipt.** The sync
envelope used to overwrite the receipt's signing identity with whatever key
ran the sync, so a teammate exporting your receipt made the hosted record
claim they signed it. The producer's public key and fingerprint now travel
unchanged, and the exporting key is named separately in
`evidence.exporter`; the envelope signature stays the exporter's. The
hosted side pins API credentials to the exporting key (the machine actually
syncing) and displays the producer as the signer, with the exporter shown
when the two differ.

## 0.2.62 — 2026-08-09

- **The comment analyzer learned the difference between deferring with a
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ checksums are published at <https://codetruss.com/downloads/codetruss-cli-latest

## Unreleased

## 0.2.63 — 2026-08-09

- **`codetruss sync` no longer relabels who produced a receipt.** The sync
envelope used to overwrite the receipt's signing identity with whatever key
ran the sync, so a teammate exporting your receipt made the hosted record
claim they signed it. The producer's public key and fingerprint now travel
unchanged, and the exporting key is named separately in
`evidence.exporter`; the envelope signature stays the exporter's. The
hosted side pins API credentials to the exporting key (the machine actually
syncing) and displays the producer as the signer, with the exporter shown
when the two differ.

## 0.2.62 — 2026-08-09

- **The comment analyzer learned the difference between deferring with a
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.62",
"version": "0.2.63",
"description": "Local-first scope, quality, and verification receipts for coding agents",
"license": "SEE LICENSE IN LICENSE",
"type": "module",
Expand Down
11 changes: 9 additions & 2 deletions packages/cli/src/receipt-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,17 @@ export async function createSyncEnvelope(receipt: Receipt): Promise<SyncEnvelope
else delete synced.analyzers.rejectedSuppressions
}
synced.verifications = synced.verifications.map((item) => ({ ...item, command: '[redacted for sync]', output: '' }))
// The producer's signing identity stays exactly as the receipt recorded it;
// the exporting key signs the envelope and is named separately. Overwriting
// the producer fields here (the old behavior) relabeled a teammate's receipt
// as whoever ran `codetruss sync`.
const producer = receipt.evidence.publicKey && receipt.evidence.keyFingerprint
? { publicKey: receipt.evidence.publicKey, keyFingerprint: receipt.evidence.keyFingerprint }
: { publicKey: key.publicKey, keyFingerprint: key.fingerprint }
synced.evidence = {
patchSha256: receipt.evidence.patchSha256,
publicKey: key.publicKey,
keyFingerprint: key.fingerprint,
...producer,
exporter: { publicKey: key.publicKey, keyFingerprint: key.fingerprint },
}
synced.coverageNotes = [
...synced.coverageNotes,
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ export interface Receipt {
coverageNotes: string[]
verdict: Verdict
reasons: string[]
evidence: { markdownSha256?: string; patchFile?: string; patchSha256?: string; signatureFile?: string; publicKey?: string; keyFingerprint?: string }
/** `exporter` appears only on hosted-sync copies: the key that signed the
* sync envelope, kept separate so exporting a teammate's receipt can never
* relabel who produced it (`publicKey`/`keyFingerprint` stay the producer's). */
evidence: { markdownSha256?: string; patchFile?: string; patchSha256?: string; signatureFile?: string; publicKey?: string; keyFingerprint?: string; exporter?: { publicKey: string; keyFingerprint: string } }
}

export interface ReviewOptions {
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/test/receipt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,10 @@ describe('signed receipts', () => {
suggestion: 'Extract [redacted unrelated path] and src/changed.ts into one module.',
filePath: 'src/changed.ts', line: 3, impactScore: 55, effort: 'medium',
}])
expect(Object.keys(synced.evidence).sort()).toEqual(['keyFingerprint', 'patchSha256', 'publicKey'])
expect(verifyBytes(envelope.signedReceipt, synced.evidence.publicKey!, envelope.signature)).toBe(true)
expect(Object.keys(synced.evidence).sort()).toEqual(['exporter', 'keyFingerprint', 'patchSha256', 'publicKey'])
// The envelope is signed by the exporting key, named separately so the
// producer identity fields cannot be relabeled by whoever runs the sync.
expect(verifyBytes(envelope.signedReceipt, synced.evidence.exporter!.publicKey, envelope.signature)).toBe(true)
expect(envelope.signedReceipt).not.toContain('secret prompt')
expect(envelope.signedReceipt).not.toContain('sensitive output')
expect(envelope.signedReceipt).not.toContain('"command": "test"')
Expand Down
170 changes: 170 additions & 0 deletions public/downloads/codetruss-cli-0.2.63.sbom.cdx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"$schema": "https://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"serialNumber": "urn:uuid:5587fc86-bda2-5368-8379-3f323f53b318",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "application",
"bom-ref": "pkg:npm/%40codetruss/cli@0.2.63",
"name": "@codetruss/cli",
"version": "0.2.63",
"description": "Local-first scope, quality, and verification receipts for coding agents",
"licenses": [
{
"license": {
"name": "CodeTruss CLI Proprietary License"
}
}
],
"purl": "pkg:npm/%40codetruss/cli@0.2.63"
},
"properties": [
{
"name": "codetruss:distribution",
"value": "single-file JavaScript bundle"
},
{
"name": "codetruss:runtimeDependencies",
"value": "0"
}
]
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/%40codetruss/analyzer-engine@0.1.0",
"name": "@codetruss/analyzer-engine",
"version": "0.1.0",
"licenses": [
{
"license": {
"name": "CodeTruss CLI Proprietary License"
}
}
],
"purl": "pkg:npm/%40codetruss/analyzer-engine@0.1.0",
"properties": [
{
"name": "codetruss:bundled",
"value": "true"
}
]
},
{
"type": "library",
"bom-ref": "pkg:npm/balanced-match@4.0.4",
"name": "balanced-match",
"version": "4.0.4",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"purl": "pkg:npm/balanced-match@4.0.4",
"properties": [
{
"name": "codetruss:bundled",
"value": "true"
}
]
},
{
"type": "library",
"bom-ref": "pkg:npm/brace-expansion@5.0.9",
"name": "brace-expansion",
"version": "5.0.9",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"purl": "pkg:npm/brace-expansion@5.0.9",
"properties": [
{
"name": "codetruss:bundled",
"value": "true"
}
]
},
{
"type": "library",
"bom-ref": "pkg:npm/minimatch@10.2.6",
"name": "minimatch",
"version": "10.2.6",
"licenses": [
{
"license": {
"id": "BlueOak-1.0.0"
}
}
],
"purl": "pkg:npm/minimatch@10.2.6",
"properties": [
{
"name": "codetruss:bundled",
"value": "true"
}
]
},
{
"type": "library",
"bom-ref": "pkg:npm/yaml@2.9.0",
"name": "yaml",
"version": "2.9.0",
"licenses": [
{
"license": {
"id": "ISC"
}
}
],
"purl": "pkg:npm/yaml@2.9.0",
"properties": [
{
"name": "codetruss:bundled",
"value": "true"
}
]
}
],
"dependencies": [
{
"ref": "pkg:npm/%40codetruss/analyzer-engine@0.1.0",
"dependsOn": []
},
{
"ref": "pkg:npm/%40codetruss/cli@0.2.63",
"dependsOn": [
"pkg:npm/%40codetruss/analyzer-engine@0.1.0",
"pkg:npm/minimatch@10.2.6",
"pkg:npm/yaml@2.9.0"
]
},
{
"ref": "pkg:npm/balanced-match@4.0.4",
"dependsOn": []
},
{
"ref": "pkg:npm/brace-expansion@5.0.9",
"dependsOn": [
"pkg:npm/balanced-match@4.0.4"
]
},
{
"ref": "pkg:npm/minimatch@10.2.6",
"dependsOn": [
"pkg:npm/brace-expansion@5.0.9"
]
},
{
"ref": "pkg:npm/yaml@2.9.0",
"dependsOn": []
}
]
}
Binary file added public/downloads/codetruss-cli-0.2.63.tgz
Binary file not shown.
1 change: 1 addition & 0 deletions public/downloads/codetruss-cli-0.2.63.tgz.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cc63fdbc491b2ef7ac579bd721488e4a876287930995a0b347b74024180be748 codetruss-cli-0.2.63.tgz
14 changes: 7 additions & 7 deletions public/downloads/codetruss-cli-latest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@codetruss/cli",
"version": "0.2.62",
"url": "/downloads/codetruss-cli-0.2.62.tgz",
"version": "0.2.63",
"url": "/downloads/codetruss-cli-0.2.63.tgz",
"latestUrl": "/downloads/codetruss-cli-latest.tgz",
"sha256": "35ad8c84288014ced4a184e702480bd478310e9c990fe18498c0e1038388e8a9",
"sbomUrl": "/downloads/codetruss-cli-0.2.62.sbom.cdx.json",
"sbomSha256": "4d5b7fb2eeaf1a17bb9f9891161b098f5a6dffd53fa7650c30de045d7c456284",
"sha256": "cc63fdbc491b2ef7ac579bd721488e4a876287930995a0b347b74024180be748",
"sbomUrl": "/downloads/codetruss-cli-0.2.63.sbom.cdx.json",
"sbomSha256": "6e89ef8f91c0d0cdc893845876af3b1fa225744a89f85c2eac2f5263c7d6dc4a",
"node": ">=20.9.0",
"repository": "https://github.com/CodeTruss/codetruss-cli",
"releaseUrl": "https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.62",
"attestationCommand": "gh attestation verify codetruss-cli-0.2.62.tgz --repo CodeTruss/codetruss-cli"
"releaseUrl": "https://github.com/CodeTruss/codetruss-cli/releases/tag/v0.2.63",
"attestationCommand": "gh attestation verify codetruss-cli-0.2.63.tgz --repo CodeTruss/codetruss-cli"
}
10 changes: 5 additions & 5 deletions public/downloads/codetruss-cli-latest.sbom.cdx.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"$schema": "https://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"serialNumber": "urn:uuid:4db60289-e4d1-5336-82e9-d4493a35b1d1",
"serialNumber": "urn:uuid:5587fc86-bda2-5368-8379-3f323f53b318",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "application",
"bom-ref": "pkg:npm/%40codetruss/cli@0.2.62",
"bom-ref": "pkg:npm/%40codetruss/cli@0.2.63",
"name": "@codetruss/cli",
"version": "0.2.62",
"version": "0.2.63",
"description": "Local-first scope, quality, and verification receipts for coding agents",
"licenses": [
{
Expand All @@ -18,7 +18,7 @@
}
}
],
"purl": "pkg:npm/%40codetruss/cli@0.2.62"
"purl": "pkg:npm/%40codetruss/cli@0.2.63"
},
"properties": [
{
Expand Down Expand Up @@ -139,7 +139,7 @@
"dependsOn": []
},
{
"ref": "pkg:npm/%40codetruss/cli@0.2.62",
"ref": "pkg:npm/%40codetruss/cli@0.2.63",
"dependsOn": [
"pkg:npm/%40codetruss/analyzer-engine@0.1.0",
"pkg:npm/minimatch@10.2.6",
Expand Down
Binary file modified public/downloads/codetruss-cli-latest.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion public/downloads/codetruss-cli-latest.tgz.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
35ad8c84288014ced4a184e702480bd478310e9c990fe18498c0e1038388e8a9 codetruss-cli-latest.tgz
cc63fdbc491b2ef7ac579bd721488e4a876287930995a0b347b74024180be748 codetruss-cli-latest.tgz