Bootstrap one metered owner cycle against a pinned standalone runtime
Some checks failed
Governed runtime contract / contract (push) Failing after 23s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-09 22:56:38 +02:00
parent 3e4c976090
commit 565b07716a
11 changed files with 664 additions and 2 deletions

View file

@ -19,7 +19,7 @@
"version": "0.1.0"
},
{
"commit": "bfe0e4c4c86003892796ba0fb990ae21d72278e1",
"commit": "be42de7caf90e7d9fe6c8d7fea263603167ac8c5",
"distribution": "sandboxer",
"source": "../sand-boxer",
"version": "0.0.0"

73
docs/owner-bootstrap.md Normal file
View file

@ -0,0 +1,73 @@
# One-cycle owner bootstrap
`rein-aharness metered-once --owner-config /absolute/private/owner.json` is the
explicit exec-env child entry point. It prepares the exact admitted pins, consumes
only the deliberately delivered `ANTHROPIC_API_KEY`, removes provider authentication
and base-URL variables before other child processes, and runs at most one claim
cycle. The provider key is held by MessagesOwner outside the sandbox. Missing/mixed
credentials, invalid pins, missing ledgers, blocked profiles, failed readiness and
unclaimed refusals return nonzero. Receipt output excludes raw errors and prompts.
There is no key fetch, alternate provider, key file, OAuth/HOME or daemon fallback.
`--check` validates local pins without a key, queue access or workload dispatch;
it can advance the ledger's clock watermark but never initializes/reset its schema.
Use the standalone candidate interpreter as `python -I -B -m rein_aharness.cli`.
`-I` excludes editable PYTHONPATH/user-site fallbacks; `-B` prevents bytecode writes
from changing the complete artifact digest. The source and namespace rein commands
continue to use their existing entry points. The owner config selects the pinned
runtime through the trusted, ephemeral sandbox binding; it cannot add egress or
provider credentials to the child. Normal `claim-loop` behavior is unchanged.
The mode-0600, regular, owner-owned config has exactly these fields:
```json
{
"version": "1",
"authority_ref": "REPLACE_WITH_ACCEPTED_SPEND_AUTHORITY",
"spend_policy_sha256": "REPLACE_WITH_ACCEPTED_SPEND_POLICY_DIGEST",
"messages_policy": {
"tariff_ref": "REPLACE_WITH_ACCEPTED_PROVIDER_BOUNDS_AND_RATES",
"model": "claude-sonnet-4-6",
"context_tokens": 0,
"max_output_tokens": 0,
"input_microusd_per_token": 0,
"output_microusd_per_token": 0,
"allowed_betas": []
},
"runtime": {
"path": "/absolute/accepted/runtime",
"sha256": "REPLACE_WITH_ACCEPTED_COMPLETE_ARTIFACT_DIGEST"
}
}
```
The placeholders and zero bounds deliberately refuse. The authority reference and
spend digest must match the private existing SpendPolicy. Worker/project, exact
profile/descriptor digests, operational readiness, model, empty-egress bwrap profile
and runtime digest are checked before claim. Runtime, owner state and target checkout
must not overlap. Provision parent and request ledgers as separate reviewed actions.
The normal ACTIVITY_CORE/AGENT_HARNESS worker and state configuration still applies.
This config is not an authorization decision or a custody provenance proof. The
invoking credential engine must already have passed its exact action approval,
consume, scoped backend/readiness and admitted consumer checks. Its reviewed command
must be the fixed one-cycle owner entry point, with the accepted immutable config.
The current `glas-claude-agent-dev-anthropic` catalog describes delivery through the
sandbox helper and does not yet admit this owner holder. SECRETS-WP-0009-T03 and
HFACT-WP-0001-T03/T04 retain that review, existing client/audit/service dependencies,
Railiance placement and live negative tests. No real key is read by local validation.
This initial bootstrap obtains delivery before one claim cycle; an empty queue still
uses that delivery attempt. It intentionally exits after that cycle. Native scheduled
activation must not wrap a persistent claim loop with one reusable provider credential.
Later per-run acquisition for a continuous worker belongs to REINAH-WP-0003-T05/T06
and the same credential owner; it must retain exact action/lease/budget semantics.
`scripts/prove-metered-runtime.py` runs with a built candidate's `python -I -B`.
It verifies all four packages and definitions come from the artifact, exercises this
CLI once against an empty fake HTTP queue with a synthetic exec-env key, and runs the
pinned actual Claude CLI through the protected bwrap mount and metered owner. The
positive fake stream and pre-forward insufficient-capacity refusal are separate
cases. It checks unchanged artifact digest and teardown. Queue/provider/key/profile
are disposable fixtures, never evidence of live admission. The project records the
candidate result in `prj-helixforge-factory/evidence/2026-09-09-owner-bootstrap.json`.

View file

@ -4,7 +4,9 @@ Source API: `rein_aharness.messages_owner.MessagesOwner`. The trusted host boots
constructs it with an accepted immutable `MessagesPolicy` and an explicitly supplied
provider key, then sets `OpsRunConfig.messages_owner`. This is an in-process owner
capability, not a queue field, serialized profile or remote sandbox API parameter.
The normal CLI does not acquire a key or construct an owner. Set
The normal claim-loop CLI does not acquire a key or construct an owner. The explicit
[metered-once bootstrap](owner-bootstrap.md) now supplies the one-cycle exec-env
child path; native delivery still needs admission. Set
`AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION=1` in a future admitted service so a missing
bootstrap refuses before claiming work. Parent spend admission must also be configured;
provision the RequestLedger schema explicitly before dispatch. No live bootstrap or

View file

@ -554,6 +554,13 @@ def main(argv: list[str] | None = None) -> int:
)
poll.add_argument("--no-hub", action="store_true", help="Skip hub on execute")
metered = sub.add_parser(
"metered-once", help="One admitted owner claim cycle with explicit exec-env delivery"
)
metered.add_argument("--owner-config", required=True, type=Path)
metered.add_argument("--check", action="store_true", help="Validate local pins without claiming")
metered.add_argument("--no-hub", action="store_true")
claim_loop = sub.add_parser(
"claim-loop",
help="Continuously claim ops_runs, select approach, execute, complete/fail",
@ -622,6 +629,9 @@ def main(argv: list[str] | None = None) -> int:
if args.command == "poll":
return _cmd_poll(args)
if args.command == "metered-once":
from rein_aharness.owner_bootstrap import run_once
return run_once(args.owner_config, check_only=args.check, report_to_hub=not args.no_hub)
if args.command == "claim-loop":
return _cmd_claim_loop(args)

View file

@ -40,6 +40,8 @@ class MessagesOwner:
provider_key: str = field(repr=False)
upstream_url: str = "https://api.anthropic.com"
allow_test_http: bool = False
runtime_path: Path | None = None
runtime_sha256: str | None = None
@contextmanager
def activate(self, run, config, spend, profile, cancel: ExecutionCancel):
@ -100,6 +102,7 @@ class MessagesOwner:
socket_path, token, profile.sandbox_profile, "agt",
config.execution_project, run.id,
private_paths=(spend.path.parent,),
runtime_path=self.runtime_path, runtime_sha256=self.runtime_sha256,
)
server.start()
timer.start()

