Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 218 additions & 0 deletions PLAN-CRED.md

Large diffs are not rendered by default.

60 changes: 33 additions & 27 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ The current plan makes these decisions:
| Initial guest | ARM64 Linux |
| Initial microVM backend | libkrun over Apple Hypervisor.framework |
| Runtime integration | Dedicated `abox-vmm` Go helper with a narrow cgo boundary |
| Guest network | No NIC and no libkrun TSI in milestone one |
| Model traffic | Guest agent calls providers; TSI inet for HTTPS only |
| Guest network | No guest NIC. TSI inet for allowlisted MCP HTTPS only |
| Model traffic | Host provider broker. TSI inet is MCP-only |
| Providers | OpenAI, Anthropic, and Grok through xAI |
| Repository state | Clean Git worktree only |
| Host workspace sharing | Prohibited |
Expand All @@ -60,7 +60,7 @@ The current plan makes these decisions:
| TUI framework | Bubble Tea v2, Bubbles, and Lip Gloss v2 |
| TUI style | Full-screen near-black interface with restrained status colors |
| agentgateway | Optional adapter; never required for basic operation |
| Connectivity broker | Host-owned, typed, endpoint-bound package and MCP broker |
| Connectivity broker | LLM broker is in. Package/MCP broker remains Planned |
| Package-manager compatibility | Origin rewrite to a guest loopback adapter, not HTTP(S) proxy |
| Instruction loading | Supervisor reads the captured host snapshot and host configuration |
| Repo instruction authority | Repo text cannot change policy, limits, connectivity, or tools |
Expand Down Expand Up @@ -182,9 +182,11 @@ The host-side `abox` process owns:
- Audit records
- Patch review and confirmed import

Provider credentials may be entered on the host (`/provider`) and are
copied into the guest agent so the model client runs inside the
microVM. The host must not run the agent loop or call provider APIs.
Provider credentials are entered on the host (`/provider`) and resolved
from env, macOS keychain, Vault, Azure Key Vault, or AWS Secrets Manager.
They are never written to session dirs or the guest disk. The host broker
calls the provider API; the host must not run the agent loop. MCP tokens
still enter the guest.

The host supervisor must remain small. It must not contain an arbitrary shell
execution path, generated-code runner, or generic guest-to-host file service.
Expand Down Expand Up @@ -220,8 +222,8 @@ The `abox-guest` worker and everything it starts are untrusted. The design
assumes the guest can become fully compromised, including guest root and the
guest kernel.

The guest owns the agent: the prompt, the model client, tools, and
everything the model starts.
The guest owns the agent: the prompt, tools, and everything the model
starts. Provider HTTPS is host-brokered.

The guest owns all effectful tools:

Expand All @@ -237,7 +239,8 @@ The guest owns all effectful tools:
- Applications started by the agent

The guest receives no model-provider credentials, host home-directory access,
cloud credentials, SSH keys, Docker socket, or read-write host mount.
cloud credentials, SSH keys, Docker socket, or read-write host mount. MCP
tokens still enter the guest.

### 4.4 External Services

Expand Down Expand Up @@ -682,10 +685,11 @@ Host may call:

Guest may call only:

- `FetchPackage`
- MCP stream methods defined in section 14.4
- `provider_open`, `provider_send`, `provider_cancel` (host LLM broker)
- Readiness and bounded log or status notifications

`FetchPackage` and MCP stream methods (section 14.4) remain Planned.

The guest must not invoke host tool, import, shell, or arbitrary-fetch
methods. Phase 3 tests both directions.

Expand Down Expand Up @@ -820,14 +824,14 @@ defense in depth but is not the primary boundary.

## 12. Agent Loop

The host supervisor owns the model interaction loop:
The guest owns the model interaction loop. The host broker performs provider HTTPS:

