feat(runtime): install pinned artifacts in a private owner store
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-5.6-luna
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-08 21:08:18 +02:00
parent 174dba17b6
commit df5e941814
6 changed files with 333 additions and 1 deletions

View file

@ -194,3 +194,29 @@ Limits: `--version` proves startup with the proxy environment, not that Claude
has sent a provider request through it. No credential acquisition or model run.
The /tmp candidate is not production placement; protected artifact installation,
owner configuration, credential adoption and real-model proof remain T04 gates.
## Install a reviewed artifact in the local owner's store
Use `scripts/install-bwrap-runtime.py --source <absolute-candidate> --sha256
<reviewed-complete-digest>` from the owner checkout with its Python environment.
The default store is `~/.local/share/sandboxer/runtimes`, with mode 0700. The
command accepts only a canonical store beneath that owner's home, checks owner
and ancestor write permissions, verifies the source, copies into a private
staging directory, verifies the copy and atomically publishes under its digest.
It refuses unsafe modes, store aliases and altered existing destinations.
Reinstalling the same unchanged artifact is idempotent; it never overwrites one.
This is owner-controlled local storage. The owner remains able to change its
files, as in the artifact trust contract above; it is not root-owned storage or
Railiance placement. Sandboxes still recheck the pin on creation and receive a
read-only mount. Installation does not select a profile, configure credentials,
activate network access or enable a schedule. Rollback selects a separately
reviewed retained digest through owner configuration; no in-place replacement
or automatic deletion is part of this command.
The 2026-09-08 bnt-lap001 installation used the existing combined Claude 2.1.263
candidate unchanged. Its installed-path startup/lifecycle proof is
[the local installation receipt](evidence/SAND-WP-0015-protected-local-install-2026-09-08.json).
SAND-WP-0015-T06 is complete. T04 still owns configured execution, native
credential adoption, real-model acceptance and production placement.

View file

@ -0,0 +1,40 @@
{
"date": "2026-09-08",
"scope": "local owner artifact installation; not Railiance admission",
"host": "bnt-lap001",
"owner_uid": 1000,
"store_mode": "0700",
"runtime_path": "/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922",
"runtime_sha256": "5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922",
"binary_version": "2.1.263",
"binary_sha256": "26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba",
"candidate_rebuilt": false,
"installed_entries": 358,
"installed_regular_bytes": 245176062,
"root_owned": false,
"smoke": {
"ok": true,
"sandbox_id": "51b59587",
"rein_cli_started": true,
"adapter_imported": true,
"claude_version": "2.1.263 (Claude Code)",
"runtime_readonly": true,
"source_absent": true,
"home_outside_workspace": true,
"home_mode": "0700",
"worktree_clean": true,
"interfaces": [
"lo"
],
"python_prefix": "/opt/sandboxer/runtime",
"https_proxy_present": true,
"credential_refs": [],
"private_state_persisted": true,
"workspace_removed": true,
"proxy_removed": true,
"exit_code": 0
},
"model_run_proven": false,
"profile_activated": false,
"credential_delivery_configured": false
}

View file

