Skip to content

audit: quarantine incompatible Validator contract pending owner decision - #13

Draft
skyrocket2026 wants to merge 2 commits into
cathedralai:mainfrom
skyrocket2026:audit/validator-contract-quarantine
Draft

audit: quarantine incompatible Validator contract pending owner decision#13
skyrocket2026 wants to merge 2 commits into
cathedralai:mainfrom
skyrocket2026:audit/validator-contract-quarantine

Conversation

@skyrocket2026

Copy link
Copy Markdown

External audit proposal — owner decision required

This is a DRAFT, fail-closed quarantine proposal. It is not launch provenance,
does not supersede the owner-controlled PRD, and does not authorize any chain,
weight, wallet, mechanism, KBS, billing, or production-routing change.

Reviewed premise

The retained CLI adapter modeled the historical signed-feed relay, but the reviewed
Validator is a direct chain writer:

  • repository: cathedralai/cathedral-validator
  • reviewed commit: d225e8758ca02627cced800b7de0c79464d89aee
  • entrypoint: cathedral_thin.independent_runtime.direct_validator:main
  • entrypoint source SHA-256: ac258f889192a88eb9269a48bb3de5f488419c8645bff59b7cb2e9aa335204eb
  • pyproject.toml SHA-256: acb8355a17c7011d4718805755c6dd70f5f42126fbb415ca294a1664fadad6df
  • direct-write confirmation is mandatory; no reviewed non-writing runtime mode exists

The CLI therefore must not turn a repin, retry, dry run, or argv edit into implied
chain-write authority.

Proposed behavior

  • Quarantine every public Validator setup/test/start/quickstart/config-write path
    before run state, publisher fences, argv construction, or child execution.
  • Keep historical status/log/evidence/cancel inspection, clearly labeled as
    historical and non-operational; never reconcile or rewrite historical run state.
  • Preserve node-wide signed releases while treating the retained Validator member
    as static evidence only. Candidate wheels are installed offline, but no
    post-install Validator interpreter/import/client/server probe intentionally runs.
  • Add receipt v6 execution tiers: runtime_checked and static_quarantine.
    Static generations expose no executable interpreter or entrypoint.
  • Bind the receipt tier to current adapter policy. A receipt plus a recomputed local
    manifest cannot self-promote static evidence to executable evidence.
  • Validate signed dependency metadata before install in a pristine bootstrap venv,
    including versions, environment markers, normalized/requested/transitive extras,
    and direct-reference refusal, without loading candidate code.
  • Harden run ownership, exact-target stop/cancel, role-control serialization,
    malformed run/config handling, redaction, and historical follow/resume behavior.

Validation performed

  • python -m pytest -q tests/test_contract.py: 122 passed on the committed tree.
  • Focused quarantine/recovery/locking set: 10 passed.
  • Focused signed-closure set: 3 passed.
  • Focused receipt promotion, public refusal, and sealed path-binding cases passed.
  • python -m compileall -q cathedral_node tests: passed.
  • git diff --check: passed.
  • Two independent code reviews found no remaining public Validator execution bypass
    or P0/P1 code blocker to opening this draft.

These focused runs are deliberately not described as Gate 0 evidence.

Formal Gate 0 status: NOT RUN / NO-GO

The exact ./run-gate0.sh command exits 4 at the manifest precheck and executes
zero tests. The read-only manifest generator reports seven unmapped proposal
tests:

  1. all public Validator test/start/setup/quickstart variants refuse without a run;
  2. capabilities label the retained Validator pin as historical;
  3. diagnostics and legacy config do not prescribe a retry;
  4. direct adapter calls refuse before runtime state;
  5. discovery reports the reviewed incompatibility;
  6. static closure handles markers/extras and rejects direct references;
  7. static execution validation cannot self-promote via a recomputed manifest.

I intentionally did not regenerate tests/gate0_manifest.json, edit the Gate 0
generator, modify Gate 12 evidence, or change cathedral.lock.json. Blindly mapping
the tests would be misleading because the current requirement map still calls for
operational Validator configuration/start behavior and says the legacy Validator is
not pinned, while this proposal deliberately retains it as static evidence.

Owner decisions required before merge or any readiness claim

  1. Amend or supersede the controlling PRD and map the new tests through an
    owner-approved Gate 0 manifest change.
  2. Resolve protocol authority. The CLI still reports protocol 1.0.0, while its own
    versioning rules classify these changed command/exit semantics as a major change.
  3. Specify a transactional static-to-runtime migration. Ordinary update is
    intentionally insufficient; a future compatible Validator needs a newly
    runtime-checked, higher signed release and explicit owner-approved migration.
  4. Resolve the retained node-wide Validator package and receipt-v6 migration policy.
  5. Replace the stale cathedral.lock.json instruction that recommends
    cathedral update --to <lockfile> even though unsigned lockfile switching is
    refused, and approve the intended signed-release workflow/profile.
  6. Re-run all formal gates after those authority changes. Until then, Validator
    execution, Gate 0 acceptance, and subnet launch remain unauthorized.

Residual limitation

