From 30d92bef488d8bb3e45d3f61a2dabf3c69aa74da Mon Sep 17 00:00:00 2001 From: didericis Date: Tue, 12 May 2026 23:57:50 -0400 Subject: [PATCH] docs: drop ssh from README/example, supersede PRD 0007 (PRD 0009) - README architecture diagram drops the socat/ssh image box and the agent's ~/.ssh/config; the prose-bullets section drops the ssh image; the manifest example swaps `ssh:` for `git:` so someone copy-pasting it picks up the new shape. - claude-bottle.example.json: `default` bottle's `"ssh": []` is gone (now just an empty bottle); the gitea-dev example already uses `git:` since the ExtraHosts work. - PRD 0007 carries a "Superseded by PRD 0009" header at the top with a one-paragraph block explaining why; the file stays so the rationale of the prior design is still in-tree. - git_gate.py: drop the now-stale shadow-route mention from a docstring (the validator went away in the manifest layer). --- README.md | 26 +++++++------------------- claude-bottle.example.json | 1 - claude_bottle/git_gate.py | 5 ++--- docs/prds/0007-ssh-egress-gate.md | 9 ++++++++- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 56b8876..51979a2 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,7 @@ that enforces the manifest before it leaves the host. │ │ built locally) │ │ (TLS bump, DLP,│ │ hosts │ │ │ │ allowlist) │ │ │ │ skills, env, │ └────────────────┘ │ - │ │ ~/.ssh/config, │ │ - │ │ ~/.gitconfig │ ssh ┌────────────────┐ │ TCP to - │ │ │ ───────────────► │ socat/ssh image│──┼──► bottle.ssh - │ │ │ │ (alpine/socat, │ │ upstreams - │ │ │ │ L4 forwarder) │ │ - │ │ │ └────────────────┘ │ - │ │ │ │ + │ │ ~/.gitconfig │ │ │ │ │ git ops ┌────────────────┐ │ SSH (push/ │ │ │ ───────────────► │ git-gate image │──┼──► fetch) to │ │ │ │ (gitleaks + │ │ bottle.git @@ -98,16 +92,12 @@ that enforces the manifest before it leaves the host. - **agent image** — built from the repo `Dockerfile` (`node:22-slim` base) on first run; runs `claude` with the manifest-granted skills, - env vars, `~/.ssh/config`, and `~/.gitconfig` (the latter for the - git-gate's `pushInsteadOf` rules when `bottle.git` is set). + env vars, and `~/.gitconfig` (the latter for the git-gate's + `insteadOf` rules when `bottle.git` is set). - **pipelock image** — per-agent sidecar. Terminates the agent's outbound HTTP/HTTPS, enforces the resolved allowlist, runs DLP scanning. Design in `docs/prds/0001-per-agent-egress-proxy-via-pipelock.md` and `docs/prds/0006-pipelock-tls-interception.md`. -- **socat/ssh image** — per-agent sidecar built on `alpine/socat`. - One container, one socat listener per `bottle.ssh` entry, each - forwarding TCP to the upstream `Hostname:Port`. SSH does *not* go - through pipelock. Design in `docs/prds/0007-ssh-egress-gate.md`. - **git-gate image** — per-agent sidecar built on `zricethezav/gitleaks` (alpine + gitleaks + git-daemon + openssh-client). Runs `git daemon` over `git://` as a bidirectional mirror of each @@ -160,14 +150,12 @@ project entries overriding home entries on key conflict). "GIT_AUTHOR_NAME": "didericis" }, - "ssh": [ + "git": [ { - "Host": "gitea", - "Hostname": "gitea.dideric.is", - "User": "git", - "Port": 30009, + "Name": "claude-bottle", + "Upstream": "ssh://git@gitea.dideric.is:30009/didericis/claude-bottle.git", "IdentityFile": "/Users/didericis/.ssh/id_ed25519_gitea", - "KnownHostKey": "gitea.dideric.is ssh-ed25519 AAAA..." + "KnownHostKey": "ssh-ed25519 AAAA..." } ], diff --git a/claude-bottle.example.json b/claude-bottle.example.json index d288613..1ac6163 100644 --- a/claude-bottle.example.json +++ b/claude-bottle.example.json @@ -2,7 +2,6 @@ "bottles": { "default": { "env": {}, - "ssh": [], "egress": { "allowlist": [ "github.com", diff --git a/claude_bottle/git_gate.py b/claude_bottle/git_gate.py index e95c63c..0de6c22 100644 --- a/claude_bottle/git_gate.py +++ b/claude_bottle/git_gate.py @@ -93,9 +93,8 @@ class GitGatePlan: def git_gate_upstreams_for_bottle(bottle: Bottle) -> tuple[GitGateUpstream, ...]: - """Lift each `bottle.git` entry into a GitGateUpstream. Cross-entry - validation (unique Names, no shadow route with bottle.ssh) already - ran in `manifest.Bottle.from_dict`.""" + """Lift each `bottle.git` entry into a GitGateUpstream. Unique-Name + validation already ran in `manifest.Bottle.from_dict`.""" return tuple( GitGateUpstream( name=e.Name, diff --git a/docs/prds/0007-ssh-egress-gate.md b/docs/prds/0007-ssh-egress-gate.md index 6ec1f66..f23f428 100644 --- a/docs/prds/0007-ssh-egress-gate.md +++ b/docs/prds/0007-ssh-egress-gate.md @@ -1,9 +1,16 @@ # PRD 0007: SSH egress gate -- **Status:** Draft +- **Status:** Superseded by PRD 0009 (2026-05-13) - **Author:** didericis - **Created:** 2026-05-12 +> **Superseded.** The ssh-gate sidecar and `bottle.ssh` manifest field +> described below were removed in PRD 0009. Every upstream this PRD +> targeted has since been folded into PRD 0008's git-gate, which +> covers the same use case with credential isolation and gitleaks +> scanning instead of bare L4 forwarding. Kept in-tree for the +> history of intent. + ## Summary Per-agent TCP-forwarder sidecar built from `bottle.ssh` entries; SSH stops