feat(manifest): add bottle.git field for git-gate upstreams
test / unit (pull_request) Successful in 12s
test / integration (pull_request) Successful in 15s

Each entry pairs a Name (local alias the gate exposes) with an
ssh:// Upstream URL, an IdentityFile the gate uses to push to
that upstream, and an optional KnownHostKey for upstream
host-key pinning. The Upstream URL is parsed at construction
into UpstreamUser/Host/Port/Path so downstream code doesn't
re-parse.

Two cross-validation rules: Names must be unique within a
bottle (each maps to a distinct bare repo), and no git entry's
(host, port) may overlap an ssh entry's (Hostname, Port) — the
same upstream reachable two ways would let a misbehaving agent
route around the gitleaks-bearing git-gate via the L4 ssh-gate.

PRD: docs/prds/0008-git-gate.md
This commit is contained in:
2026-05-12 18:48:14 -04:00
parent c91395425c
commit 5c5e9f817e
4 changed files with 374 additions and 2 deletions
+29
View File
@@ -65,6 +65,31 @@ def fixture_with_ssh_dict() -> dict[str, Any]:
}
def fixture_with_git_dict() -> dict[str, Any]:
"""Bottle declares a git-gate upstream. JSON shape."""
return {
"bottles": {
"dev": {
"git": [
{
"Name": "claude-bottle",
"Upstream": "ssh://git@gitea.dideric.is:30009/didericis/claude-bottle.git",
"IdentityFile": "/dev/null",
"KnownHostKey": "ssh-ed25519 AAAA...",
},
{
"Name": "foo",
"Upstream": "ssh://git@github.com/didericis/foo.git",
"IdentityFile": "/dev/null",
"KnownHostKey": "ssh-ed25519 BBBB...",
},
]
}
},
"agents": {"demo": {"skills": [], "prompt": "", "bottle": "dev"}},
}
def fixture_minimal() -> Manifest:
return Manifest.from_json_obj(fixture_minimal_dict())
@@ -77,6 +102,10 @@ def fixture_with_ssh() -> Manifest:
return Manifest.from_json_obj(fixture_with_ssh_dict())
def fixture_with_git() -> Manifest:
return Manifest.from_json_obj(fixture_with_git_dict())
def write_fixture(fn: Callable[[], dict[str, Any]]) -> Path:
"""Write fixture JSON to a temp file; return the path. Caller must rm.
Accepts a function returning either a dict (JSON shape) or a Manifest;