View file

@ -0,0 +1,158 @@
"""One-cycle entry point for an explicitly admitted exec-env owner child.
Custody/approval belong to the invoking delivery engine. This module neither
fetches a key nor claims that a configuration file authorizes credential access.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
import signal
import threading
from llm_connect.messages_gate import MessagesPolicy
from rein_aharness.messages_owner import MessagesOwner
from rein_aharness.ops_run_client import ActivityCoreOpsClient, OpsRunConfig
from rein_aharness.spend_admission import SpendAdmissionError, _private_file, worker_spend
class BootstrapRefused(RuntimeError):
pass
def prepare(path: Path, config: OpsRunConfig) -> tuple[MessagesPolicy, Path, str]:
"""Validate value-free, owner-controlled pins without a key or queue claim."""
from glas_harness.profiles import ProfileCatalog
from sandboxer.extensions.runtime import verified_runtime
try:
_private_file(path)
if not path.is_absolute() or path.resolve() != path or path.stat().st_size > 65536:
raise ValueError
def unique(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError
result[key] = value
return result
data = json.loads(path.read_text(), object_pairs_hook=unique)
if set(data) != {"version", "authority_ref", "spend_policy_sha256", "messages_policy", "runtime"}:
raise ValueError
if data["version"] != "1":
raise ValueError
spend = worker_spend(config)
if spend is None or data["authority_ref"] != spend.policy.authority_ref:
raise ValueError
if data["spend_policy_sha256"] != spend.policy.sha256:
raise ValueError
if (config.worker_id != spend.policy.worker_id
or config.execution_project != spend.policy.project
or path.is_relative_to(Path(spend.policy.target_repo).resolve())):
raise ValueError
values = data["messages_policy"]
if not isinstance(values, dict):
raise ValueError
if "allowed_betas" in values:
if not isinstance(values["allowed_betas"], list):
raise ValueError
values["allowed_betas"] = tuple(values["allowed_betas"])
policy = MessagesPolicy(**values)
runtime = verified_runtime({"runtime": data["runtime"]})
if runtime is None or not runtime.is_absolute() or runtime.resolve() != runtime:
raise ValueError
for private in (path.parent, spend.path.parent, Path(spend.policy.target_repo)):
a, b = runtime.resolve(), private.resolve()
if a.is_relative_to(b) or b.is_relative_to(a):
raise ValueError
catalog = ProfileCatalog()
profile, descriptor = catalog.resolve(spend.policy.profile_ref)
catalog.require_operational(profile)
from rein_aharness.spend_admission import digest
if (digest(profile.model_dump(mode="json")) != spend.policy.profile_sha256
or digest(descriptor.model_dump(mode="json")) != spend.policy.descriptor_sha256
or profile.model.model != policy.model or profile.credential_route_refs):
raise ValueError
from sandboxer.profiles.loader import load_profile
sandbox = load_profile(profile.sandbox_profile)
if sandbox.extension != "ext.bwrap" or sandbox.network.default != "deny" or sandbox.network.egress or sandbox.setup.secret_refs:
raise ValueError
spend.preflight()
# Provisioning is a separate owner action; never silently initialize/reset.
with spend._db() as db:
db.execute("SELECT run_id, token_sha256, revoked FROM request_routes LIMIT 0")
db.execute("SELECT receipt, state FROM request_reservations LIMIT 0")
return policy, runtime, data["runtime"]["sha256"]
except Exception:
raise BootstrapRefused("owner bootstrap configuration refused") from None
def run_once(path: Path, *, check_only: bool = False, report_to_hub: bool = True) -> int:
# Only this explicit exec-env child entry point consumes the conventional key.
# Remove it before any subprocess or queue client can inherit it. No OAuth,
# operator HOME, alternate provider URL or credential-file fallback is used.
provider_key = None if check_only else os.environ.get("ANTHROPIC_API_KEY")
alternate_auth = None if check_only else os.environ.get("ANTHROPIC_AUTH_TOKEN")
for name in ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"):
os.environ.pop(name, None)
stop = threading.Event()
previous = {}
config = None
try:
config = OpsRunConfig.from_env()
policy, runtime_path, runtime_sha256 = prepare(path, config)
if check_only:
print(json.dumps({"ok": True, "check_only": True, "dispatch_enabled": False,
"policy_sha256": policy.sha256, "runtime_sha256": runtime_sha256}))
return 0
if (alternate_auth or not isinstance(provider_key, str) or not provider_key
or len(provider_key) > 8192
or any(ord(c) < 33 or ord(c) > 126 for c in provider_key)):
raise BootstrapRefused("explicit exec-env provider delivery required")
owner = MessagesOwner(policy, provider_key, runtime_path=runtime_path,
runtime_sha256=runtime_sha256)
class OneCycleOwner:
def activate(self, run, config, spend, profile, cancel):
if stop.is_set():
cancel.cancel("signal")
return owner.activate(run, config, spend, profile, cancel)
config.messages_owner = OneCycleOwner()
config.require_request_admission = True
config.require_spend_admission = True
client = ActivityCoreOpsClient(config)
from rein_aharness.claim_loop import _cancel_active_run, process_one
from rein_aharness.readiness import run_readiness_checks
def cancelled(signum, frame):
stop.set()
_cancel_active_run("signal")
for signum in (signal.SIGINT, signal.SIGTERM):
previous[signum] = signal.signal(signum, cancelled)
if not run_readiness_checks(client).ok:
raise BootstrapRefused("owner readiness refused")
if stop.is_set():
raise BootstrapRefused("owner stopped before claim")
result = process_one(client, report_to_hub=report_to_hub)
ok = not stop.is_set() and (result.empty or (result.claimed and result.ok is True))
print(json.dumps({"ok": ok, "claimed": result.claimed, "empty": result.empty,
"run_id": (result.run_id or "")[:200], "ops_state": result.ops_state}))
return 0 if ok else 1
except (BootstrapRefused, SpendAdmissionError):
print(json.dumps({"ok": False, "code": "owner_bootstrap_refused"}))
return 2
except Exception:
# Provider/queue exceptions and config excerpts cannot enter logs/receipts.
print(json.dumps({"ok": False, "code": "owner_cycle_failed"}))
return 1
finally:
for signum, handler in previous.items():
signal.signal(signum, handler)
if config is not None:
config.messages_owner = None
provider_key = None

View file

@ -0,0 +1,252 @@
"""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),
"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"]),
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()

