Repository navigation
fix(validator): make the certification canary runnable and fail closed before a lease - #1921
ai-mountain wants to merge 24 commits into
Conversation
The default-off canary runtime accepted only HTTPS or loopback scorer origins, so enabling VALIDATOR_CODING_CANARY_ENABLED against the Compose value http://sandbox-docker:8000 raised during worker construction. Accept exactly that fixed service origin in addition to HTTPS/loopback; reject every other plaintext host, port, path, userinfo, query, or fragment. The certify call streamed a whole run through the shared 30 s client with no per-request bound. Derive the request timeout from the lease deadline, capped at the scorer's 32-minute operation bound, refuse to send once the deadline has passed, and wrap the stream in asyncio.timeout so a stalled scorer or task cancellation closes the connection. The call stays single-shot; the worker still revokes the grant in its shielded finally. The scorer bearer and per-lease broker private key now travel on a dedicated no-proxy client owned by the coding exit stack, and the runtime refuses a proxy-inheriting client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r image A canary-enabled scorer loads a repo-shaped certification/v1 root, but the sandbox image carried only the locked policy, so enabling DITTOBENCH_CODING_CANARY_ENABLED made codinghost construction fail and log.Fatalf took ordinary scoring down with it. Add a small coding-certification-pack stage that copies the committed certification/v1 capsule and the locked policy its manifest names, checks every file against pinned SHA-256 digests (including the manifest digest Platform binds into leases), rejects extra files and links, and makes the tree 0555/0444. Only the sandbox target copies it, root-owned, to /opt/ditto/coding/certification-root. The policy COPY's --chmod=0444 is also applied by BuildKit to the implicitly created /opt/ditto/coding, which the uid 65532 scorer cannot traverse (reproduced locally: the policy and the pack were both unreadable). Restore 0555 on that directory. Compose pins DITTOBENCH_CODING_CERTIFICATION_ROOT to the image path. It is read only when the canary gate, still false by default, is enabled and is not operator-selectable, because the runtime loader does not re-hash the workspace or grader files. Pack edits now select the scorer release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… validator The production validator stack rendered none of the canary settings, so the supported contract-v1 certification path could not be enabled there without hand-editing the host .env. Add two independent switches, both false: the scorer switch renders DITTOBENCH_CODING_CANARY_ENABLED plus the runtime image repository and digest (empty unless on), and the validator switch renders VALIDATOR_CODING_CANARY_ENABLED and its poll interval. A pure validation task runs before any host mutation and requires the scorer switch for the validator worker, excludes the executor connectivity canary, bounds the poll to [1, 300], and demands a sha256 digest and a well-formed repository whenever the scorer switch is on. The certification root stays pinned by Compose to the image-baked pack and is deliberately not rendered. A localhost check-mode playbook, wired into infra CI, proves the shipped defaults render disabled and empty, renders staged and complete profiles, and requires the validation task itself to reject 15 unsafe profiles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…form origin
Validators accept a coding grant exchange URL only when it equals
{VALIDATOR_PLATFORM_API_URL}/api/v1/validator/..., for both shadow tickets
and certification leases, and Platform derives the certification exchange
URL from DITTO_CODING_INFERENCE_EXCHANGE_URL. Production rendered that
URL from its inference origin https://dittobench.ai while every validator
uses https://platform-api.heyditto.ai/, so an issued certification grant
would fail the validator's exact check after the lease was claimed.
Render the exchange URL from a new platform_coding_validator_api_base_url
that defaults to the inference origin (dev and other hosts render exactly
as before) and pin it to https://platform-api.heyditto.ai for production.
Activation now also asserts the exchange URL equals that origin plus the
fixed path. The validator's single-origin check is unchanged rather than
widened to accept the inference host. platform_coding_shadow_enabled stays
unset in production, so nothing renders until a reviewed activation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Describe the accepted Compose scorer origin, the deadline-bounded single-shot certify call, the build-verified scorer pack and pinned root, the two validator_stack switches with their staging order, and the validator-facing exchange origin. Record what is still missing before the production validator can certify: a rootless, isolated coding daemon (checked only after claim) and targeting. Also record the one-shot lease risk that the separate Platform lease-expiry PR mitigates and the one-hour certificate window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…kerfile pins The scorer build failed on any stray local file under certification/v1 because the pack stage compared the copied tree against a hand-maintained digest list, and the root .dockerignore entries `__pycache__` and `*.pyc` match only at the context root. A capsule `tests/__pycache__` from running the capsule tests, or a `.DS_Store`, broke every scorer rebuild even though the canary is default-off. The same list was duplicated in the Python and Go tests, and the runtime loader re-hashed nothing. LoadPublicPack now verifies what it executes. The grader tree must contain exactly the manifest's grader_files (path, size, SHA-256), which Platform binds into every lease through the manifest digest. The visible workspace must match one pinned listing digest. Links, special files, and any other file fail closed. executionPlans bundles only the verified bytes, and Verify re-reads both trees on demand. One exclusion rule covers interpreter caches and Finder metadata (`__pycache__/`, `*.pyc`, `.DS_Store`). The loader never verifies or bundles those names, and the root .dockerignore now excludes them at any depth (`**/`). The pack stage only copies the tree and makes it read-only, so a bad pack can no longer fail the scorer build. The Go and Python tests share that rule: junk-laden roots load with identical plans, and tampered, extra, missing, or linked files are rejected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… grammar validImageRepository accepted references Docker rejects, such as an empty path component (`registry.invalid//runtime`) or a colon inside the path (`registry.invalid/a:b/c`). The scorer then started normally, and the bad reference failed only when the executor built `repo@sha256:...` during certify, after the lease was already claimed. Match the Docker reference grammar for a repository name instead, restricted to lowercase: an optional registry host[:port] component, then path components of alphanumerics joined by `.`, `_`, `__`, or dashes, separated by single slashes, at most 255 bytes, with no tag or digest. The validator_stack role applies the same grammar before rendering. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…diness Three scorer-side gaps let a default-off canary strand a claimed lease or stop ordinary scoring. Dedicated coding daemon. The coding host shared the scorer's LocalDocker, so its harnesses and executors used the stack's DOCKER_HOST: on Compose, the privileged rootful sandbox-docker daemon. An enabled coding gate now builds its own runtime whose every Docker CLI call, in both the sandbox runtime and the executor, targets DITTOBENCH_CODING_DOCKER_HOST. That endpoint is unset by default. It must be a local Unix socket, must not be a conventional rootful socket or the ordinary scoring daemon, and must still prove rootless mode and the isolated-daemon label live. A source check keeps every sandbox Docker call on that one helper. Readiness before claim. The validator's only pre-claim check was that the canary route existed, so it issued and claimed a lease that certify's executor preflight then refused. GET /v1/coding/certifier/canary/readiness, with the canary's bearer, runs no harness, container, grant, or lease. It re-verifies the loaded pack, reruns the harness daemon check and the executor's rootless and isolated-label checks, and confirms that the exact runtime image digest is present with the supervisor contract. It reports ready only when all of these pass, together with the pack identity, so the validator can refuse before issue and compare the issued lease before claim. Probes are serialized and bounded to 20 seconds. Non-fatal coding host. A DITTOBENCH_CODING_CANARY_ENABLED or DITTOBENCH_CODING_SHADOW_ENABLED construction failure was log.Fatalf for the whole scorer, which stopped every benchmark ticket. The failure is now logged as a refusal. Every coding route, including readiness, answers 404, and ordinary scoring keeps running. The endpoint check runs first, before the private root or source listener is touched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lient The certification canary accepted the Compose scorer origin http://sandbox-docker:8000 through a canary-only special case. The local-mode CodingSupervisorRuntime ran a near-duplicate check that still rejected the same VALIDATOR_DITTOBENCH_API_URL, so enabling the local shadow worker on that stack failed construction, and the two checks would keep drifting. coding_executor_transport.scorer_control_origin is now the single rule for both runtimes: any HTTPS origin, a loopback HTTP origin, or exactly the Compose service origin, with no path, userinfo, query, fragment, or malformed port. Remote executor mode still requires the private HTTPS :9443 origin. With both coding gates on, the canary and the local shadow worker also opened two identical no-proxy clients, each with its own connection pool, to the same scorer. They now share one lazily created private client that ignores proxy settings and closes with the coding exit stack. A disabled canary still constructs no client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…anary lease The canary worker issued and claimed a one-shot certification lease for every agent the validator scored. Its only pre-claim check was that the scorer's canary route existed, so a rootful daemon, a missing runtime image, or a mismatched pack failed certify only after the claim. Readiness. CodingCanaryRuntime.require_ready reads the scorer's read-only readiness probe with the same bearer. It refuses (and the worker keeps the offer) unless the answer is 200, private, well-formed, and explicitly ready, with the pack loaded, the dedicated daemon rootless and isolated, and the runtime image present. The worker probes only when an offer is queued, before issue. It then compares the issued lease's five pack digests with the ready scorer's and aborts the still-issued lease on any mismatch instead of claiming it. Targets. VALIDATOR_CODING_CANARY_AGENT_IDS (at most 16 canonical, non-nil, unique UUIDs) and VALIDATOR_CODING_CANARY_VALIDATOR_HOTKEY define the only leases the worker may take. Both default to empty, which refuses everything. The listed hotkey must equal this validator's own hotkey, so copied targets cannot make another validator run the canary. Offers outside the targets are dropped. Targets are rechecked before issue, and an issued lease for another agent or hotkey is aborted before claim. An enabled worker with no usable targets logs that it refuses every lease. Deadline. The certify call is bounded by the time left on the lease and nothing else. The misleading 32-minute cap is gone: the scorer runs a 20-minute default, and Platform issues 20-minute leases. asyncio.timeout alone enforces the bound, with only short connect, write, and pool limits and no read timeout that could fire first with the wrong error. Every deadline error says "deadline exceeded". The dead math.isfinite check is removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… and targets The validator_stack role accepted a fully enabled canary on today's stack, where Compose's only Docker daemon is the privileged rootful sandbox-docker. Nothing in the role encoded the rootless-daemon prerequisite or any targeting, and its repository pattern accepted references Docker rejects. Dedicated daemon. validator_stack_coding_docker_host (empty by default) renders DITTOBENCH_CODING_DOCKER_HOST, which Compose passes to the scorer only while the scorer switch is on. Either canary switch now requires a local `unix:///...sock` endpoint with no empty or dot segments. The conventional rootful sockets and any tcp:// endpoint, including the sandbox daemon tcp://127.0.0.1:2375, are refused. The scorer additionally refuses its own DOCKER_HOST and proves rootless isolation live. This role installs no such daemon, so both switches stay unusable until the separate rootless coding daemon change lands. Targets. validator_stack_coding_canary_agent_ids (1-16 canonical, non-nil, unique UUIDs) and validator_stack_coding_canary_validator_hotkey, which must equal validator_stack_hotkey, render the validator's exact targets only while the validator switch is on. Both default to empty, which refuses every lease. The hotkey is never defaulted; it comes from the role's validated hotkey. Repository. The runtime image repository check is the Docker reference grammar shared with the scorer: at most 255 bytes, anchored with \Z, and refusing empty path components, colons inside the path, and tags. The defaults now describe the scorer switch's real scope: it builds the whole coding host, and a failure disables only the coding routes. The localhost check-mode playbook renders the new values and rejects 44 unsafe profiles, each at its intended assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… origin
platform_coding_validator_api_base_url defaulted to the inference origin, so
production needed a host_vars pin. The new equality assert compared the
exchange URL with that same variable, which made it trivially true: on any
host whose inference origin differs from platform_domain, the default
rendered the exchange on the inference origin, the playbook passed, and every
validator rejected every shadow and certification grant.
Default the base to https://{{ platform_domain }}, the primary vhost
validators use as VALIDATOR_PLATFORM_API_URL. That is correct for dev (where
it matches the inference origin) and for production, so the production pin is
removed. Shadow transport activation now asserts that both the base and the
exchange URL are exactly that origin, which refuses an override that points
at the inference origin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… prerequisite Replace claims that no longer hold: the 32-minute certify cap, Dockerfile pack pins, the canary route as the pre-enable check, production's exchange-origin pin, and "no targeting". Document the shared scorer-origin rule and private client, the dedicated rootless coding daemon endpoint and its refusals, exact targets, the readiness probe before issue and claim, the lease-deadline-only certify bound, loader-enforced pack integrity with its one exclusion rule, the scorer switch's real scope and non-fatal failure, and the exchange origin default. List exactly what the separate rootless coding daemon change must provide, and state the remaining one-shot risk until the companion Platform lease-expiry change is merged and verified. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ct vector The Go readiness handler and the validator's require_ready agreed only through hand-written payloads on each side, so a renamed field would stay green in both suites and refuse every lease at runtime. coding_certification_canary_readiness_v1.json freezes the ready answer for the committed certification/v1 pack and its not-ready failure codes. The Go handler must encode it exactly and produce every listed failure code. The validator runtime must accept it, return its pack identity, and refuse each failure code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No dashboard preview for this commit. This PR resolved to preview profiles Stack previews are never automatic. A maintainer can provision one for this PR: gh workflow run preview-stack.yml -f pr=1921 # provision
gh workflow run preview-stack.yml -f pr=1921 -f action=retire # free the slotIt runs on the default branch, resolves the head commit from the API, and comments dashboard, Platform, and isolated Backroom URLs once readiness succeeds. Exact SHA: |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…o CA bundle The coding host built its Docker client with sandbox.NewLocalDocker, so it inherited the ordinary scorer's DITTOBENCH_SANDBOX_* egress network, proxy, host gateway, limits and confinement, the OpenRouter shim CA bundle, GITHUB_TOKEN_FILE and the private-harness switch. It now uses DITTOBENCH_CODING_EGRESS_NETWORK, DITTOBENCH_CODING_EGRESS_PROXY and DITTOBENCH_CODING_HOST_GATEWAY_IP only, refuses a proxy or gateway equal to the sandbox value, mounts no CA bundle, and fixes its limits. Docker CLI calls to a dedicated daemon drop inherited DOCKER_* selectors and proxy variables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… gateway Compose passes empty DITTOBENCH_CODING_EGRESS_NETWORK, _EGRESS_PROXY and _HOST_GATEWAY_IP by default, and validator_stack renders them only behind the scorer canary switch after validating a private IPv4 proxy and gateway. 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>
Resolves the validator entrypoint, config and control-plane route conflicts with the router ledger read seam from main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings main (router ledger read seam) in through #1921 and keeps both the certification socket settings and router_ledger_read_enabled in the validator config. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…racle The sandbox test classified the child environment with the production inheritedDockerSelector itself, so weakening that rule also weakened the check. Use an independent DOCKER_*/_PROXY oracle, and poison all_proxy and http_proxy in the executor twin so each dropped variable is exercised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cation canary targets Platform's certification allowlist admits exact (agent, artifact digest, screened-image digest, validator hotkey) tuples. The validator's targets bound only the agent and hotkey, so a lease for a rebuilt image or another artifact passed the validator-side check. VALIDATOR_CODING_CANARY_TARGETS replaces VALIDATOR_CODING_CANARY_AGENT_IDS with 1-16 unique <agent_id>:<artifact_sha256>:<screened_image_sha256> entries. An issued lease whose four fields are not exactly one listed tuple is aborted before claim, and a claimed lease is checked again before the harness launch, grant or certify call. validator_stack renders the targets from mappings with exactly those three fields and validates them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Peyton-Spencer
left a comment
There was a problem hiding this comment.
Open Code Review pass completed at 6eac25140b67581be9c437e933dde7f529e4e188 using z-ai/glm-5.3-flash through Ditto’s Code Review endpoint. Coverage: 57/57 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.
…diness Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Peyton-Spencer
left a comment
There was a problem hiding this comment.
Open Code Review pass completed at b9f63a4e4d16cdb1083a2723fd73188c7b512dcd using z-ai/glm-5.3-flash through Ditto’s Code Review endpoint. Coverage: 57/57 added, modified, renamed, or type-changed files in the PR diff.
This refresh re-reviewed the changed PR files and carried forward completed coverage for unchanged files. 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.
…fixture after #1921's validator API origin default Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…adiness Keep main's custody service and PostgreSQL environment infra CI steps alongside this PR's certification canary default-off step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s @ 2b7df44 # Conflicts: # .github/workflows/infra-ci.yml # docker-compose.yml # infra/ansible/roles/validator_stack/defaults/main.yml # infra/ansible/roles/validator_stack/tasks/main.yml # infra/ansible/roles/validator_stack/templates/validator.env.j2
's launch_journal_dir in #1909's attempt config - #1929 unit-uninstall guard: list the read-only live-unit tasks of #1920, #1897, #1899 and #1925 - #1923 canary targets in #1921's receipt-window worker test - #1923 strict allowlist admit in #1911's v1 certification test - #1919 router host fields and #1941 launch_journal_dir in #1909's attempt config (HostLayout.launch_journal, verify_host private directory) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fixture after #1921's validator API origin default Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Ready for review. Merge-time note: once #1901's signer test playbook is on main, |
There was a problem hiding this comment.
Ditto Review · Beta
Review completed for 2b7df4422b10c71036f99a292c0fee75f027ddef.
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.
Why
B2 (approved by Peyton; unblocked 2026-09-15 once the rootless router
candidate.router.sourceprobe passed, #1919). As deployed today, the contract-v1 certification canary cannot run on the production validator:A post-claim failure would also strand the one-shot lease. This PR is the validator, scorer and infra side. The Platform side (lease expiry, allowlist, attempt cap) is a separate PR.
What
scorer_control_originrule for both the canary and the local shadow supervisor, which admits exactly the Compose originhttp://sandbox-docker:8000. One private no-proxy scorer client is shared.LoadPublicPackverifies grader files against the manifest'sgrader_filesand the visible workspace against a pinned listing digest, and rejects extras and links;.dockerignoreand the loader skip exactly__pycache__,*.pycand.DS_Store.log.Fatalffor the whole scorer.DITTOBENCH_CODING_DOCKER_HOST/validator_stack_coding_docker_hostmust be aunix://socket;tcp://,/var/run/docker.sock,/run/docker.sockand the scorer's ownDOCKER_HOSTare refused;GET /v1/coding/certifier/canary/readiness(same bearer, no side effects) re-verifies the pack, checks rootless and isolated labels, confirms the runtime image digest is present, and returns the pack digests. The validator refuses to issue unless it's explicitly ready, and aborts rather than claims if the lease's digests differ from the scorer's. The wire format is pinned by a shared contract vector.VALIDATOR_CODING_CANARY_AGENT_IDS(1–16 UUIDs) plusVALIDATOR_CODING_CANARY_VALIDATOR_HOTKEY, which must equal the local hotkey; empty refuses all;asyncio.timeoutwith short connect/write/pool limits. Every deadline error says "deadline exceeded". Docs state 20-minute leases.platform_coding_validator_api_base_urldefaults tohttps://{{ platform_domain }}, the validator-facing origin. The prod host_vars pin is gone, and the assert requires exactly that origin.validator_stackrenders the default-off canary with two switches, and the role refuses either switch without the rootless socket and exact targets.Tests
cmd/dittobench-apicoding and control-plane tests all pass.-racepasses on codingcanary, codinghost and codingexecutor.gcp-validator-prodandgcp-platform-app.Default-off / not authorized
Both switches default off, and targets default empty (refuse all). No certification run, no production activation, no host change.
Still required before any certification attempt
io.heyditto.dittobench.isolated=true, with rootless in SecurityOptions;dittobench-apiviaDITTOBENCH_CODING_DOCKER_HOST, with egress network and proxy env set;ready: true.Questions for Peyton
Should the validator also check the artifact identity (Platform's allowlist binds it)?Done; see the 2026-09-16 update.after_scorere-offers an already certified agent. Should the validator skip it?CodingPublicationClient's separate local-origin check be unified?Update (2026-09-15): certification-only runtime settings (settings leak fix)
Peyton's decision: give certification its own proxy, gateway and no-CA settings.
The leak. The coding host built its Docker client with
sandbox.NewLocalDocker(), which reads the ordinary scorer's environment. On Compose that meant the coding harness would inherit:DITTOBENCH_SANDBOX_EGRESS_NETWORK=ditto-sandbox;DITTOBENCH_OPENROUTER_SHIM_CA_BUNDLE_PATH, mounted and injected asSSL_CERT_FILE/REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE/NODE_EXTRA_CA_CERTS);DITTOBENCH_SANDBOX_EGRESS_PROXY, host gateway, memory/tmpfs/pids/start overrides, seccomp/AppArmor names,GITHUB_TOKEN_FILEandDITTOBENCH_ALLOW_PRIVATE_HARNESS.Setting a proxy for the coding host would also have injected
HTTPS_PROXYinto every ordinary scoring container.Fix.
sandbox.NewCodingRuntimeDockerbuilds the coding client only from explicit settings:DITTOBENCH_CODING_EGRESS_NETWORK;DITTOBENCH_CODING_EGRESS_PROXY, exactlyhttp://<private IPv4>:<port>;DITTOBENCH_CODING_HOST_GATEWAY_IP, a private IPv4;DITTOBENCH_CODING_SECCOMP_PROFILEandDITTOBENCH_CODING_APPARMOR_PROFILE(unconfinedrefused).DITTOBENCH_SANDBOX_*. A coding proxy or gateway equal to the sandbox value is also refused.DOCKER_*selectors (context, TLS, config, API version) and proxy variables.validator_stackrenders them only behind the scorer canary switch and validates them.Tests.
docker runargs contain no CA mount or TLS variables and only the coding proxy, network and gateway; 25 refusal shapes; CLI environment selectors; the cmd no-fallback, equal-to-shared and before-side-effects tests.validator-stack-coding-canary.ymlgives ok=215, rescued=65, failed=0 (21 new reject cases).main(the router ledger seam conflicted in the validator entrypoint, config and control routes).Superseded by #1932 for the canary. #1932 moves certification to a host-level service reached over a Unix socket. Its readiness v2 requires a placement proof the Compose scorer cannot provide, so the Compose canary route here is never ready there.
Head:
746b8d310.Review update (2026-09-15): independent adversarial review
An independent security and correctness review of the settings leak fix found no leak. Nothing falls back to
DITTOBENCH_SANDBOX_*, the codingLocalDockermounts no CA bundle, and no harness-eor TLS variable reaches the coding container. The proxy and gateway are validated, and a value equal to the sandbox value is refused.Test defect (fixed).
TestDedicatedDockerEndpointInheritsNoDockerOrProxySelectorused the productioninheritedDockerSelectoras its own oracle. As a result, narrowing the stripped set (for example toDOCKER_HOSTonly, or droppingHTTPS_PROXY) still passed. The test now uses an independentDOCKER_*/*_PROXYoracle. The executor twin also poisonsall_proxyandhttp_proxy, so each dropped variable is mutation-checked.Head:
9e3c6a59e. CI is green.Update (2026-09-16): targets bind the artifact and screened-image digests
The validator's targets now match #1923's four-field Platform allowlist:
agent_id,artifact_sha256,screened_image_sha256andvalidator_hotkey. Before, they bound only the agent and the hotkey, so the validator would have let through a lease for a rebuilt image or a different artifact.What changed
VALIDATOR_CODING_CANARY_TARGETSreplacesVALIDATOR_CODING_CANARY_AGENT_IDS. It takes 1 to 16 unique<agent_id>:<artifact_sha256>:<screened_image_sha256>entries: a canonical non-nil UUID and two lowercase 64-hex digests, the same form as the lease authority.VALIDATOR_CODING_CANARY_VALIDATOR_HOTKEYis still the fourth field and must equal the local hotkey. Empty still refuses everything. Agent-only entries,sha256:prefixes, uppercase or short digests, duplicates and more than 16 entries are refused at config parse.CodingCanaryTargets).binds()requires the lease's agent, hotkey,agent_artifact_sha256andscreened_image_sha256to be exactly one listed tuple. Otherwise the still-issued lease is aborted before claim.validator_stack.validator_stack_coding_canary_targetsis a list of mappings with exactlyagent_id,artifact_sha256andscreened_image_sha256. It renders as the setting above. Validation checks list shape, 1 to 16 entries, the exact key set, UUID and digest grammar, a non-nil agent and unique tuples.Tests
ditto/tests/validatorand the compose, runtime, scorer image, workflow and release-plan guards. ruff and mypy are clean. New cases cover:binds()for each of the four fields, including swapped artifact and image digests;tests/validator-stack-coding-canary.ymlgives ok=242, rescued=74, failed=0. All 17 target reject cases fail at their intended assertion (checked with-vv), and the complete profile renders the exact target string.Head:
6eac25140.Restack
Restacked on main e1a8fa2 (#1937 v13 integration). Clean merge.
🤖 Generated with Claude Code
Note
Make the certification canary runnable and fail closed before a lease
codingDockerHostFromEnvironmentandsandbox.NewCodingRuntimeDockerreject endpoints shared with the ordinary scorer daemon; a coding-host construction failure now disables coding routes instead of terminating startup, so reviewers should confirm that no existing deployment relies on fatal startup behavior for misconfigured coding settings.Macroscope summarized 2b7df44.