@ -0,0 +1,23 @@
"""Install an explicitly pinned artifact; enables no execution or credential lane."""
import argparse
import json
from pathlib import Path
from sandboxer.extensions.runtime_store import install_runtime
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--sha256", required=True)
parser.add_argument("--store", type=Path,
default=Path.home() / ".local/share/sandboxer/runtimes")
args = parser.parse_args()
result = install_runtime(args.source, args.store, args.sha256)
print(json.dumps(result, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,89 @@
"""Publish a reviewed runtime in the local owner's private artifact store."""
from __future__ import annotations
import fcntl
import os
import re
import shutil
import stat
import tempfile
from pathlib import Path
from sandboxer.extensions.runtime import verified_runtime
def _owned_directory(path: Path, *, private: bool = False) -> None:
info = path.lstat()
if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid():
raise ValueError("runtime store requires owner-controlled directories")
mode = stat.S_IMODE(info.st_mode)
if mode & 0o022 or (private and mode != 0o700):
raise ValueError("runtime store permissions are not protected")
def _prepare_store(store: Path) -> None:
home = Path.home().resolve(strict=True)
if not store.is_absolute() or store.resolve() != store or store == home:
raise ValueError("store must be a canonical absolute directory below the owner home")
if not store.is_relative_to(home):
raise ValueError("store must be below the owner home")
_owned_directory(home)
current = home
for part in store.relative_to(home).parts:
current /= part
current.mkdir(mode=0o700, exist_ok=True)
_owned_directory(current, private=current == store)
def _protected_tree(root: Path) -> None:
for path in [root, *root.rglob("*")]:
info = path.lstat()
if info.st_uid != os.getuid():
raise ValueError("runtime artifact is not owned by the installing owner")
if not stat.S_ISLNK(info.st_mode) and stat.S_IMODE(info.st_mode) & 0o7022:
raise ValueError("runtime artifact has unsafe permission bits")
def install_runtime(source: Path, store: Path, sha256: str) -> dict:
"""Verify, copy, reverify and atomically publish; never replace an artifact.
The local owner remains trusted and can alter its own files. This store
protects against other local users; the broker separately mounts the
artifact read-only and verifies its digest for every sandbox creation.
This function enables no profile, credentials, egress or schedule.
"""
if not re.fullmatch(r"[0-9a-f]{64}", sha256):
raise ValueError("expected runtime digest must be a lowercase SHA-256")
config = {"runtime": {"path": str(source), "sha256": sha256}}
verified_runtime(config)
if source.is_relative_to(store) or store.is_relative_to(source):
raise ValueError("source and runtime store must not overlap")
_prepare_store(store)
destination = store / sha256
fd = os.open(store / ".install.lock", os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "rb") as lock:
info = os.fstat(lock.fileno())
if (not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid()
or stat.S_IMODE(info.st_mode) != 0o600):
raise ValueError("runtime install lock is not private and owner-controlled")
fcntl.flock(lock, fcntl.LOCK_EX)
reused = destination.exists() or destination.is_symlink()
if reused:
verified_runtime({"runtime": {"path": str(destination), "sha256": sha256}})
_protected_tree(destination)
else:
staging = Path(tempfile.mkdtemp(prefix=".install-", dir=store))
try:
shutil.copytree(source, staging, symlinks=True, dirs_exist_ok=True)
verified_runtime({"runtime": {"path": str(staging), "sha256": sha256}})
_protected_tree(staging)
staging.rename(destination)
finally:
if staging.exists():
shutil.rmtree(staging)
return {
"runtime": {"path": str(destination), "sha256": sha256},
"owner_uid": os.getuid(), "store_mode": "0700", "reused": reused,
"profile_activated": False, "credential_delivery_configured": False,
}

111
tests/test_runtime_store.py Normal file
View file

@ -0,0 +1,111 @@
from pathlib import Path
import pytest
from sandboxer.extensions import runtime_store
from sandboxer.extensions.runtime import runtime_digest, verified_runtime
@pytest.fixture
def inputs(tmp_path, monkeypatch):
home = tmp_path / "owner-home"
home.mkdir(mode=0o700)
monkeypatch.setenv("HOME", str(home))
source = tmp_path / "candidate"
(source / "bin").mkdir(parents=True)
(source / "pyvenv.cfg").write_text("home = /usr/bin\n")
(source / "bin/python3").write_bytes(b"fixture executable")
(source / "bin/python3").chmod(0o755)
(source / "bin/python").symlink_to("python3")
return source, home / "runtimes", runtime_digest(source)
def test_install_preserves_pin_and_does_not_share_mutable_source(inputs):
source, store, digest = inputs
result = runtime_store.install_runtime(source, store, digest)
installed = verified_runtime(result)
assert installed == store / digest
assert store.stat().st_mode & 0o777 == 0o700
assert not result["profile_activated"]
assert not result["credential_delivery_configured"]
assert (installed / "bin/python").is_symlink()
(source / "bin/python3").write_bytes(b"changed after installation")
assert verified_runtime(result) == installed
def test_repeat_is_idempotent_but_never_repairs_or_overwrites_existing_artifact(inputs):
source, store, digest = inputs
first = runtime_store.install_runtime(source, store, digest)
assert runtime_store.install_runtime(source, store, digest)["reused"]
installed = Path(first["runtime"]["path"])
target = installed / "bin/python3"
target.write_bytes(b"tampered installed artifact")
with pytest.raises(ValueError, match="digest"):
runtime_store.install_runtime(source, store, digest)
assert target.read_bytes() == b"tampered installed artifact"
def test_wrong_digest_refuses_before_creating_store(inputs):
source, store, _ = inputs
with pytest.raises(ValueError, match="digest"):
runtime_store.install_runtime(source, store, "0" * 64)
assert not store.exists()
def test_copy_tampering_never_publishes(inputs, monkeypatch):
source, store, digest = inputs
copytree = runtime_store.shutil.copytree
def corrupt(src, dst, *args, **kwargs):
result = copytree(src, dst, *args, **kwargs)
if Path(src) == source:
(Path(dst) / "bin/python3").write_bytes(b"corrupted during copy")
return result
monkeypatch.setattr(runtime_store.shutil, "copytree", corrupt)
with pytest.raises(ValueError, match="digest"):
runtime_store.install_runtime(source, store, digest)
assert not (store / digest).exists()
assert not list(store.glob(".install-*"))
@pytest.mark.parametrize("mode", [0o777, 0o775, 0o755])
def test_store_must_be_private(inputs, mode):
source, store, digest = inputs
store.mkdir(mode=mode)
store.chmod(mode)
with pytest.raises(ValueError, match="permissions"):
runtime_store.install_runtime(source, store, digest)
assert not (store / digest).exists()
def test_store_symlink_is_refused(inputs):
source, store, digest = inputs
actual = store.parent / "actual"
actual.mkdir(mode=0o700)
store.symlink_to(actual)
with pytest.raises(ValueError, match="canonical"):
runtime_store.install_runtime(source, store, digest)
@pytest.mark.parametrize("mode", [0o777, 0o4755, 0o2755])
def test_even_a_matching_pin_cannot_admit_unsafe_artifact_modes(inputs, mode):
source, store, _ = inputs
(source / "bin/python3").chmod(mode)
digest = runtime_digest(source)
with pytest.raises(ValueError, match="unsafe permission"):
runtime_store.install_runtime(source, store, digest)
assert not (store / digest).exists()
def test_world_writable_parent_is_refused(inputs):
source, store, digest = inputs
store.parent.chmod(0o777)
with pytest.raises(ValueError, match="permissions"):
runtime_store.install_runtime(source, store, digest)
def test_store_outside_owner_home_is_refused(inputs):
source, store, digest = inputs
with pytest.raises(ValueError, match="below the owner home"):
runtime_store.install_runtime(source, store.parent.parent / "other", digest)

View file

@ -8,7 +8,7 @@ status: blocked
owner: codex
topic_slug: bwrap-runtime-and-private-state
created: "2026-09-05"
updated: "2026-09-06"
updated: "2026-09-08"
state_hub_workstream_id: "d3f12387-fd23-58f0-b979-9c811507614d"
---
@ -204,3 +204,46 @@ Limits: `--version` proves startup with the proxy environment, not that Claude
has sent a provider request through it. No credential acquisition or model run.
The /tmp candidate is not production placement; protected artifact installation,
owner configuration, credential adoption and real-model proof remain T04 gates.
## Install the pinned runtime in the local owner's protected store
```task
id: SAND-WP-0015-T06
status: done
priority: high
assignee: the-custodian
```
HFACT-WP-0001-T04 consumes this independent installation return. Reuse the
already verified combined candidate with digest
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`;
do not rebuild resolved dependencies. Publish it under the local owner's
private artifact store, verify exact copied content/modes and refusal of unsafe
or corrupted destinations, and prove startup/read-only access/teardown through
the installed path. The owner remains trusted; this is not root-owned storage.
T04 retains real credential adoption, owner configuration, model acceptance
and production placement. No credential route or profile is activated here.
`scripts/install-bwrap-runtime.py` installs below the owner's home into a
mode-0700 store, verifies before and after copying, preserves artifact modes
and internal symlinks, serializes publishers and never overwrites a published
artifact. Existing modified artifacts are refused rather than repaired silently.
Group/other-writable ancestors, unsafe permission bits and store aliases refuse.
Unit tests cover integrity, idempotence, copy-time corruption and access modes.
T06 completed 2026-09-08 on bnt-lap001, local owner UID 1000. Installed the
unchanged 358-entry / 245176062-byte candidate at
`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
The private store is mode 0700; the installing owner remains trusted. A root-owned
system installation was not claimed or required for this local owner contract.
The installed-path sandbox smoke `51b59587` passed real Claude 2.1.263 and rein
startup, read-only mount, private state persistence, clean worktree, absent source,
loopback-only interfaces and complete workspace/proxy teardown. No model call
or credential acquisition occurred. `make check`: lint clean, 188 tests passed.
Two existing tests require the checkout directory name `sand-boxer`; the final
full check used that canonical basename. Evidence:
`docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`.
T04 retains owner execution configuration, native credential/egress and real-model
acceptance; Railiance installation needs its own target-specific return.