View file

@ -13,6 +13,7 @@ PYTHONPATH="${REPO_ROOT}:${REPO_ROOT}/../llm-connect" \
tests/test_spend_admission.py \
tests/test_request_admission.py \
tests/test_messages_owner.py \
tests/test_owner_bootstrap.py \
tests/test_claim_loop.py::test_process_one_initial_heartbeat_rejection_refuses_dispatch \
tests/test_claim_loop.py::test_process_one_lease_loss_cancels_registered_adapter_process \
tests/test_claim_loop.py::test_profiled_close_failure_happens_after_repository_lock_release \

View file

@ -14,6 +14,7 @@ PYTHONPATH="${REPO_ROOT}:${REPO_ROOT}/../llm-connect" \
tests/test_spend_admission.py \
tests/test_request_admission.py \
tests/test_messages_owner.py \
tests/test_owner_bootstrap.py \
tests/test_glas_execution.py \
tests/test_ops_run_client.py \
tests/test_claim_loop.py \

View file

@ -0,0 +1,136 @@
"""Synthetic owner config/key; no native custody, provider or queue call."""
from dataclasses import replace
import json
import os
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
from glas_harness.contract import ExecutionLimits, OperationalReadiness
from glas_harness.profiles import ProfileCatalog
from llm_connect.messages_gate import MessagesPolicy
from sandboxer.extensions.runtime import runtime_digest
from rein_aharness.claim_loop import ProcessResult
from rein_aharness.owner_bootstrap import BootstrapRefused, prepare, run_once
from rein_aharness.request_admission import RequestLedger
from rein_aharness.spend_admission import SpendLedger, digest
from test_spend_admission import ledger as ledger, configured
@pytest.fixture
def prepared(ledger, tmp_path, monkeypatch):
catalog = ProfileCatalog()
profile, descriptor = catalog.resolve("harness.agent-dev-local@1.0.0")
profile = profile.model_copy(update={
"sandbox_profile": "profile.bwrap-local", "credential_route_refs": [],
"limits": ExecutionLimits(max_budget_usd=4.0, max_turns=8),
"operational_readiness": OperationalReadiness(status="ready", reason="fixture only",
owner="tests", evidence_ref="test:bootstrap"),
})
catalog.profiles()[(profile.id, profile.version)] = profile
monkeypatch.setattr("glas_harness.profiles.ProfileCatalog", lambda: catalog)
policy = replace(ledger.policy, profile_ref=str(profile.ref),
profile_sha256=digest(profile.model_dump(mode="json")),
descriptor_sha256=digest(descriptor.model_dump(mode="json")))
parent = SpendLedger(ledger.path.parent / "prepared.sqlite3", policy)
parent.initialize()
RequestLedger(parent).initialize()
config = configured(parent)
runtime = tmp_path / "runtime"
(runtime / "bin").mkdir(parents=True)
(runtime / "bin/python3").write_bytes(b"not executed; fixture runtime structure")
(runtime / "pyvenv.cfg").write_text("fixture only")
messages = MessagesPolicy("fixture:upper-rate", profile.model.model, 1000, 1000, 1000, 1000)
data = {"version": "1", "authority_ref": policy.authority_ref,
"spend_policy_sha256": policy.sha256, "messages_policy": messages.__dict__,
"runtime": {"path": str(runtime), "sha256": runtime_digest(runtime)}}
path = parent.path.parent / "owner.json"
path.write_text(json.dumps(data)); path.chmod(0o600)
monkeypatch.setattr("rein_aharness.owner_bootstrap.OpsRunConfig.from_env", lambda: config)
return path, config, data, runtime
def test_prepare_pins_without_key_or_claim(prepared, monkeypatch):
path, config, data, runtime = prepared
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
policy, selected, checksum = prepare(path, config)
assert selected == runtime and checksum == data["runtime"]["sha256"]
assert policy.model == data["messages_policy"]["model"]
@pytest.mark.parametrize("case", ["authority", "policy_digest", "model", "version", "unknown_field",
"duplicate", "public", "runtime_changed", "schema_missing"])
def test_bad_bootstrap_refuses_before_claim(prepared, monkeypatch, capsys, case):
path, config, data, runtime = prepared
if case == "authority": data["authority_ref"] = "unrelated"
elif case == "policy_digest": data["spend_policy_sha256"] = "0" * 64
elif case == "model": data["messages_policy"]["model"] = "unadmitted"
elif case == "version": data["version"] = "2"
elif case == "unknown_field": data["upstream_url"] = "https://not-admitted.invalid"
elif case == "runtime_changed": (runtime / "added-file").write_text("changed")
elif case == "schema_missing":
import sqlite3
with sqlite3.connect(config.spend_ledger_path) as db: db.execute("DROP TABLE request_routes")
path.write_text(json.dumps(data))
if case == "duplicate": path.write_text(path.read_text().replace('"version": "1"', '"version": "1", "version": "1"'))
if case == "public": path.chmod(0o644)
claimant = MagicMock()
monkeypatch.setattr("rein_aharness.claim_loop.process_one", claimant)
monkeypatch.setenv("ANTHROPIC_API_KEY", "fixture-owner-key")
assert run_once(path) == 2
claimant.assert_not_called()
assert "fixture-owner-key" not in capsys.readouterr().out
assert "ANTHROPIC_API_KEY" not in os.environ
@pytest.mark.parametrize("check,key,alternate,code", [
(True, None, None, 0), (False, None, None, 2),
(False, "fixture-key", "alternate-token", 2), (False, "invalid key", None, 2),
])
def test_check_and_missing_or_mixed_auth_never_claim(prepared, monkeypatch, check, key, alternate, code):
path, _, _, _ = prepared
for name, value in [("ANTHROPIC_API_KEY", key), ("ANTHROPIC_AUTH_TOKEN", alternate)]:
if value is None: monkeypatch.delenv(name, raising=False)
else: monkeypatch.setenv(name, value)
claimant = MagicMock()
monkeypatch.setattr("rein_aharness.claim_loop.process_one", claimant)
assert run_once(path, check_only=check) == code
claimant.assert_not_called()
@pytest.mark.parametrize("result,code", [
(ProcessResult(claimed=False, empty=True), 0),
(ProcessResult(claimed=True, ok=True, run_id="fixture-run"), 0),
(ProcessResult(claimed=False, ok=False, reason="private exception"), 1),
(ProcessResult(claimed=True, ok=False, reason="private prompt"), 1),
])
def test_exactly_one_cycle_without_key_in_environment(prepared, monkeypatch, capsys, result, code):
path, config, data, runtime = prepared
monkeypatch.setenv("ANTHROPIC_API_KEY", "fixture-key")
monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False)
monkeypatch.setattr("rein_aharness.readiness.run_readiness_checks", lambda client: SimpleNamespace(ok=True))
seen = []
def process(client, **kwargs):
assert client.config is config
assert config.require_request_admission and config.require_spend_admission
assert config.messages_owner is not None
assert "ANTHROPIC_API_KEY" not in os.environ
seen.append(client)
return result
monkeypatch.setattr("rein_aharness.claim_loop.process_one", process)
assert run_once(path, report_to_hub=False) == code
output = capsys.readouterr().out
assert len(seen) == 1 and "fixture-key" not in output and "private" not in output
def test_unexpected_exception_is_bounded(prepared, monkeypatch, capsys):
path, _, _, _ = prepared
monkeypatch.setenv("ANTHROPIC_API_KEY", "fixture-key")
monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False)
monkeypatch.setattr("rein_aharness.readiness.run_readiness_checks", lambda client: SimpleNamespace(ok=True))
monkeypatch.setattr("rein_aharness.claim_loop.process_one", MagicMock(side_effect=RuntimeError("fixture-key")))
assert run_once(path) == 1
assert json.loads(capsys.readouterr().out) == {"ok": False, "code": "owner_cycle_failed"}