1. Receive the user's prompt from the TUI or `abox exec`.
2. Build the model request using configured instructions, the five ABox
tool schemas, and any approved discovered MCP tool schemas.
3. Stream model output into normalized host events.
3. Send the request through the host provider broker and stream events back.
4. When the model requests a tool, validate the tool name and arguments.
5. Send a typed tool request to `abox-guest` over RPC.
5. Run the tool in the guest.
6. Stream or collect the bounded guest result.
7. Display activity and result status in the TUI.
8. Return the result to the same provider conversation.
Expand Down Expand Up @@ -1011,11 +1015,11 @@ blocks while preserving the assistant content needed for subsequent turns.

### 13.4 Credentials

- Credentials remain only in host memory.
- Credentials are resolved by the host credential source from environment
variables or the operating system credential store.
- Credentials are never written to session logs.
- Credentials are never copied into the guest.
- LLM credentials remain only in host memory.
- Sources: env, macOS keychain, Vault KV v2, Azure Key Vault, AWS Secrets Manager.
- Credentials are never written to session logs or `config.raw`.
- LLM credentials are never copied into the guest.
- MCP tokens still enter the guest until section 14.4.
- Configuration stores credential references, not secret values.

The credential source is distinct from the connectivity broker. It resolves
Expand Down Expand Up @@ -1062,15 +1066,16 @@ Connectivity is independent from the guest runtime isolation profile.

- The trusted host supervisor may contact explicitly configured model-provider
endpoints.
- The connectivity broker may contact exact configured remote MCP endpoints on
behalf of the guest MCP client.
- The connectivity broker may fetch from exact configured package indexes on
behalf of guest package tooling.
- The guest remains without a NIC and without TSI.
- The guest MCP client may contact configured MCP endpoints over TSI inet.
- Package-index fetch remains Planned (section 14.4).
- The guest remains without a NIC.
- Direct mode does not imply unrestricted guest egress.

### 14.3 `agentgateway`

The LLM gateway adapter is Planned. Today `agentgateway` mode applies to MCP
endpoints; the host broker dials each model's `base_url`.

- ABox is a standalone client of a pre-existing agentgateway endpoint.
- ABox does not install a local gateway, Kubernetes CRDs, Helm charts, or an
agentgateway control plane.
Expand Down Expand Up @@ -1117,8 +1122,9 @@ enforced.

### 14.4 Connectivity Broker Contract

The first milestone includes a typed, allowlisted host broker for configured
package indexes and remote MCP servers. The broker is implemented by
This section is Planned for MCP and package indexes. The LLM provider broker
is already in. The first milestone includes a typed, allowlisted host broker
for configured package indexes and remote MCP servers. The broker is implemented by
`internal/connectivity` inside the trusted supervisor and does not run as a
separate daemon.

