Skip to content

feat: full-context integrity lock — agents.lock + agents.sum (SPEC-008) - #81

Merged
nmccready merged 1 commit into
mainfrom
feat/008-integrity-lock
Jul 20, 2026
Merged

feat: full-context integrity lock — agents.lock + agents.sum (SPEC-008)#81
nmccready merged 1 commit into
mainfrom
feat/008-integrity-lock

Conversation

@nmccready-tars

Copy link
Copy Markdown
Contributor

Implements the core of SPEC-008 (#73): a full-tree integrity lock for .agents/.

What lands (SPEC-008 PR1 + PR2)

sync-agents lock — writes two committed files at the .agents/ root:

  • agents.lock (provenance, YAML) — one entry per artifact across every bucket: path, bucket, name, origin (local / source:<entry> / linked:<file:path> / imported:<url>), resolved_sha, and the SPEC-003 tree_hash. Path-sorted; generated_at only advances when some other byte changed (no-op runs stay diff-free).
  • agents.sum (integrity, go.sum-style) — one line per file <path> sha256:<hex>, plus a <path> link:<target> line per symlink so a re-pointed link is caught even when contents match. Also sums sources.yaml, sources.lock, config, and agents.lock itself — the sum is the single root of trust.

sync-agents verify — sum + lock + coverage checks with the SPEC-008 severity policy:

  • pulled/local edit → ERROR; unlocked file (bypassed the gate) → ERROR; missing locked artifact → ERROR; hand-edited agents.lockERROR (via its sum line); linked-checkout drift → INFO (ERROR under --strict); swapped symlink → ERROR.
  • Exit codes: 0 clean · 1 drift · 2 usage/IO. No lock on disk → friendly no-op (exit 0).
  • --json (stable {status, findings, counts} schema), --strict, --explain <path>.

Coverage includes OS-scoped subdirs (macos/…) regardless of host GOOS, and excludes .quarantine/, .sources/, origin metadata, and unshared STATE_*.md (honors shared: true).

Tests

New internal/agent/integrity_test.go covers: determinism, ordering, all four origin classes, exclusions, OS subdirs, sum-includes-lock/sources, clean verify, local/pulled edit, unlocked file, missing artifact, tampered lock, no-lock no-op, JSON schema, --explain, and the full linked-source flow (linked origin, INFO-then-strict drift, re-pointed link). Package coverage ~73% (gate 70%).

Verified end-to-end against a real tree (lockverify clean → tamper → verify exit 1 → --json--explain).

Remaining (not in this PR)

Refs #73

🤖 Generated with Claude Code

…k/verify (SPEC-008)

Add `sync-agents lock` and `sync-agents verify`: a whole-tree integrity
lock covering every artifact in every bucket, closing the provenance +
tamper-detection gap that sources.lock leaves for local/imported/linked
and non-source buckets.

- agents.lock (provenance, YAML): one entry per artifact with path,
  bucket, name, origin (local | source:<entry> | linked:<file:path> |
  imported:<url>), resolved SHA, and SPEC-003 tree_hash. Path-sorted for
  minimal diffs; generated_at only advances when other bytes change.
- agents.sum (integrity, go.sum-style): one line per file
  `<path> sha256:<hex>`, plus `<path> link:<target>` for symlinks so a
  re-pointed link is caught. Sums sources.yaml/sources.lock/config and
  agents.lock itself (the sum is the single root of trust).
- verify: sum + lock + coverage checks with the SPEC-008 severity
  policy (pulled/local edit = ERROR, unlocked file = ERROR, hand-edited
  lock = ERROR, linked drift = INFO/--strict ERROR, swapped symlink =
  ERROR). Exit 0 clean / 1 drift / 2 usage-IO; no lock = friendly no-op.
  --json stable schema, --strict, --explain <path>.
- Coverage honors OS-scoped subdirs regardless of host GOOS and excludes
  .quarantine/.sources/origin-metadata/unshared STATE_*.md.
- ExitError type + main wiring for distinct process exit codes.
- Docs: docs/integrity.md + README feature bullet.

Implements SPEC-008 PR1 (writer + lock) and PR2 (verify). Auto-refresh
of the lock from mutating commands (pull/update/approve/detach/add/
import/remove) is the remaining PR3 and is tracked in #73.

Refs #73

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🤖 AI Code Review

The diff introduces a full-context integrity lock feature with lock and verify commands. The implementation has a critical security vulnerability in symlink handling that allows including arbitrary files outside the agents tree, and a minor correctness bug when reading a symlink fails.

Findings: 1 critical · 0 warning · 0 suggestion

Findings not anchorable to a diff line:

  • 🔴 critical internal/agent/integrity.go — Symlink traversal in addArtifact and fileSums: a symlink inside a bucket directory can point to an arbitrary file or directory outside the .agents/ tree (e.g., /etc/passwd or /home/user/.ssh). The code resolves the symlink and includes the target's content in agents.sum and agents.lock, and verify will read those external files. This leaks information about external files and allows an attacker to include arbitrary content in the lock. Restrict symlink targets to within the project root or validate that the resolved path is under the agents directory.

model: deepseek/deepseek-v4-flash · est. cost: $0.0035

@nmccready
nmccready merged commit 24f44ea into main Jul 20, 2026
6 checks passed
@nmccready
nmccready deleted the feat/008-integrity-lock branch July 20, 2026 06:06
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