Agent Workspace — design document (Phase 6, not shipped)
Status: design only. Per the SPB Git 2.0 directive, agentic code changes ship only once isolation, scoped permissions, approval gates and auditability can be demonstrated. None of that exists on the M1M32 gateway today (the forge runs as the owner's user with full access to every bare repository), so this phase deliberately delivers a design and the preconditions, not a feature.
1. Goal
Let the owner ask an agent, from an issue-like request, to analyse a
repository, propose a code change with tests, and open a reviewable change —
without ever giving the agent a shell on the gateway or write access to
~/srv/git.
2. Non-goals (explicit)
- No automatic merge, no automatic deploy, no push to a default branch.
- No agent execution on the gateway host (M1M32) — ever. The gateway holds every repository, the PAT store and the tunnel key.
- No access to private data: SPB Git has none (every repository is public), but
data/tokens.json,data/ai-usage.json,.envand the tunnel configuration must be unreachable from the sandbox.
3. Architecture
owner ──(PAT, approval UI)──▶ SPB Git (M1M32)
│ 1. creates a job record (audit log)
│ 2. issues a *scoped, short-lived* job token
▼
Agent runner (separate node, e.g. m4mn)
┌──────────────────────────────────────┐
│ container / sandbox per job │
│ - clone over HTTPS (anonymous) │
│ - no network except git.spboucher.ai│
│ and the configured model provider │
│ - CPU/RAM/time limits, no sudo │
│ - writes only inside the workspace │
└───────────────┬──────────────────────┘
│ 3. pushes to refs/agents/<job>/*
│ using the job token (branch-scoped)
▼
SPB Git: "Proposed change" page (diff, tests, logs)
│ 4. owner approves → fast-forward to
│ a normal branch or rejects3.1 Scoped job tokens (precondition in SPB Git)
Today a PAT grants every write. The workspace requires a new token class:
| Field | Meaning |
|---|---|
scope.repo |
exactly one repository |
scope.refs |
glob, e.g. refs/agents/<job-id>/* — receive-pack rejects any other ref via a pre-receive hook that validates the token id passed in GIT_AUTH_TOKEN_ID |
expires |
≤ 2 h |
capabilities |
push:agent-ref only — no API writes, no release uploads, no token management |
Implementation sketch: TokenStore.create(label, {scope}), smart-http
exports the verified token id to the git environment, and a pre-receive
hook (installed next to post-receive) checks refs against scope.refs.
3.2 Isolation (precondition on the runner node)
- One job = one fresh container (Docker on a Mac mini, or an Apptainer/
sandbox-execprofile as used by UQO-Chat). Root filesystem read-only except/work. - Egress allow-list:
git.spboucher.ai:443and the AI provider host only. - Resource limits: 2 CPU, 4 GB, 30 min wall clock, 2 GB disk; the runner kills the container on breach and records the reason.
- No secrets in the container except the job token (environment variable, revoked when the job ends).
3.3 Approval gates
| Action | Gate |
|---|---|
| Start a job | owner, authenticated with a full PAT, from the web UI or spbgit agent run <repo> "<task>" |
Agent pushes refs/agents/<job>/proposal |
automatic (scoped token) |
| Promote proposal to a real branch | owner approval in the UI (POST /api/v1/repos/:name/agent-jobs/:id/approve) |
| Merge / deploy | out of scope; the owner does it with normal tooling |
| Delete a job's refs | owner, with a confirmation dialog (spbConfirm) |
3.4 Audit log
Append-only data/agent-jobs.jsonl: job id, repository, requester token id,
task text, provider + model, container id, start/end, exit reason, token spend,
refs written, approval decision + approver token id. Exposed read-only at
/api/v1/agent-jobs (owner PAT) and on the proposal page.
3.5 Prompt-injection posture
The agent reads untrusted repository content. The same defenses as Phase 5
apply (content wrapped as data, instructions in the system prompt only), plus:
the agent cannot read anything outside its workspace, cannot reach the network
except the two allowed hosts, and cannot write outside refs/agents/<job>/*.
A hostile repository can therefore waste a job's budget but cannot exfiltrate
or damage anything.
4. Acceptance criteria before any implementation
- Scoped job tokens exist and are covered by e2e tests proving that a job
token cannot push to
refs/heads/*, cannot call/api/v1/reposwrites, and expires. - A runner node is prepared (
mld prepare), the sandbox profile denies all egress except the allow-list, and a test job proves the file-system and network limits. - The audit log and the proposal page exist and are covered by tests.
- The owner has reviewed this document and approved the runner node choice.
5. Estimated size
Tokens + pre-receive hook: ~2 days. Runner (containers, limits, job protocol): ~3 days. UI (proposal page, approval): ~2 days. Not started.