View file

@ -765,3 +765,29 @@ protected runtime/CLI artifact, Railiance host/profile/consumer/custody/recovery
admission, live provider compatibility and accepted bounds/tariffs/FX, then G0 and
natural model/queue evidence. No protected runtime was installed or promoted,
no existing CCR changed, no secret read or paid execution took place.
### Explicit one-cycle owner bootstrap and installed-candidate proof — 2026-09-09
Implemented `metered-once` with exact private policy/runtime/profile pins, explicit
exec-env key consumption outside bwrap, environment scrubbing, one claim cycle,
nonzero refusal and bounded receipt output. `--check` never claims or fetches a key.
Nineteen synthetic bootstrap tests cover invalid/changed config/artifact/schema,
missing/mixed authentication, exactly one empty/success/refused cycle and sanitized
exceptions. The existing cancellation/lease lifecycle remains in MessagesOwner.
Runtime selection passes through the trusted ephemeral sandbox binding.
The worker's source suite retains 392 passing non-native cases; the four native
cases use the exact proved 2.1.266 binary/digest. The workstation alias has advanced
to 2.1.267, which correctly fails the existing pin check; this is not a migration.
SAND-WP-0015 now supplies packaged owner definitions and a frozen-lock build mode.
Run `scripts/prove-metered-runtime.py` using the candidate's isolated interpreter;
source/receipt details live in docs/owner-bootstrap.md and the project's
`evidence/2026-09-09-owner-bootstrap.json`.
T05 remains progress and T06 wait. This supplies executable bootstrap source, not
native delivery acceptance. SECRETS-WP-0009-T03 must admit the changed credential
holder and fixed child command through the existing action/consume/backend gates.
The current catalog/CCRs, live credentials, profile readiness, host service and
factory queue remain unchanged. Protected placement, provider bounds/tariffs/FX,
G0 and natural execution remain. Per-run acquisition for a future continuous worker
is retained here; a delivered key must not authorize an unbounded daemon lifetime.