Stop/cancel re-probes the complete ownership tuple immediately before TERM and KILL
and serializes owner changes, but POSIX killpg does not provide pidfd-atomic process
group identity. This draft does not claim that the final check-to-signal reuse window
is cryptographically or kernel-atomically eliminated.

Please keep this PR in draft until the owner-controlled authority decisions above
are resolved. Do not merge it as launch readiness evidence.

@wallscaler

Copy link
Copy Markdown
Contributor

Assessment of the proposal at head 3db7043, from a local checkout. Contract tests ran locally on Python 3.12.

cathedral-cli#13: quarantine incompatible Validator contract pending owner decision

Artifact / Question / Out of scope

  • Artifact: PR audit: quarantine incompatible Validator contract pending owner decision #13, head 3db7043, base = main 8853a2a (no drift), 33 files, +2998/-747. Draft, single commit.
  • Question: what decision is being asked; is the premise true; quarantine versus adapting to the direct-writer contract.
  • Out of scope: PRD content; the Gate 0 manifest; deep review of the run-ownership and killpg rework, which is unrelated to the decision and needs its own pass.

What it actually changes

  • cathedral_node/engines/validator.py: removes the relay adapter (the serve --once --dry-run --offline ... argv and JSONL event parsing) and replaces it with an adapter where local_test, operate_argv, operate_env and interpret_line all raise ValidatorContractIncompatible. qualify returns a fixed contract.engine_incompatible blocker. The TOML renderer is kept as a "migration artifact". A reviewed-contract evidence dict pins commit d225e87 and two source hashes.
  • cathedral_node/engines/base.py: new operation_blocker() hook; bin, has_bin, python refuse unless the receipt says runtime_checked.
  • cathedral_node/engines/installer.py (+374): receipt schema v5 to v6 with execution_validation in {runtime_checked, static_quarantine}; the tier is derived from the current adapter policy (_role_execution_validation calls operation_blocker()), never from the receipt; static roles skip the post-install interpreter, import and entrypoint probes and get a file-only distribution check; a new pre-install signed-wheel closure probe evaluates markers and extras via pip's vendored packaging in the pristine venv; the old regex _parse_requires closure check is deleted.
  • cathedral_node/verified.py: python and bin() raise SealError for static generations; python_path is diagnostics-only.
  • state.py (+256), commands/run.py (+190), commands/status.py (+140), commands/logs.py (+86): ownership tuple re-probe before TERM and KILL, a per-role control lock, historical-run labeling, redaction of the pinned digests.
  • Every other command (setup, test, quickstart, config, doctor, explain, capabilities, update, evidence, cleanup, agent_brief) grows validator-specific refusal or "historical" labeling.
  • Docs and README rewritten around the quarantine. cathedral.lock.json and the Gate 0 manifest are untouched; the author reports Gate 0 exits 4 with seven unmapped tests.
  • Local run: tests/test_contract.py 122 passed, 330 subtests, Python 3.12.

Decision needed

  • The author asks, in one sentence: accept that cathedral-cli's validator role becomes a permanent static-evidence-only quarantine with no setup, test or start, amend the PRD and the Gate 0 manifest to match, bump the protocol major, and commit to designing a transactional static-to-runtime migration before any future validator support.
  • Underneath that is the real product question: should cathedral-cli have a validator role at all now that the validator's public operating path is the signed bootstrap plus cathedral-validator-setup (root, systemd, shipped 2026-09-02)?

Premise check

  • True as stated. At d225e87 and at current main, cathedral-validator is cathedral_thin.independent_runtime.direct_validator:main; main() exits unless --confirm-direct-write is set (direct_validator.py:653); that entrypoint has no non-writing flag. The two hashes in the PR match the files at d225e87. The only no-write console script in the package is cathedral-amd-sev-snp-dev-preview, which is dev-only.
  • Incomplete in a way that changes the framing. The CLI does not install d225e87. cathedral.lock.json pins validator at 80dd56e (2026-07-30), where cathedral-validator = scaffold.cli:main and serve accepts --once --dry-run --offline --provenance --runtime-root --state-file --jsonl, which is exactly the argv the pre-PR adapter emits. The direct entrypoint arrived on 2026-08-30 (37dc1f2). The incompatibility exists only if someone repins.
  • The pinned adapter is compatible with a retired mode, though. The relay posture was replaced on Aug 30 and one 10-second probe of https://api.cathedral.computer timed out (single probe, not a measurement). So "leave the adapter alone" is not a working product either.
  • Today nothing installs: main's README says no signed Cathedral release is published, so cathedral setup validator fails closed before any of this code runs. The user-visible delta of audit: quarantine incompatible Validator contract pending owner decision #13 today is wording and exit codes in explain, capabilities, doctor, status and config.

Recommendation

  • Held. The validator-role decision is mine to make and I will answer it separately after reading this through. The findings below are about the mechanism as proposed.

Findings

