Host CLI access to a TechVault seat¶
Host Claude Code and Codex use OpenSSH to carry MCP stdio to a fixed guest
aptl mcp-access dispatch command. The user's provider login stays in the host
client account. The transport enrolls a separate Ed25519 public key; it does
not copy provider authentication to the guest. MCP execution, service keys,
capture, artifact access and Docker authority remain in guest management.
Guest enrollment¶
Start the canonical packaged full lab with normal aptl lab start. An operator
then runs aptl mcp-access prepare-guest --request REQUEST.json --output-dir DIR.
DIR must be new and absolute. The typed request contains:
owner_id,seat_id,instance_id, positivegeneration;- separate
guest_endpointandouter_endpointobjects, each with a loopbackaddressand TCPport; - absolute
project_dir,management_home,docker_socket,node_executable,aptl_executable,host_keyandhost_public_keypaths; - the dedicated SSH
username, with/bin/shand a management-owned home; keys, each with a uniquegrant_id, Ed25519public_key,profile(redorblue) and timezone-awareexpires_atno more than 24 hours away;delivery: rootful-integrationfor controlled local software integration, ordelivery: appliancewithappliancepaths to the verified launch descriptor, release/qualification public keys and fresh runtime boundary observation.
Enrollment proves the full live workload and capture before writing private
binding.json, access.json, individual grant records, authorized_keys and
sshd_config. The dedicated listener uses only that generated configuration;
do not append these keys to a general login account. The downstream supervisor
owns user/key creation, listener startup, file ownership and outer port mapping.
The account must be valid for public-key login with UsePAM no; a locked
account is rejected by OpenSSH before key authorization. Disable password
login through the generated policy and a non-password account record. Keep
authorized-key paths and their ancestors free of group/other write access.
The dispatcher runs as a trusted guest service identity with the selected
backend's management access; participants receive no general login to it.
The listener permits public-key authentication and a forced selector only:
aptl-mcp-v1 INSTANCE GENERATION SERVER. It denies PTYs, forwarding, agent
forwarding, passwords, environment injection, user rc files, SFTP and arbitrary
commands. Guest management paths and backend argv are never participant inputs.
Host configuration¶
Obtain the access record, your public grant record, and the SSH host public key through the authenticated seat-management channel. Verify the host-key SHA256 fingerprint independently. Then, from a private project directory:
aptl mcp-access configure \
--access-record /absolute/access.json --grant /absolute/caller.grant.json \
--host-public-key /absolute/ssh_host_ed25519_key.pub \
--expected-host-key 'SHA256:VERIFIED_FINGERPRINT' \
--identity-file /absolute/host_transport_key --username aptl-mcp \
--owner-id alice --seat-id seat-1 --instance-id instance-1 \
--project-dir /absolute/project --client claude
Use --client codex for .codex/config.toml; Claude uses .mcp.json. Follow the
client's native project trust and MCP approval flow. Provider login remains the
normal client login. The generated entries use a private per-generation known
hosts file, StrictHostKeyChecking=yes, no agent, no multiplexing and -F /dev/null.
Manual settings survive regeneration. Ownership metadata records the exact
managed entries; conflicting edits, duplicate JSON keys, stale generations,
changed host pins without a new generation, or another owner's instance fail
without overwriting the config. A locked write-ahead journal recovers an
interrupted publication. .aptl and .codex state directories must be owned by
the caller and mode 0700. The target config becomes mode 0600.
Discovery, authority and lifecycle¶
aptl.seat-access/v1 contains owner/seat/instance/generation, guest boot and
Docker daemon IDs, project, full 64-hex container IDs, installed pack identity,
distinct guest/outer endpoints, host-key fingerprint, UTC observation time and
lifecycle state. It contains no secrets and grants no authority. Host setup
requires ready and an observation at most 120 seconds old.
The guest binding and enrolled key establish authority. Each operation rechecks
caller identity, expiry/revocation, role, unchanged runtime identity and required
capture. Appliance delivery additionally checks the signed host-MCP contract and
boundary evidence at most five seconds old. A changed endpoint or reset needs a
new generation, regenerated enrollment and refreshed host configuration.
aptl mcp-access refresh --binding PATH --output PATH refreshes discovery only;
aptl mcp-access revoke --binding PATH --grant-id ID revokes active and future
connections. Refresh cannot extend a grant.
Concurrent MCP servers share the guest lifecycle observation lock. These reads exclude start/reset mutations, and an active lifecycle mutation rejects access.
The relay verifies the exact canonical tool inventory before returning MCP initialization. It supports tool listing/calls, ping and lifecycle notifications; resources, prompts, sampling and server-initiated requests are denied. Limits: 1 MiB frames, eight queued requests, 120-second calls, 300-second idle timeout, one-hour connection lifetime, 64 MiB output and one process per instance/server. Revocation closes the connection and terminates its process group. Strict remote SSH teardown must acknowledge closure; otherwise a private taint blocks reuse until operator recovery creates a clean generation. Do not remove a taint as a substitute for proving remote cleanup.
Tool schemas retain their JSON Schema structure. Terminal session handles remain available to the caller for subsequent commands and closure; API login sessions, service credentials and secret-shaped command output stay redacted.
Software integration checks¶
After building the MCP packages, run:
These checks start an ordinary-user OpenSSH listener on an allocated loopback port. They exercise the production dispatcher and relay with built MCP processes, including red-role discovery, a blue-role indexer query against a controlled HTTPS service, revocation, runtime identity mismatch and the browser WebSocket route. Deployment and capture observations are fixtures. The checks do not access Docker, alter host limits or qualify a deployed lab.
The payload tests initialize all eight MCP servers from archived and extracted build outputs after removing their temporary source tree. Flattened common-library packages retain their own dependencies so runtime imports remain self-contained. The tests supply a fresh public CA after extraction for services that require runtime trust material; they make no service calls.
The checks workflow runs this suite after building the MCP packages. The clean-install job uses the smaller scenario to cover package materialization and the install/start/teardown lifecycle. Full TechVault tests are triggered manually during related development. They are intentionally separate from routine CI because running the full lab on every change is too costly.
Client-driven checks used Claude Code 2.1.274 and Codex 0.154.0 to invoke
kali_info through the generated SSH entries. Both returned the built backend's
target_name: Kali Linux and ssh_user: kali. Codex also exercised its native
app-server MCP call interface. Headless Codex requires explicit approval for the
selected MCP tool; this check approved only kali_info for that invocation.
Provider authentication stayed in the host client account. These checks prove
client/transport interoperability, not command execution in a booted guest.
Appliance-seat integration¶
aptl seat start now carries this contract through the real VM management
channel. The launcher publishes a nonce-bound enrollment request in the
read-only launch share, accepts the guest response only on the private
generation-specific socket, persists the owner-only access bundle, and writes
the selected Claude and Codex configurations. Stop and failed start invalidate
the generation; reset destroys the overlay, rotates instance/generation state,
and requires new client material. Calls made with stale files therefore fail
instead of silently reaching a replacement seat.
Production qualification invokes a read-only tool through both native client configurations, proves revocation after stop, resets the seat, and records the current guest/container identities. The two-seat machine-A drill and distinct machine-B drill remain separately signed evidence; neither unit protocol tests nor two VMs on one machine substitute for that evidence.