Expand Down Expand Up @@ -1185,7 +1191,7 @@ The contract enforces:
bounded and cancellable.
- The broker is not a TCP, CONNECT, SOCKS, DNS, or general HTTP forwarder.
- Provider, gateway, package-index, MCP, and host credentials remain on the
host and are never returned to the guest.
host and are never returned to the guest. MCP tokens are the current exception.
- In offline mode, all remote broker methods are refused.
- With required agentgateway enforcement, the broker may open only the
configured agentgateway endpoint and never a direct backend or package-index
Expand Down
51 changes: 39 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ abox
```

- `/provider` sets Grok, OpenAI, or Anthropic API keys
- `/credential` points a model at Vault, Azure Key Vault, or AWS Secrets Manager
- `/mcp` lists configured Streamable HTTP MCP servers and accepts a Bearer token (`abox mcp login` for OAuth)
- `abox --resume` reopens the latest session for this repo (same `root.raw`, LLM conversation, and TUI transcript). `abox --resume <id>` picks a session. Plain `abox` still starts a new session.
- `ctrl+c` quits
Expand Down Expand Up @@ -137,7 +138,7 @@ Clone of (1) for that run. This is /dev/vda → /. Repo, guest Git, agent writes
ABox does not boot (1). It copies (1) → (2), then the microVM uses (2). --resume skips the copy and boots the existing (2).

3. Config disk — sessions/<id>/config.raw
~1 MiB, read-only /dev/vdb. Session id, model, keys. Not cloned from the golden image, not an OS. It lives inside of the directory where your sandbox harness session lives.
~1 MiB, read-only /dev/vdb. Session id, model. Not cloned from the golden image, not an OS. It lives inside of the directory where your sandbox harness session lives.

The VM boots **only** the session clone, not the golden file. Destroy a session directory and that run’s guest files are gone; the golden image stays clean for the next `abox`. `make image-update` patches `/usr/local/bin/abox-guest` on an existing golden disk; `make image` rebuilds the golden disk from scratch.

Expand Down Expand Up @@ -239,19 +240,17 @@ abox exec --prompt "list the repository files"

## LLM Integration

ABox is an LLM **client/harness**. The model loop/context is not on the host (your ABox instance/harness running on your computer). Prompts, streaming, tool calls, and provider HTTPS all run inside `abox-guest` in the microVM. The host TUI forwards your text over vsock (`user_turn`) and renders `agent_event` frames. That is the same isolation idea as MCP: the sandbox is the trust boundary for anything the model sees or starts.
ABox is an LLM **client/harness**. The model loop/context is not on the host (your ABox instance/harness running on your computer). Prompts, streaming, and tool calls run inside `abox-guest` in the microVM. Provider HTTPS is brokered by the host. The host TUI forwards your text over vsock (`user_turn`) and renders `agent_event` frames. That is the same isolation idea as MCP: the sandbox is the trust boundary for anything the model sees or starts.

```go
func Stream(ctx context.Context, model config.Model, messages []Message, tools []ToolSchema) (<-chan Event, error)
func Stream(ctx context.Context, model config.Model, key string, client *http.Client, messages []Message, tools []ToolSchema) (<-chan Event, error)
```

`Stream` talks to one configured profile. xAI and OpenAI use Chat Completions (`/chat/completions`). Anthropic uses Messages (`/v1/messages`). Provider-side shell, code execution, and file tools stay off. The model only sees ABox’s five guest tools plus any MCP tools discovered in the guest.

![](img/prov1.png)
![](img/prov2.png)

Config lives at `~/.abox/config.yaml`. Keys are **not** stored in that file. `/provider` in the TUI writes `~/.abox/credentials.env` (mode 0600) and copies the value onto the sealed guest `config.raw` disk so the microVM can dial the API. Same as direct-mode MCP: the token has to live in the guest because the guest makes the HTTPS call.

Default profiles:

```yaml
Expand All @@ -277,9 +276,37 @@ models:

Pick one in the TUI with `/provider`, or pass `--model grok-default` (and the other profile names) on `abox` / `abox exec`. Missing `XAI_API_KEY` / `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` fails the turn, not VM boot (`abox --probe-vm` still works).

Guest egress is allowlisted: `api.x.ai`, `api.openai.com`, `api.anthropic.com` on HTTPS `:443` only. Those sockets leave via libkrun TSI inet (no guest NIC). Isolation is still **Planned**. A compromised guest can read the key on `config.raw`; the allowlist is ABox’s Go dialer, not a VMM guarantee.
Guest egress is allowlisted for configured MCP origins on HTTPS `:443` via libkrun TSI inet (no guest NIC). Provider HTTPS is host-brokered, so those hosts are not on the guest allowlist. Isolation is still **Planned**. The allowlist is ABox’s Go dialer, not a VMM guarantee.

LLM traffic does **not** take the MCP `connectivity.mode` path. Direct vs agentgateway today applies to MCP servers. The host broker hits the provider `base_url` above.

## Credentials

The following credential providers are supported (where your LLM API key lives):

| Source | `name` is | Auth |
| --- | --- | --- |
| `env` | environment variable (also reads `~/.abox/credentials.env`) | — |
| `keychain` | macOS keychain account (service `abox`) | — |
| `vault` | Vault KV v2 path (`secret/abox/anthropic`) | `VAULT_ADDR` + `VAULT_TOKEN` (or `~/.vault-token`) |
| `azure` | Key Vault secret URI (`https://myvault.vault.azure.net/secrets/name`) | `AZURE_CLIENT_ID` / `AZURE_TENANT_ID` / `AZURE_CLIENT_SECRET`, or `az login` |
| `aws` | Secrets Manager secret id | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (`AWS_REGION`), or `~/.aws/credentials` |

