feat(constation): wire production miner-key dial path end to end#44
Conversation
Miner submits a Lium API key plus instance_id; BASE custodies the key, verifies the pod, dials the sidecar over HTTP, and continuously re-checks the digest triangle (allowlist required == sidecar actual == Lium declared) for the whole run. On success it seals a nested constation_bundle, stores it, and the worker forwarder attaches it for prism production constation_ok. Also fixes B1 (attestation verify key was never passed into the master router) and enforces B2 (BASE issues nonces; only prism consumes them). Includes allowlist sealed_manifest_hashes as SoT, surface S1-S8, and fail-closed defaults (constation.enabled=False).
📝 WalkthroughWalkthroughThis change adds production Constation support across persistence, digest validation, sidecar attestation, miner pod binding, bundle orchestration, nonce handling, runtime configuration, master wiring, and HTTP/e2e coverage. ChangesConstation attestation flow
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/base/master/constation/allowlist_repository.py (1)
122-152: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winHandle legacy empty
sealed_manifest_hashesrows inload_allowlist().Migration 0019 added
image_digest_allowlist.sealed_manifest_hasheswith server default{}, whileDigestRecordrejects empty maps. Any pre-migration allowlist row is reconstructed there without a per-rowtry/except, so one legacy record aborts the whole allowlist load and breaks/internal/v1/constation/check_allowlistfor every allowed digest. Catch this per-row as “fail closed” for that digest only, or add a backfill before this validation goes live.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/base/master/constation/allowlist_repository.py` around lines 122 - 152, The load_allowlist method must handle legacy rows whose sealed_manifest_hashes is empty without aborting the entire load. Add per-row handling around DigestRecord construction and registration so that an invalid legacy digest is skipped or otherwise fails closed individually, while valid entries and deny records continue loading normally.
🧹 Nitpick comments (6)
tests/surface/test_constation_production_surface.py (1)
200-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate AST-walk guard for
build_constation_routerwiring across two test files. Both sites independently re-implement the same ~25-line AST parse that asserts everybuild_constation_routercall site passesattestation_verify_key=; a shared helper avoids drift if the guard's requirements evolve (e.g., checkingpod_binding=too).
tests/surface/test_constation_production_surface.py#L200-L234: extract the AST-walk into a shared helper (e.g. in a test-utils module) and call it fromtest_b1s_verify_key_wiring_smoke.tests/unit/test_attestation_verify_key_wiring.py#L71-L102: call the same shared helper fromtest_main_passes_attestation_verify_key_into_build_constation_routerinstead of duplicating the AST loop.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/surface/test_constation_production_surface.py` around lines 200 - 234, Duplicate AST-walk assertions for build_constation_router wiring should be centralized to prevent drift. Extract the shared AST parsing and keyword-validation logic into a test utility, then update tests/surface/test_constation_production_surface.py:200-234 and tests/unit/test_attestation_verify_key_wiring.py:71-102 to call that helper from their respective tests instead of maintaining separate loops; preserve validation that every call passes attestation_verify_key=.src/base/master/constation/allowlist_repository.py (1)
73-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSame reconstruction risk on the rebind-conflict path.
If an existing row has legacy empty
sealed_manifest_hashes, buildingboundhere raisesValueErrorbefore the actual conflict comparison runs. Callers wrap this intry/except ValueError -> 422today, so it degrades gracefully, but the resulting 422 detail will describe asealed_manifest_hashesvalidation error rather than the real rebind conflict — worth a clearer message or handling once theload_allowlistfix above is in place.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/base/master/constation/allowlist_repository.py` around lines 73 - 89, The rebind-conflict path reconstructs the existing row through DigestRecord and can raise validation errors before reporting the actual conflict. Update the bound-record handling in the method containing this block to normalize legacy empty sealed_manifest_hashes consistently with load_allowlist, or otherwise defer that validation until after the digest conflict comparison, so callers receive the intended rebind-conflict message.tests/unit/test_constation_sidecar_client.py (1)
97-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLoosen-to-tighten: assertion allows two outcomes though source is deterministic.
Source code always maps
httpx.TimeoutExceptiontoConstationFailCode.NETWORK_PARTITION(neverSIDECAR_ATTEST_FAILED). Assertingresult.reason in {...}weakens this test's ability to catch a regression that silently changes the mapping.✅ Proposed tightened assertion
- assert result.reason in { - ConstationFailCode.SIDECAR_ATTEST_FAILED, - ConstationFailCode.NETWORK_PARTITION, - } + assert result.reason is ConstationFailCode.NETWORK_PARTITION🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/test_constation_sidecar_client.py` around lines 97 - 110, In test_attest_timeout_maps_to_connection_failure, tighten the result.reason assertion to require ConstationFailCode.NETWORK_PARTITION specifically. Keep the timeout setup and SidecarAttestMiss type assertion unchanged.src/base/compute/constation_runner.py (1)
192-211: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFinal per-sample triangle re-evaluation is unreachable/redundant.
_poll_once(271-283) already returns aConstationVerdict— never aPollSample— wheneverevaluate_digest_trianglefails for a given sample. Sinceresult.samplesonly containsPollSamples that already passed this exact check with the samerequest.required_digest, this loop can never actually fail; it re-derives an outcome that's already guaranteed. Consider dropping it (or converting to anassert/comment documenting the invariant) to reduce confusion about what this code path is defending against.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/base/compute/constation_runner.py` around lines 192 - 211, The final per-sample triangle loop in the enclosing verdict computation is redundant because _poll_once already excludes samples that fail evaluate_digest_triangle. Remove this re-evaluation and its failure return, or replace it with a concise invariant assertion/comment while preserving the existing successful result handling.src/base/compute/constation_sidecar_client.py (1)
27-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
SidecarAttestPhaseenum is defined/exported but unused for validation.Phase validation uses the separate
_VALID_PHASESfrozenset literal instead ofSidecarAttestPhase, leaving two sources of truth for the same three values.Also applies to: 252-258
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/base/compute/constation_sidecar_client.py` around lines 27 - 32, Update phase validation to reuse the exported SidecarAttestPhase enum rather than the separate _VALID_PHASES frozenset literal. Remove the duplicate phase-value source and ensure validation accepts exactly the enum’s START, INTERVAL, and END values.src/base/compute/__init__.py (1)
41-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInconsistent re-export surface for sidecar result/phase types.
SidecarAttestResultis re-exported, but the analogousSidecarEndpointResultunion alias is not, despite both being declared in their source modules'__all__.SidecarAttestPhaseis similarly omitted. If callers are meant to usebase.computeas the package surface for these types, add the missing symbols for consistency.📦 Proposed fix
from base.compute.constation_sidecar_client import ( HttpSidecarAttestor, SidecarAttestHit, SidecarAttestMiss, + SidecarAttestPhase, SidecarAttestResult, ) from base.compute.constation_sidecar_endpoint import ( SidecarEndpointHit, SidecarEndpointMiss, SidecarEndpointMissReason, + SidecarEndpointResult, resolve_sidecar_base_url, )Also applies to: 170-183
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/base/compute/__init__.py` around lines 41 - 56, Update the re-export imports in the package initializer to include the missing SidecarAttestPhase from constation_sidecar_client and SidecarEndpointResult from constation_sidecar_endpoint, alongside the existing sidecar result types. Ensure both symbols are also listed in the initializer’s public export surface if it defines one, preserving base.compute as the access point for these aliases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/challenges/prism/src/prism_challenge/attestation_routes.py`:
- Around line 323-340: Move allowlist.register(record) into the existing try
block in register_digest so ValueError from digest rebind conflicts is converted
to the same 422 HTTPException as record-construction errors. Preserve the
current successful response and exception chaining behavior.
In `@src/base/compute/constation_sidecar_endpoint.py`:
- Around line 62-91: Address the transport trust boundary in
resolve_sidecar_base_url: either enforce HTTPS for sidecar URLs or document and
enforce that default HTTP traffic is confined to a trusted isolated network,
especially when targeting executor.executor_ip_address through a
public-published port. Preserve the existing host and port resolution behavior
while making the confidentiality and MITM protection guarantee explicit.
In `@src/base/master/constation/custody_keys.py`:
- Around line 152-198: Strengthen the variant validation in the pre-forward hook
before constructing ConstationOrchestrationRequest: require a non-empty string
and validate or coerce it through ImageVariant, catching ValueError and
returning without forwarding when invalid. Replace the current variant is not
None gate while preserving the existing incomplete-identity debug behavior for
missing or invalid variants.
In `@src/base/master/constation/orchestrator.py`:
- Around line 313-336: Update _issue_blocking so durable or awaitable nonce
issuance runs on the already active executor/event loop instead of invoking
_run_coro_in_worker with a newly created loop per poll. Preserve the existing
asyncio.get_running_loop() check and synchronous issuance behavior, and reuse
the established executor-loop scheduling mechanism so the shared session factory
remains on its owning loop.
In `@src/base/master/constation/pod_binding.py`:
- Around line 66-164: Centralize the probe error-classification flow and
nonblank validation used by pod_binding.register with the existing
LiumKeyCustody.register implementation and its _require_nonblank helper. Update
register to call the shared custody logic, remove the duplicated local probe
handling and helper, and preserve the current fail-closed verdicts and
pod-binding behavior.
In `@src/base/master/worker_reconciliation.py`:
- Around line 401-414: Decouple the _constation_hook invocation in _forward from
reconcile_once’s session_scope so attestation work cannot hold the
reconciliation DB transaction open. Schedule the hook only after the transaction
commits, preferably as a background task, while preserving the existing payload,
winner hotkey, exception logging, and forward-success behavior; if post-commit
dispatch is not available, enforce an explicit bounded timeout.
---
Outside diff comments:
In `@src/base/master/constation/allowlist_repository.py`:
- Around line 122-152: The load_allowlist method must handle legacy rows whose
sealed_manifest_hashes is empty without aborting the entire load. Add per-row
handling around DigestRecord construction and registration so that an invalid
legacy digest is skipped or otherwise fails closed individually, while valid
entries and deny records continue loading normally.
---
Nitpick comments:
In `@src/base/compute/__init__.py`:
- Around line 41-56: Update the re-export imports in the package initializer to
include the missing SidecarAttestPhase from constation_sidecar_client and
SidecarEndpointResult from constation_sidecar_endpoint, alongside the existing
sidecar result types. Ensure both symbols are also listed in the initializer’s
public export surface if it defines one, preserving base.compute as the access
point for these aliases.
In `@src/base/compute/constation_runner.py`:
- Around line 192-211: The final per-sample triangle loop in the enclosing
verdict computation is redundant because _poll_once already excludes samples
that fail evaluate_digest_triangle. Remove this re-evaluation and its failure
return, or replace it with a concise invariant assertion/comment while
preserving the existing successful result handling.
In `@src/base/compute/constation_sidecar_client.py`:
- Around line 27-32: Update phase validation to reuse the exported
SidecarAttestPhase enum rather than the separate _VALID_PHASES frozenset
literal. Remove the duplicate phase-value source and ensure validation accepts
exactly the enum’s START, INTERVAL, and END values.
In `@src/base/master/constation/allowlist_repository.py`:
- Around line 73-89: The rebind-conflict path reconstructs the existing row
through DigestRecord and can raise validation errors before reporting the actual
conflict. Update the bound-record handling in the method containing this block
to normalize legacy empty sealed_manifest_hashes consistently with
load_allowlist, or otherwise defer that validation until after the digest
conflict comparison, so callers receive the intended rebind-conflict message.
In `@tests/surface/test_constation_production_surface.py`:
- Around line 200-234: Duplicate AST-walk assertions for build_constation_router
wiring should be centralized to prevent drift. Extract the shared AST parsing
and keyword-validation logic into a test utility, then update
tests/surface/test_constation_production_surface.py:200-234 and
tests/unit/test_attestation_verify_key_wiring.py:71-102 to call that helper from
their respective tests instead of maintaining separate loops; preserve
validation that every call passes attestation_verify_key=.
In `@tests/unit/test_constation_sidecar_client.py`:
- Around line 97-110: In test_attest_timeout_maps_to_connection_failure, tighten
the result.reason assertion to require ConstationFailCode.NETWORK_PARTITION
specifically. Keep the timeout setup and SidecarAttestMiss type assertion
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 43dfab1c-1207-488a-a148-d3568831ca5d
📒 Files selected for processing (47)
alembic/versions/0019_allowlist_sealed_manifest_hashes.pypackages/challenges/prism/src/prism_challenge/attestation_routes.pyscripts/e2e_lium_attestation.pyscripts/prism_lium_attestation_e2e.pyscripts/surface_constation.shsrc/base/cli_app/main.pysrc/base/compute/__init__.pysrc/base/compute/constation_custody.pysrc/base/compute/constation_pod.pysrc/base/compute/constation_runner.pysrc/base/compute/constation_sidecar_client.pysrc/base/compute/constation_sidecar_endpoint.pysrc/base/compute/constation_triangle.pysrc/base/compute/constation_types.pysrc/base/compute/digest_allowlist.pysrc/base/config/settings.pysrc/base/db/models.pysrc/base/master/constation/__init__.pysrc/base/master/constation/allowlist_repository.pysrc/base/master/constation/attestation_keys.pysrc/base/master/constation/bundle_seal.pysrc/base/master/constation/custody_keys.pysrc/base/master/constation/orchestrator.pysrc/base/master/constation/pod_binding.pysrc/base/master/constation/routes.pysrc/base/master/worker_reconciliation.pytests/__init__.pytests/surface/__init__.pytests/surface/constation_surface_helpers.pytests/surface/test_constation_production_surface.pytests/surface/test_constation_production_surface_prism_http.pytests/unit/test_attestation_http.pytests/unit/test_attestation_verify_key_wiring.pytests/unit/test_compute_constation_service.pytests/unit/test_compute_digest_allowlist.pytests/unit/test_constation_bundle_seal.pytests/unit/test_constation_main_wiring.pytests/unit/test_constation_orchestrator.pytests/unit/test_constation_pod.pytests/unit/test_constation_pod_binding.pytests/unit/test_constation_settings.pytests/unit/test_constation_sidecar_client.pytests/unit/test_constation_sidecar_endpoint.pytests/unit/test_constation_triangle.pytests/unit/test_digest_allowlist_repository.pytests/unit/test_e2e_lium_attestation_offline.pytests/unit/test_validator_challenge_adapters.py
| async def register_digest(request: Request, body: RegisterDigestBody) -> dict[str, str]: | ||
| ensure_default_constation_services(request.app) | ||
| allowlist: DigestAllowlist = request.app.state.digest_allowlist | ||
| record = DigestRecord( | ||
| commit_sha=body.commit_sha, | ||
| tree_sha=body.tree_sha, | ||
| variant=ImageVariant(body.variant.strip().lower()), | ||
| digest=body.digest, | ||
| ) | ||
| try: | ||
| record = DigestRecord( | ||
| commit_sha=body.commit_sha, | ||
| tree_sha=body.tree_sha, | ||
| variant=ImageVariant(body.variant.strip().lower()), | ||
| digest=body.digest, | ||
| sealed_manifest_hashes=body.sealed_manifest_hashes, | ||
| ) | ||
| except ValueError as exc: | ||
| raise HTTPException( | ||
| status.HTTP_422_UNPROCESSABLE_ENTITY, | ||
| detail=str(exc), | ||
| ) from exc | ||
| allowlist.register(record) | ||
| return {"status": "registered", "digest": record.digest} |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
allowlist.register(record) sits outside the ValueError handler — rebind conflicts 500 instead of 422.
DigestAllowlist.register() raises ValueError when the same digest is rebound to a different commit/tree/variant. Here that call (line 339) is outside the try/except ValueError block, so a legitimate conflict crashes with an unhandled 500. The equivalent endpoint in src/base/master/constation/routes.py correctly wraps both the DigestRecord construction and the allowlist_repo.register(record) call in the same try block, converting ValueError to a 422 — this file should match that pattern.
🐛 Proposed fix to catch registration conflicts too
try:
record = DigestRecord(
commit_sha=body.commit_sha,
tree_sha=body.tree_sha,
variant=ImageVariant(body.variant.strip().lower()),
digest=body.digest,
sealed_manifest_hashes=body.sealed_manifest_hashes,
)
+ allowlist.register(record)
except ValueError as exc:
raise HTTPException(
status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=str(exc),
) from exc
- allowlist.register(record)
return {"status": "registered", "digest": record.digest}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async def register_digest(request: Request, body: RegisterDigestBody) -> dict[str, str]: | |
| ensure_default_constation_services(request.app) | |
| allowlist: DigestAllowlist = request.app.state.digest_allowlist | |
| record = DigestRecord( | |
| commit_sha=body.commit_sha, | |
| tree_sha=body.tree_sha, | |
| variant=ImageVariant(body.variant.strip().lower()), | |
| digest=body.digest, | |
| ) | |
| try: | |
| record = DigestRecord( | |
| commit_sha=body.commit_sha, | |
| tree_sha=body.tree_sha, | |
| variant=ImageVariant(body.variant.strip().lower()), | |
| digest=body.digest, | |
| sealed_manifest_hashes=body.sealed_manifest_hashes, | |
| ) | |
| except ValueError as exc: | |
| raise HTTPException( | |
| status.HTTP_422_UNPROCESSABLE_ENTITY, | |
| detail=str(exc), | |
| ) from exc | |
| allowlist.register(record) | |
| return {"status": "registered", "digest": record.digest} | |
| async def register_digest(request: Request, body: RegisterDigestBody) -> dict[str, str]: | |
| ensure_default_constation_services(request.app) | |
| allowlist: DigestAllowlist = request.app.state.digest_allowlist | |
| try: | |
| record = DigestRecord( | |
| commit_sha=body.commit_sha, | |
| tree_sha=body.tree_sha, | |
| variant=ImageVariant(body.variant.strip().lower()), | |
| digest=body.digest, | |
| sealed_manifest_hashes=body.sealed_manifest_hashes, | |
| ) | |
| allowlist.register(record) | |
| except ValueError as exc: | |
| raise HTTPException( | |
| status.HTTP_422_UNPROCESSABLE_ENTITY, | |
| detail=str(exc), | |
| ) from exc | |
| return {"status": "registered", "digest": record.digest} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/challenges/prism/src/prism_challenge/attestation_routes.py` around
lines 323 - 340, Move allowlist.register(record) into the existing try block in
register_digest so ValueError from digest rebind conflicts is converted to the
same 422 HTTPException as record-construction errors. Preserve the current
successful response and exception chaining behavior.
| def resolve_sidecar_base_url( | ||
| pod_raw: Mapping[str, Any], | ||
| *, | ||
| internal_port: int, | ||
| scheme: str = "http", | ||
| ) -> SidecarEndpointResult: | ||
| """Resolve ``{scheme}://{host}:{external_port}`` for the sidecar. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pod_raw: | ||
| Raw Lium ``PodDetailResponse`` mapping (e.g. ``LiumPodRead.raw``). | ||
| internal_port: | ||
| Container-internal sidecar listen port to look up in ``ports_mapping``. | ||
| scheme: | ||
| URL scheme; defaults to ``http``. | ||
| """ | ||
| host = _extract_host(pod_raw) | ||
| if host is None: | ||
| return SidecarEndpointMiss(SidecarEndpointMissReason.POD_ENDPOINT_MISSING) | ||
|
|
||
| mapping_result = _normalize_ports_mapping(pod_raw.get("ports_mapping")) | ||
| if isinstance(mapping_result, SidecarEndpointMissReason): | ||
| return SidecarEndpointMiss(mapping_result) | ||
|
|
||
| external = _lookup_external_port(mapping_result, internal_port) | ||
| if external is None: | ||
| return SidecarEndpointMiss(SidecarEndpointMissReason.SIDECAR_PORT_UNPUBLISHED) | ||
|
|
||
| return SidecarEndpointHit(base_url=f"{scheme}://{_format_host(host)}:{external}") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether callers ever override scheme to https, and whether network policy docs mention this.
rg -n 'resolve_sidecar_base_url\(' -A3 -B3
rg -n 'scheme\s*=' src/base/compute/constation_sidecar_endpoint.py src/base/master/constationRepository: BaseIntelligence/base
Length of output: 159
🏁 Script executed:
#!/bin/bash
set -u
echo "Files matching constation_sidecar_endpoint.py or sidecar/client/constation:"
git ls-files | rg 'constation.*(sidecar|client)$|sidecar.*endpoint|sidecar.*client|constation_sidecar' || true
echo
echo "Search resolve_sidecar_base_url usages:"
rg -n 'resolve_sidecar_base_url' . || true
echo
echo "Search HttpSidecarAttestor / sidecar attestor / sidecar_base_url:"
rg -n 'HttpSidecarAttestor|sidecar.*attest|sidecar_base_url|base_url' . || true
echo
echo "Search scheme occurrences under relevant paths if present:"
rg -n 'scheme\s*=|sidecar.*https|https' src test tests .github README.md 2>/dev/null || trueRepository: BaseIntelligence/base
Length of output: 50378
🏁 Script executed:
#!/bin/bash
set -u
echo "Candidate files:"
git ls-files | rg -i 'constation|sidecar|attest' || true
echo
echo "Top-level source tree snippets around constation:"
for f in $(git ls-files | rg -i 'constation.*sidecar.*endpoint|constation.*sidecar.*client|constation.*sidecar'); do
echo "--- $f"
wc -l "$f"
ast-grep outline "$f" --view compact 2>/dev/null || true
doneRepository: BaseIntelligence/base
Length of output: 3929
🏁 Script executed:
#!/bin/bash
set -u
echo "--- src/base/compute/constation_sidecar_endpoint.py lines 1-120"
sed -n '1,120p' src/base/compute/constation_sidecar_endpoint.py
echo
echo "--- src/base/compute/constation_sidecar_endpoint.py lines 160-200"
sed -n '160,200p' src/base/compute/constation_sidecar_endpoint.py
echo
echo "--- src/base/compute/constation_runner.py lines 300-380"
sed -n '300,380p' src/base/compute/constation_runner.py
echo
echo "--- src/base/compute/constation_sidecar_client.py lines 1-145"
sed -n '1,145p' src/base/compute/constation_sidecar_client.py
echo
echo "--- focused docs/comments for sidecar/constation private network/HTTP"
rg -n -C 2 'sidecar|constation|Lium|Private|private|overlay|HTTP|http|HTTPS|HTTPS|TLS|tls|plaintext|eavesdrop|MITM|in-flight' README.md deploy scripts packages docs src tests/surface tests/unit/test_e2e_lium_attestation_offline.py scripts/e2e_lium_attestation.py scripts/prism_lium_attestation_e2e.py 2>/dev/null | head -n 240Repository: BaseIntelligence/base
Length of output: 29873
Document or enforce the trust boundary for sidecar HTTP attestation.
resolve_sidecar_base_url always produces http:// unless the caller overrides scheme, and the actual callers use the default against executor.executor_ip_address. If this dial-out uses a public-published sidecar port, it is plaintext on the wire; the HMAC covers payload integrity, not transport confidentiality or MITM resistance. Add an explicit design note, traffic-isolation guarantee, or switch to HTTPS for transport protection.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/base/compute/constation_sidecar_endpoint.py` around lines 62 - 91,
Address the transport trust boundary in resolve_sidecar_base_url: either enforce
HTTPS for sidecar URLs or document and enforce that default HTTP traffic is
confined to a trusted isolated network, especially when targeting
executor.executor_ip_address through a public-published port. Preserve the
existing host and port resolution behavior while making the confidentiality and
MITM protection guarantee explicit.
| md = dict(metadata or {}) | ||
| required_digest = md.get("required_digest") or md.get("digest") | ||
| commit_sha = md.get("commit_sha") | ||
| tree_sha = md.get("tree_sha") | ||
| variant = md.get("variant") | ||
| sealed = md.get("sealed_manifest_hashes") | ||
| if not ( | ||
| isinstance(required_digest, str) | ||
| and required_digest | ||
| and isinstance(commit_sha, str) | ||
| and commit_sha | ||
| and isinstance(tree_sha, str) | ||
| and tree_sha | ||
| and variant is not None | ||
| and isinstance(sealed, Mapping) | ||
| and sealed | ||
| ): | ||
| logger.debug( | ||
| "constation pre-forward hook skipped: incomplete identity " | ||
| "on work_unit_id=%s miner_hotkey=%s", | ||
| work_unit_id, | ||
| miner_hotkey, | ||
| ) | ||
| return | ||
| try: | ||
| await orchestrator.run( | ||
| ConstationOrchestrationRequest( | ||
| work_unit_id=work_unit_id, | ||
| miner_hotkey=miner_hotkey, | ||
| required_digest=required_digest, | ||
| commit_sha=commit_sha, | ||
| tree_sha=tree_sha, | ||
| variant=variant, | ||
| sealed_manifest_hashes=dict(sealed), | ||
| duration_seconds=float( | ||
| md.get("duration_seconds", duration_seconds) | ||
| ), | ||
| pod_id=md.get("pod_id") | ||
| if isinstance(md.get("pod_id"), str) | ||
| else None, | ||
| instance_id=( | ||
| md.get("instance_id") | ||
| if isinstance(md.get("instance_id"), str) | ||
| else None | ||
| ), | ||
| ) | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the declared type of `variant` on ConstationOrchestrationRequest and whether
# str values are coerced to ImageVariant anywhere before use.
rg -n -A5 'class ConstationOrchestrationRequest' src/base/master/constation/orchestrator.py
rg -n 'variant' src/base/master/constation/orchestrator.py
rg -n 'class ImageVariant' src/base/compute/digest_allowlist.pyRepository: BaseIntelligence/base
Length of output: 675
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "custody_keys pre-forward section:"
sed -n '130,210p' src/base/master/constation/custody_keys.py
echo
echo "orchestrator request handling and ImageVariant enum:"
sed -n '1,90p' src/base/compute/digest_allowlist.py
sed -n '45,225p' src/base/master/constation/orchestrator.py
echo
echo "surface helper request usage:"
rg -n -A8 -B5 'orchestration_request|ConstationOrchestrationRequest|ImageVariant' tests/surface/constation_surface_helpers.pyRepository: BaseIntelligence/base
Length of output: 13971
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "ConstationRunner.build_bundle and digest lookup usages:"
rg -n -A12 -B8 'async def build_bundle|class ConstationRunner|sealed_manifest_hashes|variant_mismatch|constation_ok|DigestRecord' src base tests | sed -n '1,260p'
echo
echo "all direct ConstationOrchestrationRequest construction sites:"
rg -n -A6 -B6 'ConstationOrchestrationRequest\(' src testsRepository: BaseIntelligence/base
Length of output: 18584
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "ConstationRunner build_bundle / seal fields:"
sed -n '95,175p' src/base/compute/constation_runner.py
echo
echo "DigestRecord and allowlist lookup implementations:"
rg -n -A40 'class DigestRecord|def _require_variant|def .*_commit.*|def .*variant|constation_ok|lookup' src/base/compute/digest_allowlist.py tests | sed -n '1,260p'
echo
echo "_require_variant exact behavior:"
python3 - <<'PY'
import ast
from pathlib import Path
path = Path("src/base/compute/digest_allowlist.py")
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "_require_variant":
print(ast.get_source_segment(path.read_text(), node))
PYRepository: BaseIntelligence/base
Length of output: 23742
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "ProductionConstationOrchestrator bundle sealing failure path:"
sed -n '180,260p' src/base/master/constation/orchestrator.py
echo
echo "digest_allowlist.py file-level exact lines of _require_variant and lookup comparison:"
sed -n '82,91p' src/base/compute/digest_allowlist.py
sed -n '215,254p' src/base/compute/digest_allowlist.py
echo
echo "behavioral probe matching the repo variant coercion:"
python3 - <<'PY'
from enum import StrEnum
class ImageVariant(StrEnum):
CPU = "cpu"
CUDA = "cuda"
def require_variant(value):
if isinstance(value, ImageVariant):
return value
try:
return ImageVariant(str(value).strip().lower())
except ValueError as exc:
raise ValueError(f"variant must be one of {[v.value for v in ImageVariant]}, got {value!r}") from exc
for value in ["".strip(), "cpu", " CuDa ", None, {"kind": "cuda"}]:
try:
print(repr(value), "=>", require_variant(value))
except Exception as exc:
print(repr(value), "=> raises", type(exc).__name__, str(exc))
PYRepository: BaseIntelligence/base
Length of output: 5618
Fail closed on invalid variant before forwarding.
variant is not None is weaker than the checks for sibling identity fields and weaker than ImageVariant.__init__ expectations. An empty string or an invalid type/value passes this gate and causes a ValueError during DigestRecord construction, which the orchestrator treats as RUN_INCOMPLETE rather than a missing variant identity. Require a non-empty string that can be coerced to ImageVariant here, or convert it here and fail-closed on ValueError.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/base/master/constation/custody_keys.py` around lines 152 - 198,
Strengthen the variant validation in the pre-forward hook before constructing
ConstationOrchestrationRequest: require a non-empty string and validate or
coerce it through ImageVariant, catching ValueError and returning without
forwarding when invalid. Replace the current variant is not None gate while
preserving the existing incomplete-identity debug behavior for missing or
invalid variants.
| def _issue_blocking(nonce_service: NonceIssuer, binding: NonceBinding) -> NonceRecord: | ||
| """Call ``issue`` from a sync context (runner poll_nonce_fn).""" | ||
| issue = nonce_service.issue | ||
| # Coroutine function (DurableAttestationNonceService.issue) | ||
| if inspect.iscoroutinefunction(issue): | ||
| return _run_coro_in_worker(issue(binding)) | ||
| record = issue(binding) | ||
| if inspect.isawaitable(record): | ||
| return _run_coro_in_worker(record) | ||
| return record | ||
|
|
||
|
|
||
| def _run_coro_in_worker(coro: Awaitable[NonceRecord]) -> NonceRecord: | ||
| """Run ``coro`` on a fresh loop in a worker thread (no same-loop deadlock).""" | ||
| import concurrent.futures | ||
|
|
||
| async def _await_once() -> NonceRecord: | ||
| return await coro | ||
|
|
||
| def _thread_main() -> NonceRecord: | ||
| return asyncio.run(_await_once()) | ||
|
|
||
| with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: | ||
| return pool.submit(_thread_main).result(timeout=60.0) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect DurableAttestationNonceService.issue to check for event-loop-bound resources
# (DB pools/engines, asyncio.Lock, etc.) created outside the per-call scope.
rg -n "class DurableAttestationNonceService" -A 80 src/base/compute/attestation_nonce.pyRepository: BaseIntelligence/base
Length of output: 159
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Find orchestrator and nonce-related files:"
fd -a 'orchestrator|attestation_nonce|nonce|constation' . | sed 's#^\./##' | head -100
echo
echo "Search relevant symbols:"
rg -n "DurableAttestationNonceService|NonceBinding|NonceRecord|NonceIssuer|poll_nonce_fn|_issue_blocking|_run_coro_in_worker" -S .Repository: BaseIntelligence/base
Length of output: 16025
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "orchestrator relevant sections:"
sed -n '1,380p' src/base/master/constation/orchestrator.py | cat -n
echo
echo "DurableAttestationNonceService source:"
sed -n '1,170p' src/base/master/constation/nonce_repository.py | cat -n
echo
echo "runners/poll paths:"
sed -n '240,370p' src/base/compute/constation_runner.py | cat -nRepository: BaseIntelligence/base
Length of output: 27041
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Find session_scope/session_factory definitions and usages:"
fd -a 'session.py|database|factory|engine' src/base | sed 's#^\./##' | head -100
rg -n "async_sessionmaker|create_async_engine|def session_scope|async def session_scope|session_scope_fn|DurableAttestationNonceService\\(" src/base src -S
python3 - <<'PY'
from pathlib import Path
for path in Path("src/base").rglob("*.py"):
text = path.read_text(errors="ignore")
if "DurableAttestationNonceService" in text or "async_sessionmaker" in text or "session_scope" in text:
print(path)
for i, line in enumerate(text.splitlines(), 1):
if any(s in line for s in ["DurableAttestationNonceService", "async_sessionmaker", "session_scope", "create_async_engine"]):
print(f"{i}: {line}")
PYRepository: BaseIntelligence/base
Length of output: 21076
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "src/base/db/session.py:"
sed -n '1,90p' src/base/db/session.py | cat -n
echo
echo "DurableAttestationNonceService wiring around CLI:"
sed -n '1140,1175p' src/base/cli_app/main.py | cat -n
echo
echo "Search DurableAttestationNonceService construction beyond CLI:"
rg -n "DurableAttestationNonceService\\(" src tests packages 2>/dev/null | sed -n '1,120p'
echo
echo "Search production config/dependency injection for constation nonce service:"
rg -n "constation.*nonce|nonce.*constation|DurableAttestationNonceService|async_sessionmaker|make_session_factory|session_factory" -S src base packages 2>/dev/null | sed -n '1,160p'Repository: BaseIntelligence/base
Length of output: 21444
Keep durable nonce calls on an active executor loop.
The durable path blocks the calling master loop in _issue_blocking for up to 60s per nonce, and DurableAttestationNonceService.issue uses the shared async_sessionmaker/session_scope created from the main-loop engine. Running that coroutine in _run_coro_in_worker with a fresh asyncio.run() thread can fail only if the engine/session factory has loop-bound resources, and the pool executor is recreated per call. Keep this asyncio.get_running_loop() check, but avoid launching the worker loop directly from this tight poll path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/base/master/constation/orchestrator.py` around lines 313 - 336, Update
_issue_blocking so durable or awaitable nonce issuance runs on the already
active executor/event loop instead of invoking _run_coro_in_worker with a newly
created loop per poll. Preserve the existing asyncio.get_running_loop() check
and synchronous issuance behavior, and reuse the established executor-loop
scheduling mechanism so the shared session factory remains on its owning loop.
| hotkey = _require_nonblank("miner_hotkey", miner_hotkey) | ||
| key = _require_nonblank("api_key", api_key) | ||
| pod_id = _require_nonblank("instance_id", instance_id) | ||
|
|
||
| client = self.custody.client_factory(key) | ||
| try: | ||
| await self.custody.probe_fn(client) | ||
| except LiumAuthError: | ||
| logger.warning("lium key probe rejected (401) for miner_hotkey=%s", hotkey) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.LIUM_AUTH_REVOKED, | ||
| detail="probe_401", | ||
| ) | ||
| except LiumError as exc: | ||
| logger.warning( | ||
| "lium key probe failed for miner_hotkey=%s status=%s", | ||
| hotkey, | ||
| getattr(exc, "status_code", None), | ||
| ) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.PROBE_FAILED, | ||
| detail=type(exc).__name__, | ||
| ) | ||
|
|
||
| try: | ||
| pod = await client.get_pod_raw(pod_id) | ||
| except LiumAuthError: | ||
| logger.warning( | ||
| "lium get_pod rejected (401) for miner_hotkey=%s instance_id=%s", | ||
| hotkey, | ||
| pod_id, | ||
| ) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.LIUM_AUTH_REVOKED, | ||
| detail="get_pod_401", | ||
| ) | ||
| except LiumNotFoundError: | ||
| logger.warning( | ||
| "lium get_pod not found for miner_hotkey=%s instance_id=%s", | ||
| hotkey, | ||
| pod_id, | ||
| ) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.POD_HOTKEY_MISMATCH, | ||
| detail="pod_not_found", | ||
| ) | ||
| except LiumRateLimitError: | ||
| logger.warning( | ||
| "lium get_pod rate limited for miner_hotkey=%s instance_id=%s", | ||
| hotkey, | ||
| pod_id, | ||
| ) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.LIUM_RATE_LIMITED, | ||
| detail="get_pod_429", | ||
| ) | ||
| except LiumError as exc: | ||
| logger.warning( | ||
| "lium get_pod failed for miner_hotkey=%s instance_id=%s status=%s", | ||
| hotkey, | ||
| pod_id, | ||
| getattr(exc, "status_code", None), | ||
| ) | ||
| return ConstationVerdict( | ||
| ok=False, | ||
| reason=ConstationFailCode.PROBE_FAILED, | ||
| detail=type(exc).__name__, | ||
| ) | ||
|
|
||
| bound = assert_pod_bound(pod_raw=pod.raw, expected_hotkey=hotkey) | ||
| if not bound.ok: | ||
| logger.warning( | ||
| "pod bind failed for miner_hotkey=%s instance_id=%s reason=%s", | ||
| hotkey, | ||
| pod_id, | ||
| bound.reason, | ||
| ) | ||
| return bound | ||
|
|
||
| self.custody.store_probed_key(miner_hotkey=hotkey, api_key=key) | ||
| self._instance_by_hotkey[hotkey] = pod_id | ||
| logger.info( | ||
| "miner pod bound for miner_hotkey=%s instance_id=%s", | ||
| hotkey, | ||
| pod_id, | ||
| ) | ||
| return ConstationVerdict(ok=True, reason=ConstationFailCode.OK) | ||
|
|
||
|
|
||
| def _require_nonblank(field_name: str, value: str) -> str: | ||
| normalized = value.strip() | ||
| if not normalized: | ||
| raise ValueError(f"{field_name} must be a non-empty string, got {value!r}") | ||
| return normalized |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Duplicated fail-closed probe logic and helper across custody.py and pod_binding.py.
register() (Lines 70-90) reimplements the same probe → classify-error logic already in LiumKeyCustody.register (constation_custody.py Lines 88-116), and _require_nonblank (Lines 160-164) is a verbatim copy of the identical helper in constation_custody.py. Since this is a fail-closed security path, future changes to error classification risk silently diverging between the two copies.
♻️ Suggested extraction
# constation_custody.py
class LiumKeyCustody:
+ async def probe_only(self, api_key: str) -> tuple[LiumClient, ConstationVerdict | None]:
+ """Probe api_key; return (client, None) on success or (client, fail_verdict)."""
+ key = _require_nonblank("api_key", api_key)
+ client = self.client_factory(key)
+ try:
+ await self.probe_fn(client)
+ except LiumAuthError:
+ return client, ConstationVerdict(ok=False, reason=ConstationFailCode.LIUM_AUTH_REVOKED, detail="probe_401")
+ except LiumError as exc:
+ return client, ConstationVerdict(ok=False, reason=ConstationFailCode.PROBE_FAILED, detail=type(exc).__name__)
+ return client, None # pod_binding.py
- client = self.custody.client_factory(key)
- try:
- await self.custody.probe_fn(client)
- except LiumAuthError:
- ...
- except LiumError as exc:
- ...
+ client, fail = await self.custody.probe_only(key)
+ if fail is not None:
+ return fail🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/base/master/constation/pod_binding.py` around lines 66 - 164, Centralize
the probe error-classification flow and nonblank validation used by
pod_binding.register with the existing LiumKeyCustody.register implementation
and its _require_nonblank helper. Update register to call the shared custody
logic, remove the duplicated local probe handling and helper, and preserve the
current fail-closed verdicts and pod-binding behavior.
| async def _forward(self, primary: WorkAssignment, winner: WorkerAssignment) -> bool: | ||
| if self._constation_hook is not None: | ||
| try: | ||
| await self._constation_hook( | ||
| work_unit_id=primary.work_unit_id, | ||
| miner_hotkey=str(winner.miner_hotkey or ""), | ||
| metadata=dict(primary.payload or {}), | ||
| ) | ||
| except Exception: | ||
| logger.exception( | ||
| "constation pre-forward hook failed for unit %s " | ||
| "(forward continues)", | ||
| primary.work_unit_id, | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm orchestrator.run's real-world duration bound and whether _forward/hook is ever
# invoked outside the reconciliation DB transaction elsewhere.
rg -n -A15 'async def run' src/base/master/constation/orchestrator.py
rg -n 'session_scope' src/base/master/worker_reconciliation.pyRepository: BaseIntelligence/base
Length of output: 1051
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== orchestrator run implementation =="
sed -n '1,220p' src/base/master/constation/orchestrator.py
echo
echo "== worker reconciliation relevant sections =="
sed -n '150,230p' src/base/master/worker_reconciliation.py
sed -n '230,430p' src/base/master/worker_reconciliation.py
echo
echo "== constation hook wiring =="
fd -a 'custody_keys.py|constation.*' src | sed 's#^\./##' | rg 'custody_keys|constation|worker_reconciliation|orchestrator' || true
rg -n -A30 -B10 'make_constation_pre_forward_hook|constation_hook|ConstationPreForwardHook|build_constation_runtime|_constation_hook' srcRepository: BaseIntelligence/base
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== custody_keys pre-forward hook =="
sed -n '127,240p' src/base/master/constation/custody_keys.py
echo
echo "== settings / constation bounds =="
rg -n -A8 -B6 'class .*Constation|duration_seconds|poll_timeout_seconds|gap_budget_seconds|min_interval_seconds|max_interval_seconds|max_polls|sidecar_timeout_seconds' src -g '*.py'
echo
echo "== runner duration handling =="
sed -n '1,260p' src/base/compute/constation_runner.py
echo
echo "== orchestrator rest =="
sed -n '220,380p' src/base/master/constation/orchestrator.pyRepository: BaseIntelligence/base
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== bundle store put tail =="
sed -n '330,400p' src/base/master/constation/orchestrator.py
echo
echo "== session_scope implementation =="
rg -n -A40 'def session_scope|async def session_scope|class .*Session.*scope|contextmanager' src/base/db src/base/master/worker_reconciliation.py src/cli_app/main.py
echo
echo "== test coverage for hook timing =="
rg -n -A25 'test_reconciliation_constation_hook_called_before_forward|constation_hook_called|WorkerReconciliationService|reconcile_once|_forward' src -g '*test*.py' -g '*.py'Repository: BaseIntelligence/base
Length of output: 3956
Decouple the constation attestation hook from the reconciliation DB transaction.
_forward runs inside reconcile_once’s session_scope, while the production hook invokes orchestrator.run(...), which drives a ConstationRunner with real timing and sidecar polling (default 5 min run budget, configurable max_polls, and sidecar timeout). A slow attestation run for one accepted unit can hold the DB transaction open and serialize the rest of the reconciliation pass until the run or its retries fail. Run the hook after the transaction commits, dispatch it as a background task, or wrap it with an explicit timeout.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/base/master/worker_reconciliation.py` around lines 401 - 414, Decouple
the _constation_hook invocation in _forward from reconcile_once’s session_scope
so attestation work cannot hold the reconciliation DB transaction open. Schedule
the hook only after the transaction commits, preferably as a background task,
while preserving the existing payload, winner hotkey, exception logging, and
forward-success behavior; if post-commit dispatch is not available, enforce an
explicit bounded timeout.
Summary
instance_id.executor.miner_hotkeybind), dials sidecarPOST /v1/sidecar/attest, continuously re-checks digest triangle (allowlist required == sidecar actual == Lium declared).constation_bundle→ store → worker forwarder attaches for prism productionconstation_ok(tier ≤ 1).Changes
ConstationSettings(BASE_CONSTATION__*), fail-closedenabled=Falseevaluate_digest_triangle, sidecar URL resolver, HTTP sidecar clientsealed_manifest_hashes+ alembic0019MinerPodBinding+POST /internal/v1/constation/register_miner_keyProductionConstationOrchestrator(issue → run → seal → put, never consume)scripts/surface_constation.shTest plan
ruff check .+ruff format --check .mypy src tests— cleanpytest --cov=base --cov-fail-under=80— 2431 passed, coverage 83.73%bash scripts/surface_constation.sh— 10/10test_prism_no_tee_absenceintactattestation_verify_key.consume(CI
Notes
switch-templateafter verify.LIUM_API_KEYin the operator environment (offline path covered).Summary by CodeRabbit
New Features
Bug Fixes