Skip to content

feat(infra): bind the release-recorded probe runner binary and per-language probe images (B5 PR3b) - #1934

Open
ai-mountain wants to merge 19 commits into
agent/coding-native-host-approvalfrom
agent/coding-native-probe-binding
Open

ai-mountain wants to merge 19 commits into
agent/coding-native-host-approvalfrom
agent/coding-native-probe-binding

Conversation

@ai-mountain

@ai-mountain ai-mountain commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

B5 PR 3b, stacked on #1933. It binds the enforcement probe runner and the probe images. Peyton's decisions:

  • "In PR 3, bind the exact release-recorded probe-runner binary digest. Retain the source-tree hash and source revision as supporting provenance, but runtime acceptance must pin the binary that actually ran."
  • "'Every language image' means every image named by the approved profile, using the same approved limits/timeouts but each language's canonical, explicitly recorded test command. Rust-specific arguments are fine only when pinned in the profile/approval and verified; do not force a false common argv."

Default-off. It adds no host workflow, collection, approval or activation.

Status: both parts are pushed.

Stack update (2026-09-16, a41dabdfc)

Merged #1933 556cc9cc4 (Peyton's 2026-09-16 approval decisions: curator signature verified over the exact stored approval bytes with strict, non-canonical parsing; Docker server_version observed rather than enforced on the host; approval validity capped at 24 h). One README table conflict resolved (kept this PR's enforcement_images_sha256 pin plus #1933's wording). No other fallout; the enforcement-images set and release index remain canonical documents. Tests: native Python suites (evidence, preflight, matrix, release, probe runner, probe workflow, qualification) 548 passed, 2 skipped; go test -p 2 ./internal/codingenforcement/... ./cmd/dittobench-coding-enforcement-probe/ ok; ruff, format, mypy, gofmt clean.

Review update (2026-09-15): independent adversarial review

Rebased onto #1933 9b7fb9902. The fixes:

  • Rust test commands are verified, not just pinned (9666d0f12). The image set accepted any bounded Rust argv. Its vector, test fixture and CI template pinned --crate subject commands that codingexecutor.rustCommand refuses.
    • Go (catalog.RustTestArgv) and Python now require each Rust test command to be the driver's authority command: dittobench-test-driver --group <its own group> --authority <relative .json> --authority-sha256 <hex>.
    • A new codingexecutor test checks the offline check against rustCommand on the shared vector, which gains nine Rust refusal cases.
    • A common argv is refused for Rust.
  • check-approval checks approval images against the release index (0edebfb42). It compared only approval_sha256. The host's release_policy also requires image_ref, config_digest and driver_profile to equal the index, so a mismatched approval passed offline and was refused on-host.
  • Tests that passed without their guard (b45616087).
    • Removing the record-to-release-index check still passed, because the preflight check names the same field.
    • Removing the probe runner measurement check still passed, because a later image lookup also failed.
    • Both tests now assert the exact refusal.
  • Docs (62b7ee2ea). /proc/self/exe is a self-report, and the verifier can't tell it from a collector measurement. PR4/PR5 collectors must hash the file they execute from outside the runner and refuse a differing self-report.
  • Tests:
    • Python native, probe and workflow suites: 656 passed, 3 skipped.
    • Go: go test -p 2 on codingenforcement/..., codingexecutor, codinghostedworker and the probe command. vet is clean, including -tags native_probe_integration.

Part 1: probe runner binary binding

  • Release. The native runtime bundle builds bin/dittobench-coding-enforcement-probe with the worker's static flags. It smoke-checks the binary (no arguments exits 1 with its usage error) and requires it as an executable amd64 ELF. The release set (now dittobench-coding-native-release-set-v3) records runtime.probe_runner_sha256 from the bundle manifest. native.release_policy accepts v3 only.
  • Measured on host. The runner hashes /proc/self/exe, the executable image actually running, and reports probe_runner_binary_sha256. The rootless CI job compares it with an independent sha256sum of the binary it built.
  • Evidence. The ambiguous tools.runner_sha256 is now tools.probe_runner_source_sha256: supporting provenance, still computed from the reviewed checkout. New tools.probe_runner_binary_sha256 must equal the release index's runtime.probe_runner_sha256.
    • verify, review and check-approval require --release-index. Records must name the index's exact digest, and their source revision, runtime archive and image approvals must match it.
    • approval.runner_sha256 still names only run.py.
  • Refusal tests:
    • an operator-built binary, and the source hash or runtime archive passed off as the binary;
    • the old runner_sha256 key;
    • index drift, and record release fields that differ from the index;
    • a v2, non-canonical or malformed index, and a probe runner digest equal to the worker's;
    • a missing --release-index, and a missing, non-executable or non-ELF probe binary in the bundle.

