feat(git-gate+orchestrator): slice 10 — source-IP-keyed multi-tenant repo namespace #365
Closed
didericis-claude
wants to merge 2 commits from
orchestrator-gitgate-multitenant into orchestrator-rename-gateway
pull from: orchestrator-gitgate-multitenant
merge into: didericis:orchestrator-rename-gateway
didericis:main
didericis:lazy-backend-imports
didericis:fix-orchestrator-recreate-drops-tokens
didericis:post-build-image-smoke-test
didericis:orchestrator-agent-compose
didericis:orchestrator-gateway-ca
didericis:orchestrator-consolidated-launch
didericis:orchestrator-gateway-provision
didericis:orchestrator-gateway-network
didericis:orchestrator-client
didericis:orchestrator-gateway-net
didericis:orchestrator-gitgate-provision
didericis:orchestrator-registration
didericis:orchestrator-lifecycle
didericis:orchestrator-supervise-writers
didericis:orchestrator-supervise-multitenant
didericis:orchestrator-rename-gateway
didericis:orchestrator-slice8
didericis:orchestrator-slice7
didericis:orchestrator-slice6
didericis:prd-orchestrator
didericis:orchestrator-slice5
didericis:orchestrator-slice4
didericis:orchestrator-slice3
didericis:orchestrator-slice2
didericis:ci-kvm-runner
didericis:firecracker-backend
didericis:issue-330-cached-images
didericis:forge-native-integration
didericis:prd-smolmachines-linux
didericis:claude-forward-host-credentials
didericis:fold-orchestrator-subpackage
didericis:prd-egress-control-plane
didericis:manifest-break-import-cycle
didericis:dlp-supervise-quality-fixes
didericis:table-drive-dlp-tests
didericis:fix-integration-test-failures
didericis:fix/macos-container-relative-dockerfile
didericis:prd-0054-install-script
didericis:commit-bottle-state
didericis:pr-211
didericis:move-codex-auth-to-contrib
didericis:feat/pipelock-skip-scan-extensions
didericis:prd-0049-named-labelled-agents
didericis:harden-git-gate-shell-rendering
Labels
Clear labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Breaking change that won't be backward compatible
Something is not working
Documentation changes
Improve existing functionality
New functionality
This is security issue
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: didericis/bot-bottle#365
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "orchestrator-gitgate-multitenant"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
First slice of git-gate consolidation (the docker "Everything" consolidation program). Makes the smart-HTTP git backend serve every bottle from one process, selecting each request's repo root by the calling bottle's source IP — the same attribution invariant +
PolicyResolverthe multi-tenant egress addon already uses.Why HTTP, not
git daemongit daemoncan't source-IP-route per connection, so the consolidated gateway serves git-gate over the Pythongit_http_backend.py(HTTP, port 9420 — the transport firecracker/macOS already use). This slice makes that transport multi-tenant; wiring the docker path onto it (replacinggit daemon) lands with the launch-integration slice.Changes
policy_resolver: factor out_post_resolve; addresolve_bottle_id(source IP → bottle id, same fail-closed403→Nonecontract asresolve). The git-gate has no policy blob to parse — the bottle is the namespace.git_http_backend:resolve_repo_root(resolver, base, source_ip, token)— single-tenant passthrough when no resolver; else<base>/<bottle_id>, fail-closed on unattributed / resolver error / namespace-escape.BOT_BOTTLE_ORCHESTRATOR_URL(same env as egress) flips the shared gateway multi-tenant. The identity header is read for attribution and never forwarded to the CGI. Per-repo creds + hooks already scope by repo directory, so isolating the root per bottle isolates its creds too.Verification
Single-tenant path unchanged — the existing real-git-push tests stay green. New unit coverage for
resolve_bottle_id+ the repo-root selection matrix (passthrough / attributed / unattributed / error / escape). Full unit suite green (1690 tests; the 13test_sidecar_init/bin/sleeperrors are pre-existing NixOS-local noise).Stacked on #364 (the Sidecar→Gateway rename).
🤖 Generated with Claude Code
@@ -9,0 +13,4 @@the unspoofable source IP via the orchestrator. Per-repo credentials +hooks scope by repo directory, so isolating the *root* per bottle isolatesits creds too. Unattributed clients fail closed (404). Unset → the legacyper-bottle single-tenant flat root, unchanged.Should note that we're going to remove this/legacy behavior will be stripped out once we've got all the different backends working.
@@ -40,10 +106,24 @@ class GitHttpHandler(BaseHTTPRequestHandler):def do_POST(self) -> None:self._run_backend()def _project_root(self) -> Path | None:nit, but should change this to
_sandbox_rootor something that better indicates this is scoped per sandbox, and not a "project" like a specific repo included in git-gate (project is too amorphous/non specific)@@ -73,0 +75,4 @@fetches from the orchestrator so revocations / changes / teardownsare honored immediately. `identity_token` is optional — omit it toresolve by source IP alone (the network-layer attribution).Raises `PolicyResolveError` if the orchestrator can't be reached."""think we should always require the identity token... is there a reason not to?
Addressed in
115a64c.git_http_backend.py— "note that the legacy single-tenant behavior gets stripped out" (#3411): Agreed. Added the caveat in both the module docstring andresolve_sandbox_root's docstring — theresolver is Noneflat-root path is transitional and comes out once every backend runs the consolidated gateway.git_http_backend.py—_project_rootnaming (#3413): Renamed tosandbox:_project_root→_sandbox_root, the localproject_root→sandbox_root,resolve_repo_root→resolve_sandbox_root,DEFAULT_PROJECT_ROOT→DEFAULT_REPO_ROOT. KeptGIT_PROJECT_ROOTas-is (that's git's own env-var name, not ours).policy_resolver.py— always require the identity token (#3415): Good instinct, and I agree it's the right end state — defense-in-depth only counts if it's enforced, so a misconfigured/31+nft shouldn't silently degrade to "source IP is the sole attributor." The one real reason it's optional today: identity-token delivery is still a PRD 0070 open question ("exactly how the per-bottle token is placed where the agent can present it but not swap in another's"). Until that exists, the agent has no token to send, so requiring it would make consolidated mode dead-on-arrival. Source-IP-only is the transitional bridge — sound by construction on Firecracker (/31+nft make source IP unforgeable), genuinely weaker on docker.So the plan: flip it to mandatory at the
/resolveboundary (the endpoint rejects an empty token) as part of the launch-integration slice that also wires token delivery — not as a standalone change here, since enforcing it before delivery exists just breaks the path. I documented that trajectory inresolve's docstring so it's not lost. If you'd rather I enforce it now and let that pull token-delivery forward into this arc, say the word.pyright 0 errors, pylint 9.95/10, unit suite green after these changes.
@didericis-claude file an issue RE the token
Filed as #366 — Require the per-bottle identity token at /resolve (retire source-IP-only attribution).
Captures the why (delivery is the blocker), the end-state change (
/resolverejects an empty token, drop the resolver default + the source-IP-only branch), and that it's sequenced with the launch-integration slice that wires token delivery. The transitional caveats already in the code point at this direction.feat(git-gate): source-IP-keyed multi-tenant repo namespaceto feat(git-gate): slice 10 — source-IP-keyed multi-tenant repo namespacefeat(git-gate): slice 10 — source-IP-keyed multi-tenant repo namespaceto feat(git-gate+orchestrator): slice 10 — source-IP-keyed multi-tenant repo namespaceCollapsed into the single stack PR #380 (per-host orchestrator, PRD 0070). Closing this slice PR; review + merge happens on #380.
Pull request closed