Skip to content

fix(security): remote-upgrade doc drift + fail-closed throttle (#321)#324

Merged
VijitSingh97 merged 1 commit into
developfrom
claude/321-security-doc-drift
Jul 19, 2026
Merged

fix(security): remote-upgrade doc drift + fail-closed throttle (#321)#324
VijitSingh97 merged 1 commit into
developfrom
claude/321-security-doc-drift

Conversation

@VijitSingh97

Copy link
Copy Markdown
Contributor

Closes #321. Stacked on #323 (the #320 status-contract PR) — merge that first; GitHub will retarget this to develop automatically.

Found reviewing v1.11.2's control-upgrade as the pithead#597 consumer. The security posture is settled (ADR 0002 D5, signed off as D-OPEN-2); the bug was that the docs and one ADR claim didn't match the shipped code, plus one real posture fix.

What changed

  1. SECURITY.md describes the verification the code actually performs. It claimed the remote-upgrade path is verified "by SHA256SUMS hash only" — no checksum is fetched or checked on that path, by design. The real anchor is now stated: git checkout of the release tag over TLS to GitHub, origin/main ancestry (D10), immutable releases locking the tag→commit binding. Checksums play no role there.
  2. ADR 0002 D8 amended in place. "A flock serializes concurrent triggers" overstated it — the only flock guards the D6 throttle stamp and is released before the build. The amendment names the real serialization (systemd Type=oneshot + newest-wins spool draining) and the upgrade path if that's ever not enough (verb-scoped lock spanning fetch/build).
  3. The anti-beacon throttle fails CLOSED. _control_upgrade_throttle_ok used to proceed unthrottled (with a warn) when it couldn't take its lock — for a guard whose purpose is bounding how often a compromised trigger can make the rig dial out, fail-open inverted the posture on exactly the degraded box an attacker might arrange. It now returns rc 2 and the verb refuses with a failed status naming the throttle state — deliberately distinct from throttled (which means retry-later) so a consumer doesn't poll forever against a broken rig.

Testing

  • The fail-open unit test flipped to fail-closed (rc 2), using a dangling-symlink lock so the open fails for any uid — root included — unlike the previous chmod-based setup.
  • New caller-level assert: unusable throttle state → failed with a reason naming the throttle state, never throttled.
  • Full suite: 1494 passed, 0 failed. make lint + make lint-md clean.

🤖 Generated with Claude Code

@VijitSingh97
VijitSingh97 force-pushed the claude/321-security-doc-drift branch from 36ca23d to 7bcdb9c Compare July 19, 2026 20:00
Base automatically changed from claude/320-upgrade-status-contract to develop July 19, 2026 20:05
Found reviewing v1.11.2 as the pithead#597 consumer — on a security
surface, docs-vs-code drift is the bug.

- SECURITY.md claimed the remote-upgrade path is verified "by SHA256SUMS
  hash only"; it never was. The real anchor (ADR 0002 D5, per-spec in
  the code) is a git checkout of the release tag over TLS to GitHub,
  origin/main ancestry, and immutable releases — checksums play no role
  on that path. The doc now states that.
- ADR 0002 D8 claimed "a flock serializes concurrent triggers"; the
  only flock guards the D6 throttle stamp, released before the build.
  Amended in place to name the real mechanism (Type=oneshot + newest-
  wins spool draining) and the upgrade path if it's ever not enough.
- _control_upgrade_throttle_ok fails CLOSED (rc 2) when it can't take
  its lock: the guard bounds how often a compromised trigger can make
  the rig dial out, and fail-open inverted that on exactly the degraded
  box an attacker might arrange. The caller reports a failed status
  naming the throttle state — distinct from throttled, so a consumer
  doesn't retry-later forever against a broken rig.

Tests: the fail-open unit test flipped to fail-closed (rc 2, via a
dangling-symlink lock that defeats any uid), plus a caller-level assert
that the status is failed with the real cause, never "throttled".

Closes #321

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@VijitSingh97
VijitSingh97 force-pushed the claude/321-security-doc-drift branch from 7bcdb9c to e930366 Compare July 19, 2026 20:05
@VijitSingh97
VijitSingh97 merged commit 9a02490 into develop Jul 19, 2026
9 checks passed
@VijitSingh97
VijitSingh97 deleted the claude/321-security-doc-drift branch July 19, 2026 20:09
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.

Security-doc drift on the remote-upgrade trust model + two ADR-vs-code mismatches

1 participant