Config lives at `~/.abox/config.yaml`. Keys are **not** stored in that file. Each model or MCP server points at a source:

```yaml
credential:
source: keychain # env | keychain | vault | azure | aws
name: ANTHROPIC_API_KEY # env var, keychain account, vault path, Azure secret URI, or AWS secret id
# field: value # vault/aws only
# version: "4" # vault/azure only
```

`credential_env: XAI_API_KEY` is the same as `{source: env, name: XAI_API_KEY}`.

`/provider` and `/mcp` in the TUI save to the macOS keychain first, falling back to `credentials.env` (mode 0600) if the keychain is locked or missing. `/credential` writes a Vault / Azure Key Vault / AWS Secrets Manager reference into `config.yaml` (it does not store cloud tokens). When the cloud auth env vars are unset, Azure uses the local `az login` session and AWS uses `~/.aws/credentials` (and region from `~/.aws/config`). `abox creds migrate` moves existing `credentials.env` entries into the keychain.

LLM traffic does **not** take the MCP `connectivity.mode` path. Direct vs agentgateway today applies to MCP servers. The model client always hits the provider `base_url` above.
LLM keys stay on the host. MCP tokens still go to the guest because the guest makes those HTTPS calls.

## MCP Integration

Expand All @@ -301,7 +328,7 @@ type StreamableClientTransport struct {

![](img/mcpsandbox.png.png)

Config lives at `~/.abox/config.yaml` (same pattern as `~/.claude`, `~/.codex`). First `abox` run creates `~/.abox/` (mode 0700) and a default `config.yaml` if they are missing. Credentials are `~/.abox/credentials.env`.
Config lives at `~/.abox/config.yaml` (same pattern as `~/.claude`, `~/.codex`). First `abox` run creates `~/.abox/` (mode 0700) and a default `config.yaml` if they are missing. MCP tokens use the same credential sources as LLM keys (see [Credentials](#credentials)).

Add a Streamable HTTP server without editing YAML by hand. `--mode` is required:

Expand Down Expand Up @@ -412,14 +439,14 @@ Because of the above, Go or Rust are naturally great languages. Because I like G

## What is not done yet

Compaction, checkpoint/rollback/fork, stdio MCP, host broker, and resource
Compaction, checkpoint/rollback/fork, stdio MCP, host MCP/package broker, and resource
acceptance. See `PLAN.md`. Streamable HTTP MCP is in; isolation stays Planned.

## Security

Do not describe this build as verified isolation. The device plan is
allowlisted (no guest NIC, no host-path virtio-fs, TSI flags zero). Claims
stay Planned until the hardware suite in `PLAN.md` §21.4 passes.
allowlisted (no guest NIC, no host-path virtio-fs). TSI inet is MCP HTTPS only.
Claims stay Planned until the hardware suite in `PLAN.md` §21.4 passes.
## Whats Currently In Place

```
Expand All @@ -436,7 +463,7 @@ stay Planned until the hardware suite in `PLAN.md` §21.4 passes.
├──────────────────┼──────────────────────────────────────────────────────────────────────┤
│ Providers │ Grok/OpenAI (chat completions) + Anthropic Messages. /provider keys. │
├──────────────────┼──────────────────────────────────────────────────────────────────────┤
│ LLM egress │ Allowlist: api.x.ai, api.openai.com, api.anthropic.com via TSI inet │
│ LLM egress │ Host broker dials providers. Guest TSI inet is MCP-only
├──────────────────┼──────────────────────────────────────────────────────────────────────┤
│ MCP │ Guest Streamable HTTP client; direct URLs or exclusive agentgateway │
├──────────────────┼──────────────────────────────────────────────────────────────────────┤
Expand Down
Loading
Loading