rein-aharness/scripts/prove-metered-runtime.py
tegwick ee3800f26d
Some checks failed
Governed runtime contract / contract (push) Failing after 32s
Address the pinned CLI through its namespace path in artifact proof
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
2026-09-09 23:09:30 +02:00

254 lines
13 KiB
Python

"""Installed owner + CLI + bwrap proof with a fake provider and an empty fake queue.
Run using the candidate's python -I -B. Never acquires a real credential.
"""
from __future__ import annotations
import argparse
from datetime import UTC, datetime, timedelta
import hashlib
import http.server
import importlib
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import threading
import yaml
from glas_harness.contract import ExecutionLimits, OperationalReadiness
from glas_harness.profiles import ProfileCatalog
from llm_connect.messages_gate import MessagesPolicy
from llm_connect.models import RunConfig
from rein_aharness.adapter import AgenticClaudeCodeAdapter
from rein_aharness.execution_cancel import ExecutionCancel
from rein_aharness.messages_owner import MessagesOwner
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig
from rein_aharness.repository_grant import RepositoryGrant
from rein_aharness.request_admission import RequestLedger
from rein_aharness.spend_admission import SpendLedger, SpendPolicy, digest
from sandboxer.extensions.runtime import runtime_digest
from sandboxer.models import Consumer, SandboxCreateRequest, SandboxExecRequest
from sandboxer.profiles.loader import profiles_dir
from sandboxer.extensions.registry import extensions_dir
BETAS = (
"claude-code-20250219", "interleaved-thinking-2025-05-14",
"thinking-token-count-2026-05-13", "context-management-2025-06-27",
"prompt-caching-scope-2026-01-05", "effort-2025-11-24",
)
DUMMY_KEY = "synthetic-provider-key-no-live-authority"
def response_bytes():
events = [
{"type": "message_start", "message": {"id": "msg_fixture", "type": "message",
"role": "assistant", "model": "claude-sonnet-4-6", "content": [],
"stop_reason": None, "stop_sequence": None,
"usage": {"input_tokens": 100, "output_tokens": 0,
"cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}},
{"type": "content_block_start", "index": 0,
"content_block": {"type": "text", "text": ""}},
{"type": "content_block_delta", "index": 0,
"delta": {"type": "text_delta", "text": "fixture complete"}},
{"type": "content_block_stop", "index": 0},
{"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None},
"usage": {"input_tokens": 100, "output_tokens": 10,
"cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}},
{"type": "message_stop"},
]
return "".join("event: " + x["type"] + "\ndata: " + json.dumps(x) + "\n\n" for x in events).encode()
class FixtureServer(http.server.ThreadingHTTPServer):
provider_calls = 0
queue_claims = 0
provider_key_correct = False
class Handler(http.server.BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def reply(self, body, content_type="application/json"):
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
if self.path.startswith("/ops-runs"):
self.reply(b'{"items":[]}')
else:
self.send_error(404)
def do_POST(self):
self.rfile.read(int(self.headers.get("Content-Length", "0")))
if self.path == "/ops-runs/claim":
self.server.queue_claims += 1
self.reply(b'{"items":[]}')
elif self.path == "/v1/messages":
self.server.provider_calls += 1
self.server.provider_key_correct = self.headers.get("x-api-key") == DUMMY_KEY
self.reply(response_bytes(), "text/event-stream")
else:
self.send_error(404)
def case(root, runtime, checksum, server, cap, bootstrap=False):
root.mkdir()
private = root / "private"; private.mkdir(mode=0o700)
source = root / "source"; source.mkdir()
subprocess.run(["git", "init", "-q", str(source)], check=True)
(source / "README.md").write_text("Disposable fixture. Do not edit files.\n")
profiles = root / "profiles"; profiles.mkdir()
catalog = ProfileCatalog()
profile, descriptor = catalog.resolve("harness.agent-dev-local@1.0.0")
profile = profile.model_copy(update={
"limits": ExecutionLimits(max_budget_usd=cap, max_turns=4),
"operational_readiness": OperationalReadiness(status="ready", reason="disposable fixture only",
owner="tests", evidence_ref="test:installed-owner"),
})
(profiles / "fixture.yaml").write_text(yaml.safe_dump(profile.model_dump(mode="json")))
grant = RepositoryGrant("1", ("result.txt",), 1, 1, False)
policy = SpendPolicy(
version="1", envelope_id="fixture", authority_ref="fixture:no-live-authority",
valid_from="2026-09-01T00:00:00Z", expires_at="2099-01-01T00:00:00Z",
timezone="Europe/Berlin", worker_id="fixture-worker", activity_definition_id="fixture-definition",
target_repo=str(source), project="fixture-factory", profile_ref=str(profile.ref),
profile_sha256=digest(profile.model_dump(mode="json")),
descriptor_sha256=digest(descriptor.model_dump(mode="json")), repository_grant_id=grant.grant_id,
max_budget_usd=str(cap), max_liability_usd="5" if cap > 0.01 else "0.01", max_turns=4,
eur_per_usd="1", per_run_eur="5", daily_eur="10", total_eur="15",
)
parent = SpendLedger(private / "spend.sqlite3", policy); parent.initialize()
meter = RequestLedger(parent); meter.initialize()
policy_path = private / "spend-policy.json"
policy_path.write_text(json.dumps(policy.__dict__)); policy_path.chmod(0o600)
messages = MessagesPolicy("fixture:not-live-prices", profile.model.model, 200000, 32000,
3, 15, allowed_betas=BETAS)
owner_path = private / "owner.json"
owner_path.write_text(json.dumps({"version": "1", "authority_ref": policy.authority_ref,
"spend_policy_sha256": policy.sha256, "messages_policy": messages.__dict__,
"runtime": {"path": str(runtime), "sha256": checksum}})); owner_path.chmod(0o600)
bootstrap_result = None
if bootstrap:
env = {"PATH": os.defpath, "LANG": "C.UTF-8", "PYTHONDONTWRITEBYTECODE": "1",
"ACTIVITY_CORE_URL": f"http://127.0.0.1:{server.server_port}",
"AGENT_HARNESS_WORKER_ID": policy.worker_id,
"AGENT_HARNESS_EXECUTION_PROJECT": policy.project,
"AGENT_HARNESS_SPEND_POLICY": str(policy_path),
"AGENT_HARNESS_SPEND_LEDGER": str(parent.path),
"REIN_AHARNESS_STATE_DIR": str(private / "worker-state"),
"GLAS_PROFILE_DIR": str(profiles), "XDG_DATA_HOME": str(root / "data"),
"ANTHROPIC_API_KEY": DUMMY_KEY}
child = subprocess.run([str(runtime / "bin/python3"), "-I", "-B", "-m", "rein_aharness.cli",
"metered-once", "--owner-config", str(owner_path), "--no-hub"],
env=env, capture_output=True, text=True, timeout=30)
assert child.returncode == 0, "installed bootstrap refused disposable configuration"
bootstrap_result = json.loads(child.stdout)
assert bootstrap_result["empty"] and server.queue_claims == 1
assert server.provider_calls == 0 and not parent.status()["reservations"]
config = OpsRunConfig(worker_id=policy.worker_id, execution_project=policy.project)
item = OpsRun(id="fixture-run", activity_definition_id=policy.activity_definition_id,
idempotency_key="fixture-key", target_repo=str(source), title="fixture", description="",
state="claimed", claim_owner=policy.worker_id, attempt=1, repository_grant=grant,
harness_profile_ref=str(profile.ref),
lease_until=(datetime.now(UTC) + timedelta(seconds=90)).isoformat())
parent.reserve(item)
owner = MessagesOwner(messages, DUMMY_KEY, f"http://127.0.0.1:{server.server_port}", True,
runtime_path=runtime, runtime_sha256=checksum)
before = server.provider_calls
consumer = Consumer(actor="agt", project=policy.project, run_id=item.id)
with owner.activate(item, config, parent, profile, ExecutionCancel()) as manager:
status = manager.create(SandboxCreateRequest(profile="profile.bwrap-local", consumer=consumer,
inputs={"repo": str(source)}))
try:
assert status.state.value == "ready", "sandbox not ready"
probe = manager.execute(status.sandbox_id, SandboxExecRequest(
consumer=consumer, timeout_seconds=30, command=["python3", "-c", """import os,json,subprocess,sys
from pathlib import Path
runtime=Path('/opt/sandboxer/runtime')
try:
(runtime/'must-not-write').write_text('no')
except OSError: readonly=True
else: readonly=False
print(json.dumps({'runtime_readonly':readonly,'python_prefix':sys.prefix,
'cli_version':subprocess.check_output(['claude','--version'],text=True).strip(),
'private_absent':not Path(sys.argv[1]).exists(),'source_absent':not Path(sys.argv[2]).exists(),
'proxy_absent':not any('proxy' in k.lower() for k in os.environ),
'interfaces':[x.split(':')[0].strip() for x in Path('/proc/net/dev').read_text().splitlines()[2:]]}))
""", str(private), str(source)]))
assert probe.exit_code == 0, "installed runtime probe failed"
facts = json.loads(probe.stdout)
assert facts["runtime_readonly"] and facts["private_absent"] and facts["source_absent"]
assert facts["proxy_absent"] and facts["interfaces"] == ["lo"]
assert facts["cli_version"] == "2.1.266 (Claude Code)"
adapter = AgenticClaudeCodeAdapter(workdir=Path(status.inputs["workspace_dir"]),
cli_path="/opt/sandboxer/runtime/bin/claude",
model=profile.model.model)
argv = adapter._build_command(RunConfig(model_params={"max_budget_usd": cap, "max_turns": 4}))
executed = manager.execute(status.sandbox_id, SandboxExecRequest(
consumer=consumer, command=argv, stdin_text="Reply fixture complete. Do not edit files.",
timeout_seconds=45))
terminal = json.loads(executed.stdout)
if cap <= 0.01:
assert executed.exit_code != 0 and terminal["is_error"]
assert server.provider_calls == before and not meter.status()
else:
assert executed.exit_code == 0 and not terminal.get("is_error")
assert server.provider_calls == before + 1 and server.provider_key_correct
assert len(meter.status()) == 1 and meter.status()[0]["state"] == "charged"
token = manager._messages_route.token
finally:
manager.destroy(status.sandbox_id)
assert not Path(status.inputs["workspace_dir"]).exists()
with parent._db() as db:
assert db.execute("SELECT revoked FROM request_routes").fetchone()[0] == 1
output = {"native_threshold_usd": cap, "probe": facts,
"provider_requests": server.provider_calls-before, "cli_exit_code": executed.exit_code,
"cli_is_error": terminal.get("is_error"), "cli_estimated_usd": terminal.get("total_cost_usd"),
"request_reservations": meter.status(), "route_revoked": True, "workspace_removed": True,
"bootstrap": bootstrap_result}
assert token not in json.dumps(output) and DUMMY_KEY not in json.dumps(output)
return output
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--runtime", required=True, type=Path)
parser.add_argument("--sha256", required=True)
args = parser.parse_args()
runtime = args.runtime.resolve()
assert sys.flags.isolated and sys.dont_write_bytecode, "run candidate python with -I -B"
assert runtime_digest(runtime) == args.sha256
imports = {}
for package in ("rein_aharness", "llm_connect", "glas_harness", "sandboxer"):
path = Path(importlib.import_module(package).__file__).resolve()
assert path.is_relative_to(runtime), "editable source fallback"
imports[package] = str(path.relative_to(runtime))
assert profiles_dir().is_relative_to(runtime) and extensions_dir().is_relative_to(runtime)
os.environ["SANDBOXER_NO_STATE_HUB"] = "1"
with tempfile.TemporaryDirectory(prefix="installed-metered-proof-") as directory:
root = Path(directory)
os.environ["XDG_DATA_HOME"] = str(root / "data")
server = FixtureServer(("127.0.0.1", 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
try:
positive = case(root / "positive", runtime, args.sha256, server, 1.0, bootstrap=True)
refused = case(root / "refused", runtime, args.sha256, server, 0.01)
finally:
server.shutdown(); server.server_close()
assert runtime_digest(runtime) == args.sha256, "proof mutated the candidate artifact"
print(json.dumps({"scope": "standalone installed packages and real CLI/bwrap; synthetic key/provider/empty queue",
"ok": True, "runtime_sha256": args.sha256,
"claude_sha256": hashlib.sha256((runtime/'bin/claude').read_bytes()).hexdigest(),
"imports": imports, "packaged_definitions": True, "positive": positive, "refused": refused,
"artifact_unchanged": True, "live_factory_attempts": 0}, indent=2))
if __name__ == "__main__":
main()