Repository Public MIT Selected
1<!--2 ─────────────────────────────────────────────3 SPB Git — Personal Git Platform4 ─────────────────────────────────────────────5 Author : Simon-Pierre Boucher6 Contact : contact@spboucher.ai7 File : docs/AGENT-WORKSPACE-DESIGN.md8 Purpose : Phase 6 — design (not implementation) of the security-gated Agent Workspace9 License : MIT © Simon-Pierre Boucher10 ─────────────────────────────────────────────11-->12# Agent Workspace — design document (Phase 6, not shipped)1314**Status: design only.** Per the SPB Git 2.0 directive, agentic code changes15ship only once isolation, scoped permissions, approval gates and auditability16can be demonstrated. None of that exists on the M1M32 gateway today (the forge17runs as the owner's user with full access to every bare repository), so this18phase deliberately delivers a design and the preconditions, not a feature.1920## 1. Goal2122Let the owner ask an agent, from an issue-like request, to analyse a23repository, propose a code change with tests, and open a reviewable change —24without ever giving the agent a shell on the gateway or write access to25`~/srv/git`.2627## 2. Non-goals (explicit)2829- No automatic merge, no automatic deploy, no push to a default branch.30- No agent execution on the gateway host (M1M32) — ever. The gateway holds31 every repository, the PAT store and the tunnel key.32- No access to private data: SPB Git has none (every repository is public), but33 `data/tokens.json`, `data/ai-usage.json`, `.env` and the tunnel configuration34 must be unreachable from the sandbox.3536## 3. Architecture3738```39owner ──(PAT, approval UI)──▶ SPB Git (M1M32)40 │ 1. creates a job record (audit log)41 │ 2. issues a *scoped, short-lived* job token42 ▼43 Agent runner (separate node, e.g. m4mn)44 ┌──────────────────────────────────────┐45 │ container / sandbox per job │46 │ - clone over HTTPS (anonymous) │47 │ - no network except git.spboucher.ai│48 │ and the configured model provider │49 │ - CPU/RAM/time limits, no sudo │50 │ - writes only inside the workspace │51 └───────────────┬──────────────────────┘52 │ 3. pushes to refs/agents/<job>/*53 │ using the job token (branch-scoped)54 ▼55 SPB Git: "Proposed change" page (diff, tests, logs)56 │ 4. owner approves → fast-forward to57 │ a normal branch or rejects58```5960### 3.1 Scoped job tokens (precondition in SPB Git)6162Today a PAT grants every write. The workspace requires a new token class:6364| Field | Meaning |65|---|---|66| `scope.repo` | exactly one repository |67| `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` |68| `expires` | ≤ 2 h |69| `capabilities` | `push:agent-ref` only — no API writes, no release uploads, no token management |7071Implementation sketch: `TokenStore.create(label, {scope})`, `smart-http`72exports the verified token id to the git environment, and a `pre-receive`73hook (installed next to `post-receive`) checks `refs` against `scope.refs`.7475### 3.2 Isolation (precondition on the runner node)7677- One job = one fresh container (Docker on a Mac mini, or an Apptainer/`sandbox-exec`78 profile as used by UQO-Chat). Root filesystem read-only except `/work`.79- Egress allow-list: `git.spboucher.ai:443` and the AI provider host only.80- Resource limits: 2 CPU, 4 GB, 30 min wall clock, 2 GB disk; the runner kills81 the container on breach and records the reason.82- No secrets in the container except the job token (environment variable,83 revoked when the job ends).8485### 3.3 Approval gates8687| Action | Gate |88|---|---|89| Start a job | owner, authenticated with a full PAT, from the web UI or `spbgit agent run <repo> "<task>"` |90| Agent pushes `refs/agents/<job>/proposal` | automatic (scoped token) |91| Promote proposal to a real branch | owner approval in the UI (`POST /api/v1/repos/:name/agent-jobs/:id/approve`) |92| Merge / deploy | **out of scope**; the owner does it with normal tooling |93| Delete a job's refs | owner, with a confirmation dialog (`spbConfirm`) |9495### 3.4 Audit log9697Append-only `data/agent-jobs.jsonl`: job id, repository, requester token id,98task text, provider + model, container id, start/end, exit reason, token spend,99refs written, approval decision + approver token id. Exposed read-only at100`/api/v1/agent-jobs` (owner PAT) and on the proposal page.101102### 3.5 Prompt-injection posture103104The agent reads untrusted repository content. The same defenses as Phase 5105apply (content wrapped as data, instructions in the system prompt only), plus:106the agent cannot read anything outside its workspace, cannot reach the network107except the two allowed hosts, and cannot write outside `refs/agents/<job>/*`.108A hostile repository can therefore waste a job's budget but cannot exfiltrate109or damage anything.110111## 4. Acceptance criteria before any implementation1121131. Scoped job tokens exist and are covered by e2e tests proving that a job114 token cannot push to `refs/heads/*`, cannot call `/api/v1/repos` writes, and115 expires.1162. A runner node is prepared (`mld prepare`), the sandbox profile denies all117 egress except the allow-list, and a test job proves the file-system and118 network limits.1193. The audit log and the proposal page exist and are covered by tests.1204. The owner has reviewed this document and approved the runner node choice.121122## 5. Estimated size123124Tokens + pre-receive hook: ~2 days. Runner (containers, limits, job protocol):125~3 days. UI (proposal page, approval): ~2 days. Not started.126