Design workspace for a high-risk, agent-driven security research lab on an Apple Silicon MacBook Air.
- Proposed architecture and threat model
- Anthropic CVP Defense containment foundation
- In-app updates and release trust
- Human identity and release authenticity research
- First-party research harness feasibility
- Multi-harness admission and plugin-ecosystem isolation
- Harness lifecycle and verified upgrade
- Harness lifecycle foundation and Host quarantine
- Harness conformance and disposable qualification
- Protected harness generation store and transactional activation
- Harness-bound sessions, leases, and runtime migration
- Control Center harness lifecycle GUI
- Implementation overview and acceptance contract
- Build prerequisites and readiness checks
- MacBook Neo 8 GB VM layout qualification
- Local broker protocol hardening
- Transactional publisher crash recovery
- Azure evidence and alerting plane
- Azure alert-semantics separation
- Agent artifact evidence pipeline
- Brokered source acquisition
- Agent-visible source integrity verification
- Verified corpus intelligence
- Legacy VM data migration and GLM continuity
- Corpus Intelligence v2: inventory, comparison, and fixed indicators
- Large-corpus fixed-profile scan scaling
- Default-configuration static reachability
- CodeGraph and findings ergonomics
- Operator-mediated dependency orchestration
- Bounded run leases
- Transactional PKI lifecycle v2
- Agent self-definition and institutional memory
- Candidate findings and independent adjudication
- Trusted evidence receipts
- Sealed review-bundle verification
- Verified external-containment audit adapter
- Trusted guest time synchronization
- Fresh-VM containment restoration and acceptance
- Operator-visible privileged authorization provenance
- Touch ID operator authorization
- Operator ceremony and diagnostics
- Universal host maintenance authorization
- Disposable Python fixture execution
- Parallels snapshot retention
- Parallels lifecycle resilience
- Session discovery and private operator labels
Host-side entry point: scripts/labctl.
VM lifecycle changes are readback-driven and require the Parallels Desktop GUI
to be closed. labctl status is read-only. labctl start and labctl stop
serialize transitions, converge safe partially completed states, and emit a
final secure-sandbox-host-lifecycle-v1 record. A command response is never
accepted as proof that a VM changed state; only independent prlctl readback
can produce status:"verified". See
PARALLELS_LIFECYCLE_RESILIENCE_DESIGN.md.
Because the dedicated SSH agents are intentionally ephemeral, labctl start
and labctl stop verify their exact single-key fingerprints and restore them
from the 1Password Agents vault after a host reboot when necessary. Restoration
must complete before any VM or adapter mutation. labctl status remains
strictly read-only and never loads credentials.
The long-lived labctl claude path immediately enters a root-owned, versioned
host-controller release. Deploy a reviewed controller only while no invocation
is active, naming the requesting agent conversation so its declared label is
visible and distinguishable in the macOS authorization ceremony:
python3 scripts/deploy-host-controller.py deploy \
--requesting-agent Codex --requesting-session SESSION_LABELRoll back with the same identity flags and the rollback command.
See HOST_CONTROLLER_RELEASE_INTEGRITY_DESIGN.md.
The one-time migrations from a release without the protected authorization
broker and from the password-only broker schema use bootstrap-authorization
and bootstrap-touchid-schema, respectively. Normal mutations then present a
signed native window with explicit Touch ID, administrator-password, and cancel
choices. See OPERATOR_AUTHORIZATION_PROVENANCE_DESIGN.md
and TOUCH_ID_OPERATOR_AUTHORIZATION_DESIGN.md.
Approval and negative cancellation tests are separate, request-bound ceremonies;
terminal failures end in one content-free diagnostic with an explicit retry
disposition. See
OPERATOR_CEREMONY_DIAGNOSTICS_DESIGN.md.
Disposable Python execution has a stronger operation-specific ceremony: the native window requires Touch ID, offers no password fallback, and signs the exact run, Claude session, registered source, code, fixtures, timeout, and pinned Target runtime identity. Research consumes that short-lived approval once before it can contact the independently contained Target. See REQUEST_BOUND_EXECUTION_APPROVAL_DESIGN.md.
Installer publication and VM lifecycle maintenance use the same attributed
ceremony. Read-only installer validation and dry-run VM planning remain
prompt-free; Parallels execution is irreversibly deprivileged to the invoking
Mac user before prlctl runs. See
UNIVERSAL_HOST_MAINTENANCE_AUTHORIZATION_DESIGN.md.
Historical GLM/Claude continuity from a quarantined VM generation uses a separate content-bound preservation operation. Its dry run is prompt-free and its apply operation never boots a legacy guest or deletes a quarantined VM:
python3 scripts/vm-operation.py preserve-legacy \
--generation GENERATIONSee the legacy migration runbook
before using --apply.
PKI v2 deployment and full-generation rotation are serialized against agent launches through the same host coordination lock:
python3 scripts/pki-operation.py deploy
python3 scripts/pki-operation.py rotate \
--requesting-agent Codex --requesting-session SESSION_LABELThe rotation creates fresh CA, Gateway server, and three Research client keys, uses dual trust while changing generations, resumes a root-anchored pending transaction after interruption, and commits only after both guests verify the same manifest. Private keys remain on their owning VM. See PKI_LIFECYCLE_V2_DESIGN.md.
The implementation requires new agent launches to fail closed unless the host, Gateway, and Research all report the same generation UUID and manifest hash. Deployment state and acceptance evidence are intentionally not asserted here.
Report the ordinary host-direct Claude Code posture and independently verify the exact isolated launch path with fresh local and off-host evidence:
./scripts/labctl safety-auditThe generic safety-audit result is retained unchanged. Tier 4 applies only to
new runs through the exact reported labctl claude path while every live
boundary and policy-bound Azure evidence check passes.
Guest time is a launch boundary. Check it with ./scripts/labctl time-sync or,
while no Claude run is active, synchronize all three guests from the Mac with
./scripts/labctl time-sync --apply. Startup applies the same control before
boundary verification, and Claude launch fails closed when any guest differs
by more than five seconds. See TIME_SYNCHRONIZATION_DESIGN.md.
Import an exact public GitHub source snapshot without giving the agent, Bash, or Research general Internet access:
./scripts/labctl source plan OWNER/REPOSITORY REF
./scripts/labctl source apply PLAN_UUID
./scripts/labctl sourcesPlanning resolves the mutable ref to a full commit SHA. Applying consumes the
15-minute plan once, fetches only through the Gateway's UID-scoped GitHub
allowlist, validates and normalizes the archive in a networkless Research unit,
and publishes only after encrypted provenance is confirmed in locked Azure
storage. The result is a root-owned read-only baseline and a separate
agent-writable, non-executable working copy with no .git directory.
Claude Code has one root-managed local MCP capability for independently recomputing those imported hashes without Bash or network access:
verify_source(source_id="SOURCE_UUID", scope="both")
Fresh imports are tied to a root-owned per-entry import manifest. Older imports are reported explicitly as legacy/unanchored rather than given false assurance.
Assemble the exact scoped direct runtime dependencies of a verified imported npm source without granting npm, registry egress, or lifecycle execution:
./scripts/labctl dependencies plan SOURCE_UUID
./scripts/labctl dependencies status PLAN_UUID
./scripts/labctl dependencies apply PLAN_UUID
./scripts/labctl dependencies listMissing packages must first pass through the existing operator-reviewed
source npm-plan and source apply workflow. Published bundles are sealed and
explicitly non-transitive; they are analysis layouts, not runnable installs.
For a large imported source, build its query-only index while no agent run is active:
./scripts/labctl corpus index SOURCE_UUIDClaude then receives six root-managed capabilities: exhaustive literal search,
exhaustive fixed-profile scanning, bounded relevance exploration,
manifest-backed tree inventory, exact comparison of two registered source
manifests, and—for code-bearing sources with a sealed CodeGraph index—bounded
analyze_reachability. The last traces a fixed-profile static path between
exact symbols in exact registered files. Its result is always non-exhaustive
static evidence, labels the default entrypoint as caller-selected, and
explicitly does not claim measured execution, runtime branch feasibility, or
exploitability. Every excerpt is labelled untrusted; the agent cannot select an
arbitrary filesystem root, define regular expressions, rebuild the index,
execute source content, or add network access.
Successful exhaustive search/scan results include an immutable evidence receipt
minted by a dedicated corpus-broker identity. Disposable execution results
likewise include a receipt minted by execution-broker. The agent cannot read
or write the receipt registry, and receipt-backed finding evidence must reproduce
the instrument's returned evidence_receipt object exactly.
For empirical validation of a static finding, Claude also has one separately prompted high-risk capability:
run_python_fixture(
source_id="SOURCE_UUID",
code="...small experiment...",
fixture_paths=["optional/registered/file.py"],
timeout_seconds=5
)
The managed MCP call is configured to prompt for operator confirmation. That prompt is a human-control surface, not a cryptographic authorization consumed by the broker; containment does not depend on it. Execution runs in a fresh rootless Target container with no network, a read-only root filesystem, fixed non-root identity, and hard time/output/CPU/memory/PID bounds. It is not Bash, cannot select an image or mount, and can receive only re-hashed files from an immutable registered source. JavaScript remains unavailable in v1.
GLM may also propose durable institutional memory without being able to modify its own future instructions:
propose_memory(
category="working_principle",
title="...",
proposal="...",
rationale="...",
evidence_refs=["..."]
)
Proposals enter a bounded broker-owned spool as explicitly untrusted review
inputs. They are never auto-loaded or mechanically promoted. List pending
metadata with ./scripts/labctl memory proposals; accepted ideas must be
restated by the operator and architect in the root-owned persona and redeployed. Native
Claude auto-memory is disabled, and the exact launcher uses restricted mode so
general command-running tools are absent from GLM's tool surface.
Operator-published white-box handoffs live in a root-owned canonical store with
a read-only review-bundles/ view in GLM's workspace. GLM verifies one without Bash, arbitrary paths, or
disposable execution by calling:
verify_review_bundle(bundle_id="NAME-review")
The result proves that the readable bytes match the sealed handoff manifest; it does not prove that those bytes are deployed or correct.
GLM can also preserve a bounded security hypothesis with the managed findings tools. Submissions and amendments are immutable, source-bound candidates; they cannot validate their own claims or severity. The operator and architect adjudicate through the separate root-owned, hash-chained operator path:
./scripts/labctl findings
./scripts/labctl finding show FINDING_UUID
./scripts/labctl finding apply REQUEST.jsonSensitive vulnerability reports never belong in source control. The host
vulnerability-reports/ directory is quarantined by .gitignore; only its
tracked policy README may enter either private remote.
scripts/labctl claude creates a unique UUIDv4 run ID, claims a root-owned
marker in Research, prints the ID for the operator, and clears only that same
marker when the SSH session ends. A concurrent launch or boundary verification
fails closed instead of overwriting an active run's attribution. Relay and
Gateway events share one request ID end to end.
Every labctl claude run also fails closed unless the encrypted conversation
and artifact capture services are healthy. To inspect a completed run, use:
./scripts/labctl transcript RUN_IDResume an existing Claude conversation with:
./scripts/labctl claude --resume CLAUDE_SESSION_UUIDThe resume UUID is narrowly validated and is the only caller-supplied CLI
option the hardened launcher accepts. It must also appear in the root-owned
registry built from Claude journals created inside the Research workspace. List
eligible sessions with ./scripts/labctl sessions; it prints content-free
creation and activity timestamps and user-turn and compaction counts from the
Research journal, plus trusted post-DDD host-run counts and an optional private
operator label. Historical run counts are unknown rather than inferred from
untrusted journal bookkeeping. Manage labels with
./scripts/labctl session label SESSION_UUID "LABEL" and session unlabel.
Labels remain in a 0600 Mac-local catalog and are never derived from
conversation content. See
Session Discovery and Labels.
Each invocation receives a new SecureSandbox run ID for evidence correlation,
while the Claude session UUID remains stable across resumed invocations.
Session validation distinguishes a definitive registry miss from an unreachable Research VM or failed registry refresh. Both fail closed, but only the former is reported as an unregistered UUID; availability failures do not make claims about registry contents.
Inspect or enforce the bounded five-milestone Parallels recovery policy with:
./scripts/labctl snapshots
./scripts/labctl snapshots --applyEnforcement requires all three lab VMs to be stopped and at least 50 GiB of host free space. It never operates on unrelated Parallels VMs.
This downloads age-encrypted chunks from the 30-day locked Azure archive, verifies ciphertext hashes and sequence linkage, obtains the decryption identity from the Agents vault without writing it to disk, and emits the original Claude Code JSONL records. Those records include prompts, model-returned visible thinking, responses, tool calls/results, and approvals that Claude Code wrote to its journal. They cannot include private provider-side reasoning that the API never returned to Claude Code.
Agent-authored .md, .txt, and .json reports written directly under
/home/agent/research-workspace/reports are independently snapshotted after a
two-second stability window. Each revision is hashed, age-encrypted, transported
through the existing pinned mTLS evidence path, and retained in the locked Azure
archive. List or safely render immutable snapshots with:
./scripts/labctl artifacts
./scripts/labctl artifact ARTIFACT_UUIDThe renderer validates both ciphertext and plaintext hashes and neutralizes terminal control sequences before displaying agent-authored content.
Current readiness and explicit activation gates: IMPLEMENTATION.md.
Automated boundary reports are retained only in the ignored local runs/
directory.
Before publishing, install the repository hooks with
git config core.hooksPath .githooks. The pre-commit hook scans the exact Git
index, and the pre-push hook permits only public-release to
public-release after scanning every object reachable from the outgoing commit.
Run the complete offline exact-candidate gate with
./scripts/run-public-release-checks.sh HEAD. While hosted CI capacity is
deferred, GitHub's workflow is a manually dispatched post-publication verifier,
not an admission control. The initial public repository is therefore sealed
read-only with branch and tag rulesets immediately after visibility changes;
the sealing command also enables private vulnerability reporting. See
PUBLIC_RELEASE_CONTROLS_DESIGN.md.
Publish a later reviewed fast-forward with
./scripts/publish-public-github-release.py; it reruns the local gate and
performs the explicit unseal, exact push, and verified reseal ceremony.
Installation-local DDD identifiers belong in the ignored
.private/secure-enclave-ddd.json, created from
config/secure-enclave-ddd.example.json; generated signed authorization apps,
profiles, and bundle manifests are also excluded and rejected if force-added.
Large imported repositories can be searched exhaustively and navigated through bounded document structure without granting a general shell. The design and CodeGraph-admission criteria are in CORPUS_INTELLIGENCE_DESIGN.md.
No host CLI or agent configuration is managed by this project.
SecureSandbox is available under the MIT License.
The approved posture is a robust baseline, not a permanent freeze. Proposed functionality changes should be discussed against observed workflow needs, threat-modeled, implemented as narrowly as practical, and reverified before becoming the new baseline.