Part 2: per-language probe images and commands

  • Image set. dittobench-coding-native-enforcement-images-v1 is canonical, with closed keys.
    • It binds grading_profile_sha256: the approved profile whose limits, timeouts, command IDs and expected totals every language shares.
    • It names go, node, python and rust exactly once. Each has a distinct OCI manifest image_digest, its own build_argv, and its own test_argv for hidden and visible.
    • Every argv is 1 to 64 printable arguments with a bare non-shell executable, and test commands must use dittobench-test-driver. Rust arguments such as --crate or --out-dir are fine because the set records them.
    • Go (catalog.ParseEnforcementImages) and Python parse one shared vector with 16 refusal cases.
  • Manifest image digest is bound. GradingProfile.EnforcementProbeManifest now takes the pinned language entry. It applies that language's commands, keeps the profile's limits, timeouts, IDs and totals, never mutates the approved profile through shared slices, and refuses a manifest whose image differs from the pinned one.
    • The runner drops --image and reads --enforcement-images. It refuses a set for another profile, and a manifest or resolved repository digest other than the pinned one. --language only narrows which pinned languages are observed.
    • The rootless CI job fills a committed template with its synthetic python digest. The Python live test checks the report's set digest, image digests and argv against that file.
  • Evidence. Resource and pre-exec records carry inputs.enforcement_images_sha256 (catalog inputs updated). The verifier requires:
    • the set names the supplied grading profile;
    • every language image is the release index's released image;
    • the grading profile's own image is exactly one released language, whose recorded commands equal the profile's.
    • review/check-approval need --enforcement-images. The approval pins the set in profile_pins.enforcement_images_sha256, and the approval vector was repinned.
  • Refusal tests:
    • a set for another profile, an unreleased image, python commands that differ from the profile, and a profile image that is not a released language;
    • a missing image set document;
    • a mismatched enforcement-images pin at check-approval;
    • Go-side missing or renamed groups, an untrusted driver, a tag instead of a digest, a set naming another profile, an unknown or repeated language, and a runner whose binary can't be measured.
    • Explicitly recorded identical or Rust-specific argv are accepted.

Tests (part 1)

  • Python: evidence 365 passed; release, runtime bundle, matrix and preflight 147 passed; probe runner and workflow 8 passed, 2 live skips; all workflow suites 69 passed. ruff, format and mypy clean.
  • Go: go test -p 2 ./internal/codingenforcement/... ./cmd/dittobench-coding-enforcement-probe/; gofmt and vet clean. The golden record was repinned.

Tests (after part 2)

  • Python: evidence 375 passed; release, preflight, matrix, qualification, image import, runtime bundle, probe runner and all workflow suites 296 passed, 3 skipped (the local Docker import opt-in and the 2 live report tests). ruff, format and mypy clean.
  • Go: go test -p 2 ./internal/codingenforcement/... ./internal/codinghostedworker/ ./cmd/dittobench-coding-enforcement-probe/; vet clean, including -tags native_probe_integration.

Residuals

  • The per-language commands for the real hostile fixtures are placeholders in tests. The actual per-language argv is recorded when the fixtures land (PR 5), and Peyton reviews them as part of the pinned set.
  • The source-tree roots still cover only the runner, probe and catalog trees (provenance only).
  • A lying binary could report another digest. The future collector must also measure the file it executes; PR 4/5 must do this.
  • The runtime bundle schema string stays v2, but now requires the probe binary, so bundles built before this change are refused.

No collection, host run, approval or activation is requested.

🤖 Generated with Claude Code

Note

Add probe-runner binary and enforcement-image profile to v3 release index

  • Upgrades the native release-set schema from v2 to v3 and records a separate probe-runner executable digest read from the runtime manifest, distinct from the worker digest.
  • Introduces a canonical per-language enforcement-image document that pins image digests, build commands, and test commands for Go, Node, Python, and Rust; the probe reads this document instead of accepting caller-supplied image digests.
  • Adds probe.RunningExecutableSHA256 so the probe measures its own running binary and reports the digest in live observations and evidence records.
  • Builds the enforcement-probe as a static Go binary in the runtime Dockerfile, includes it in the bundle archive, and validates its ELF header and executable manifest entry.
  • Risk: release-policy validation now rejects v2 schemas; the observe-requested-config command removes the direct --image flag and requires --enforcement-images and --repository arguments instead.

Macroscope summarized 32e8c87.

@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

No dashboard preview for this commit. This PR resolved to preview profiles stack with no dashboard profile: it changes no apps/platform/dashboard/ path, so there is no dashboard build to publish. Profile selection fails closed rather than guessing, which is why a PR touching no recognized runtime path still reads as stack.

Stack previews are never automatic. A maintainer can provision one for this PR:

gh workflow run preview-stack.yml -f pr=1934                    # provision
gh workflow run preview-stack.yml -f pr=1934 -f action=retire   # free the slot

It runs on the default branch, resolves the head commit from the API, and comments dashboard, Platform, and isolated Backroom URLs once readiness succeeds. profile defaults to auto, which picks stack-copy when the PR touches apps/platform/alembic/. Eight slots cap every active preview; a re-dispatch reuses the slot this PR already holds. Reconciliation keeps it while the PR is open, up to a 24-hour lease cap, and retires it four hours after the PR closes.