F1. The policy-derived receipt tier makes whole-node verification depend on CLI code rather than on signed release data, so lifting the quarantine later breaks every existing install until reinstall.

  • Code path: installer.py _role_execution_validation(role, lock) returns the tier from engine_adapters.load(role, lock).operation_blocker(); verify_group_pointer computes expected from it for every role and returns False for the whole group on any receipt mismatch ("receipt execution_validation does not match the current role policy").
  • User-visible: after any CLI upgrade that removes the validator blocker, status, start, test for compute and distill fail with that message until a full reinstall, even though nothing about those roles changed.
  • Relevance: this is the mechanism the PR asks me to accept, and the author lists the resulting migration design as owner decision 3. The cost is node-wide for a validator-only reason.

F2. The PR frames the incompatibility as if it applied to the installed engine; the pinned engine matches the adapter.

  • Code path: cathedral.lock.json engines.validator.revision = 80dd56e; validator pyproject.toml at 80dd56e maps cathedral-validator to scaffold.cli:main; scaffold/cli.py at that revision defines the serve flags the adapter emits.
  • User-visible: the new CONTRACT_NOTICE tells operators the CLI "cannot test or start that contract", naming d225e87, when the contract it would actually install is the relay.
  • Relevance: it turns "repin and adapt" versus "quarantine" into the decision, when the actual choice is "keep a validator role or not".

Refuted

  • "The premise is false." No. The direct-writer claim is verified byte-for-byte at d225e87 and still holds at main.
  • "The relay adapter still works, so nothing is needed." Cannot claim that. The feed endpoint did not answer one probe and the relay posture was retired Aug 30.
  • "Quarantine breaks miners today." No. No signed release exists, so no generation exists anywhere; tests pass.
  • Looked for a public validator execution path that bypasses the blocker: Engine.bin, has_bin, python, VerifiedRole.bin, VerifiedRole.python all gate on runtime_checked; the adapter's four operation hooks raise. None found by reading.
  • "Receipt v6 invalidates existing v5 generations for compute and distill." True in code (_receipt_types_ok requires the field and schema == v6), but there are no installs to invalidate. Observation, not a finding.

Observations

  • Scope creep: the run-ownership re-probe before TERM and KILL, the role control lock, redaction and the logs/status relabeling are unrelated to the decision. The author's own "residual limitation" note is about that code. It needs its own review if it survives.
  • Gate 0 not run by design (exit 4, seven unmapped tests); the PR says so plainly and did not touch the manifest.
  • The pre-install signed-wheel closure probe (markers and extras via pip's vendored packaging) is a genuine improvement over the deleted regex check and would be worth keeping on its own if the installer is kept.
  • The author asks to keep the PR in draft until the decisions are made. It is a proposal, not a merge request, and it says so.
  • Same authorship pattern as #206: commits by inference2026, PR by skyrocket2026.

Questions for the author

  1. Why does the PR not state that the lock pins 80dd56e (the relay) and that the mismatch is with upstream HEAD, not with the installed engine?
  2. Would a minimal alternative be acceptable: remove or mark the validator role unsupported, no receipt v6, no static tier?
  3. Can the ownership and killpg hardening be split into its own PR?
  4. Is there any reason the CLI should wrap the root-level guided setup rather than point operators at it?

@skyrocket2026

Copy link
Copy Markdown
Author

Follow-up at 833239e691adca8059b11c24f00e4d89f6144dff after the independent review: the premise correction is
accepted.

The checked-in lock still pins Validator 80dd56e, whose relay entry point is
compatible with the pre-PR adapter. The incompatibility is between that retired
pin/adapter model and reviewed current Validator main (d225e87 at the proposal
freeze, now newer), not proof that an already installed 80dd56e generation
would fail its historical CLI contract. No signed CLI release is published, so
this draft does not claim a deployed generation needs emergency repair.

That leaves the actual owner decision exactly as the review frames it: retain a
Validator role as static signed evidence pending a future transactional
migration, or remove/mark the CLI Validator role unsupported and direct operators
to the Validator's root-level signed bootstrap. This external proposal does not
choose that product/authority decision and remains held in draft. The run-control
and killpg hardening also needs its own focused review (and may be split by the
maintainer); it is not being promoted as evidence for the Validator-role choice.

This follow-up only fixes avoidable proposal/test drift:

  • replaces the nonexistent cathedral update --to lockfile instruction with the
    actual owner-reviewed signed whole-node release command;
  • makes static-quarantine fixtures inspect python_path without granting
    execution;
  • mirrors the Validator blocker in test doubles; and
  • runs generic verified-inode race tests through executable Distill rather than
    the deliberately quarantined Validator role.

Validation: the exact ten previously regressed nodes now pass (10 passed in 710.13s), the lockfile regression passes (2 passed in 2.27s for its focused
class), compile/JSON/Bash/diff checks pass, and an independent read-only review
returned PASS. Neither scripts/gate0_manifest.py nor
tests/gate0_manifest.json changed.

Formal Gate0 remains NO-GO: ./run-gate0.sh exits 4 before execution on the
same seven owner-controlled unmapped tests (five Validator-quarantine cases plus
the static-closure and anti-self-promotion cases). I have not mapped them or
changed the governing requirements. This commit is test/proposal hygiene, not
owner approval, merge readiness, release evidence, or launch evidence.

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.

3 participants