glove/docs/managed-client-setup.md
2026-08-23 09:18:59 -06:00

264 lines
10 KiB
Markdown

# Managed agent client setup
This runbook turns an operator-installed Codex, Claude Code, Pi, Copilot, or
OpenCode distribution into an explicit Glove runtime. It does not copy the
operator's home or make a client usable merely because it runs in the service
user's shell. Read the authority model and evidence status in
[client-adoption-matrix.md](client-adoption-matrix.md) first.
## Security outcome
A ready runtime has four independently reviewed closures:
1. an immutable executable and dependency closure;
2. a newly created `/home/agent` containing only Glove-owned client state;
3. optional credential leases at exact private-home targets; and
4. an exact egress policy, or `no-network`.
Keep the first deployment credential-free and offline. Provider access is a
separate profile and is never implied by client detection, Pi adoption, or a
successful `--version` probe.
## Prepare protected roots
Use owner-local roots outside client homes and enrolled projects. The examples
assume the Glove service user owns each directory and no other principal can
write it.
```sh
install -d -m 0700 \
"$HOME/.local/share/glove/harnesses" \
"$HOME/.local/share/glove/pi-adoption" \
"$HOME/.local/share/glove/evidence"
install -d -m 0700 "$HOME/.config/glove"
```
Do not use `$HOME/.codex`, `$HOME/.claude`, `$HOME/.pi`, `$HOME/.copilot`,
XDG client-state directories, authentication directories, or package caches as
runtime dependency roots.
## Detect, stage, and pin each distribution
Detection reads only explicitly listed directories and never inherited `PATH`:
```sh
glove policy detect \
--search-path /absolute/reviewed/client-bin \
--json
```
For every selected runtime, stage its supported package/interpreter closure
into the protected root. Review the dry run before confirming it.
```sh
glove policy stage \
--runtime <codex|claude-code|pi|copilot|opencode> \
--source /absolute/reviewed/client-bin/<executable> \
--directory "$HOME/.local/share/glove/harnesses" \
--dry-run --json
# Repeat the identical command with --yes only after reviewing its paths and digest.
```
Generate and inspect the individual runtime entry using only the staged
executable or an explicit protected search directory:
```sh
glove policy generate \
--runtime <runtime-id> \
--executable /absolute/protected/staged/<executable> \
--template-id <runtime-id>-safe
```
The adapter fixes the private home and native skill location:
| Runtime | Executable | Managed skill location | Glove-owned environment |
|---|---|---|---|
| `codex` | `codex` | `/home/agent/.codex/skills` | `CODEX_HOME=/home/agent/.codex` |
| `claude-code` | `claude` | `/home/agent/.claude/skills` | none |
| `pi` | `pi` | `/home/agent/.pi/agent/skills` | none |
| `copilot` | `copilot` | `/home/agent/.copilot/skills` | `COPILOT_HOME=/home/agent/.copilot` |
| `opencode` | `opencode` | `/home/agent/.config/opencode/skills` | `XDG_CONFIG_HOME=/home/agent/.config` |
Arguments, environment, and read-only dependencies are part of the local
runtime digest. A Sage plan can select only the resulting template ID and
digest; it cannot replace those fields.
## Adopt Pi packages without adopting Pi identity
Pi is the only built-in adapter with a package-adoption step. Use an explicitly
reviewed settings file and package store, then inspect the snapshot before
confirming it:
```sh
glove policy adopt-pi \
--settings /absolute/reviewed/pi/settings.json \
--package-store /absolute/reviewed/pi/package-store \
--directory "$HOME/.local/share/glove/pi-adoption" \
--dry-run --json
# Repeat with --yes only after reviewing selected packages, paths, and digests.
```
The result contains immutable package payloads and generated managed settings.
It excludes host authentication, sessions, provider/model selection, host
settings, package-manager credentials, and arbitrary Pi state. Never place
`auth.json` or a host Pi home in the adoption root.
## Generate the credential-free policy first
Build the full policy from the same reviewed roots. Omit `--secret` and
`--egress` for the baseline so the generated runtime remains credential-free
and uses `no-network`.
```sh
glove setup policy \
--search-path /absolute/protected/staged/bin \
--harness-root "$HOME/.local/share/glove/harnesses" \
--path-root /absolute/enrolled-project-root \
--output "$HOME/.config/glove/session-policy.json" \
--backend linux_production \
--runtime codex \
--runtime claude-code \
--runtime pi \
--runtime copilot \
--runtime opencode \
--pi-settings /absolute/reviewed/pi/settings.json \
--pi-package-store /absolute/reviewed/pi/package-store \
--pi-adoption-root "$HOME/.local/share/glove/pi-adoption" \
--dry-run --json
# Review the complete output, then repeat with --yes.
glove policy validate --file "$HOME/.config/glove/session-policy.json"
glove policy explain --file "$HOME/.config/glove/session-policy.json" --json
```
Select only runtime template IDs emitted by that policy when running `glove
setup`. Policy replacement is not live mutation: `gloved` freezes the policy
for its process lifetime, so restart it only after an approved atomic policy
replacement.
## Credential and egress profile
Create a separate online policy only after the offline lifecycle passes. A
credential source must be an owner-protected regular file outside every client
home and enrolled project. The controller receives only the opaque handle.
```sh
install -m 0600 /secure/input/client-credential \
"$HOME/.config/glove/<runtime>-credential.next"
glove setup policy \
<the reviewed baseline arguments> \
--secret <runtime-id> <opaque-handle> \
"$HOME/.config/glove/<runtime>-credential.next" \
/home/agent/<exact-client-private-target> \
--egress <policy-id> <exact-provider-host> 443 \
--dry-run --json
```
Repeat `--egress` only for additional exact hosts required by the reviewed
client flow. Do not use wildcards, broad private-network access, a host
credential directory, or a provider's entire domain family. A credential does
not authorize egress, and egress does not authorize a secret handle.
Repository-tested credential targets are deliberately narrow:
| Runtime | Repository-approved target | Status |
|---|---|---|
| `codex` | `/home/agent/.codex/auth.json` | Covered by secret-policy, lease, launch-binding, and lifecycle tests |
| `claude-code` | none | Operator must verify the exact target for the pinned distribution before enabling a secret |
| `pi` | none | Host Pi auth is explicitly excluded; a future model profile must separately approve an exact managed target |
| `copilot` | none | Operator must verify the exact target for the pinned distribution before enabling a secret |
| `opencode` | none | Operator must verify the exact target for the pinned distribution before enabling a secret |
"None" is not permission to mount a directory. Until an exact target gains
executable review evidence, that client remains credential-free.
## Rotation and revocation
1. Write a new owner-only source file under a new filename.
2. Generate a dry-run policy with a new handle or content lease.
3. Validate and atomically install the reviewed policy.
4. Restart only `gloved`; do not restart unrelated services.
5. Run one bounded session and verify its plan/profile/receipt digests.
6. Revoke the old provider credential.
7. Remove the old policy handle in the next reviewed policy revision.
8. Retain redacted evidence; never copy credential bytes into logs or receipts.
Do not overwrite a live source file in place. Content-versioned leases and an
exclusive lock serialize use, but policy revision and provider revocation are
still explicit operator actions.
## Validation lanes
### Repository enforcement
Run the normal suite plus the Linux managed-session cases. These use synthetic
executables and real Glove filesystem, cgroup, terminal, receipt, stop, and
cleanup paths. A topology skip is a limitation, not passing enforcement
evidence.
### Installed distributions
Prepare one immutable test root with this reviewed shape:
```text
<harness-root>/
node_modules/.bin/{codex,claude,pi,copilot,opencode}
node-runtime/bin/
```
Then run the dedicated credential-free lane:
```sh
GLOVE_TEST_NATIVE_HARNESS_ROOT=/absolute/protected/harness-root \
ctest --test-dir build/dev --output-on-failure \
-R '^container_linux_native_harness_matrix$'
```
The lane invokes only `--version`, uses a fresh managed home, binds a verified
skill projection, enforces limits, verifies terminal/profile receipts, and
requires an empty materialization root after every client. If the variable or
a real distribution is absent, the lane must report a skip; synthetic clients
must not be substituted.
### Private model smoke
For each client, use a dedicated least-privilege credential and exact provider
egress. Submit one bounded non-sensitive prompt, disable wallet authority and
retained host writes, record plan/session/profile/receipt digests, stop the
session, and verify cleanup. This is private deployment evidence, not public CI.
Never claim model access from a successful `--version` probe.
## Recovery and evidence checklist
Record, without credential contents:
- client version and distribution/source digest;
- staged closure and Pi-adoption manifest digests;
- policy revision, runtime-template ID, and adapter digest;
- allowed secret handles and egress policy IDs;
- plan, profile, projection, terminal, and resource-receipt digests;
- stop/cancellation outcome and empty materialization root; and
- skip reasons for missing distributions or unavailable kernel enforcement.
After daemon or host recovery, require the same session identity and profile
digest before reconciling terminal state. A changed executable, dependency,
policy, Pi package snapshot, credential handle, or egress target requires a new
reviewed policy revision. Do not resume a session from host client state.
## Current limitations
- Repository tests prove shared Glove enforcement; they do not prove every
vendor distribution remains compatible.
- Missing installed clients are explicit skips, not passes.
- Model-backed smoke requires private operator credentials and exact provider
egress and is intentionally absent from public CI.
- Tool-policy identifiers are plan-bound, but harness-native tool enforcement
is not yet a supported claim.
- Managed Pi does not gain Sage adapter connectivity until a policy-enforced
Sage bridge exists.
- Linux resource claims require delegated cgroups and the required mount,
namespace, and seccomp capabilities.