Exact SHA: 32e8c87d3c115843d7978fcf14abf9b9cf72b7b6

ai-mountain and others added 17 commits September 15, 2026 21:48
…in the release set

The native runtime bundle builds bin/dittobench-coding-enforcement-probe with
the worker's static flags, smoke-checks it, and requires it as an executable
amd64 ELF. The release set (now v3) records runtime.probe_runner_sha256 from the
bundle manifest. That is the release-recorded binary digest evidence binds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The runner hashes /proc/self/exe, the executable image actually running, and
reports probe_runner_binary_sha256. It refuses to report if the binary can't be
measured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…evidence

Records now carry tools.probe_runner_binary_sha256, the binary that ran,
measured on the host. It must equal runtime.probe_runner_sha256 in the release
index, which verify, review and check-approval now require via
--release-index.

The ambiguous tools.runner_sha256 becomes tools.probe_runner_source_sha256.
It is kept as supporting provenance and still computed from the reviewed
checkout. Records must also name the exact index digest, and their source
revision, runtime archive and image approvals must match the index.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… binary

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cover refusals for an operator-built binary, the source hash passed off as the
binary, the old runner_sha256 key, index drift, record release fields that
differ from the index, malformed or v2 indexes and a missing --release-index.
Repin the golden record in Go and Python.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dittobench-coding-native-enforcement-images-v1 names every catalog language
once. Each entry has a distinct OCI manifest digest and its own explicitly
recorded build and test argv; test commands use the trusted driver and shells
are refused. It binds the grading profile whose limits and timeouts every
language shares. Resource and pre-exec records take its digest as an input.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… set

EnforcementProbeManifest applies the language's own build and test argv, and
keeps the approved profile's limits, timeouts, IDs and totals without mutating
it. The runner drops --image. It reads --enforcement-images, refuses a set for
another profile, and refuses a manifest or resolved repository digest other
than the pinned one. --language narrows which pinned languages are observed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ommands

Records that carry enforcement_images_sha256 must name the supplied grading
profile, and every language image must be the one the release index released.
The profile's own image must be exactly one language, whose recorded commands
equal the profile's. The approval pins the set in profile_pins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…inding

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…river

The enforcement image set accepted any bounded argv for Rust, and its vector,
test fixture and CI template pinned `--crate subject` commands that
codingexecutor.rustCommand refuses. Rust arguments were pinned but never
verified. Go and Python now require each Rust test command to be the driver's
authority command, `dittobench-test-driver --group <its own group>
--authority <relative .json> --authority-sha256 <hex>`. A codingexecutor test
checks the offline check against rustCommand on the shared vector, which gains
nine Rust refusal cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two refusal tests passed without their guards. Removing the record-to-release
index check still passed because the preflight comparison also names the
field, and removing the probe runner measurement check still passed because a
later image lookup failed too. Both tests now assert the refusal that the
guard itself produces, plus a malformed runner digest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The runner's /proc/self/exe digest is a self-report, and the verifier cannot
distinguish it from a collector measurement. Record that PR4/PR5 collectors
must hash the file they execute from outside the runner and refuse a
differing self-report.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
check-approval compared only each approval image's approval digest with the
review. The host's native.release_policy also requires image_ref,
config_digest and driver_profile to equal the release index, so an approval
with another image reference was reported consistent offline and refused
on-host. check-approval now compares every approval image field with the
release index it already requires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tive-probe-binding

Brings in the 2026-09-16 approval decisions: signature over exact stored
bytes with strict parsing, server_version observed rather than enforced on
the host, and the 24 hour approval validity cap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@Peyton-Spencer Peyton-Spencer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open Code Review pass completed at a41dabdfce6b8fc22373254ca2e30d1998160621 using z-ai/glm-5.3-flash through Ditto’s Code Review endpoint. Coverage: 34/34 added, modified, renamed, or type-changed files in the PR diff.

Model candidates were checked against the source; independently confirmed actionable findings, if any, are posted separately as inline review comments. This was a static review; no PR code or test suite was executed. This comment is not a merge approval.

…ve-probe-binding

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ai-mountain added a commit that referenced this pull request Sep 16, 2026
# Conflicts:
#	ditto/tests/test_coding_hosted_runtime_bundle.py
#	ditto/tests/test_coding_native_release.py
#	infra/ansible/roles/coding_hosted_runtime/files/runtime-bundle.py
#	infra/containers/coding-hosted-runtime.Dockerfile
@ai-mountain
ai-mountain marked this pull request as ready for review September 17, 2026 19:48

@ditto-code ditto-code Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto Review · Beta

Review completed for 32e8c87d3c115843d7978fcf14abf9b9cf72b7b6.

No actionable findings in the reviewed files. This is not an approval or proof that the PR is bug-free.

Configure Ditto Review. Inference is billed through your Ditto Router endpoint; targeted verification is included during beta.

This branch was successfully deployed

1 active deployment
preview — 32e8c87d Deployed Sep 16, 2026 by ai-mountain via dashboard-publish / retire #2623
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