Finishes the supervise data-plane writers for the shared gateway. Both
still keyed off a single SUPERVISE_BOTTLE_SLUG env; now each proposal is
attributed to the calling bottle, keeping slices 10/11's per-bottle model.
- git_http_backend: in consolidated mode stamp SUPERVISE_BOTTLE_SLUG=
<bottle_id> into the CGI env. The gitleaks-allow pre-receive hook runs as
a child of the `git http-backend` we spawn, so it inherits the stamp and
queues under the right bottle — no change to the (fragile) embedded hook.
The bottle id is the namespaced root's final component (slice 10), the
same key egress uses. Single-tenant leaves the container-stamped slug.
- supervise_server: resolve the proposal slug per request by source IP when
BOT_BOTTLE_ORCHESTRATOR_URL is set (`_attributed_config`), fail-closed —
an unattributed / unreachable source raises rather than queue under the
wrong or empty slug. `serve`/`main` build + attach the resolver and make
the env slug optional in consolidated mode. Single-tenant unchanged.
Tests: a real git push proving the slug reaches the hook's env; the
supervise attribution matrix (single-tenant slug kept, source-IP bottle
bound, unattributed + resolver-error fail closed).
Remaining supervise gap (noted): websocket DLP still self.config-only.
pyright 0 errors; pylint 9.83/10; unit suite green (1705 tests; the 13
test_sidecar_init /bin/sleep errors are pre-existing NixOS-local noise).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
The constant now covers the daemon path, the HTTP backend access-hook,
and the git http-backend CGI subprocess, so 'daemon' in the name was
too narrow. Updated the comment to list all three current uses.
Closes#255. Without timeouts, a hung upstream during the access-hook
or git http-backend CGI call (git_http_backend.py) and a stalled Gitea
API during deploy-key provisioning (contrib/gitea/deploy_key_provisioner.py)
could wedge a sidecar indefinitely. Adds GIT_HTTP_BACKEND_TIMEOUT_SECS
(30s) to both subprocess.run calls in the HTTP backend, mirroring the
existing GIT_GATE_DAEMON_TIMEOUT_SECS on the daemon path. Adds
_API_TIMEOUT_SECS (30s) and _KEYGEN_TIMEOUT_SECS (10s) to the Gitea
provisioner's urlopen and ssh-keygen calls. Tests verify the timeout
values are forwarded in all four call sites.
An empty or non-numeric Status: header from git http-backend raised
ValueError/IndexError that escaped the handler thread. Wrap the parse
in a try/except and fall back to HTTP 500 instead.
Closes#254
Previously when the access-hook returned non-zero, git-http would pipe
the hook's stderr into the 403 body sent back to the agent's git
client but never log it locally, so docker logs just showed
`"GET ... 403 -"` with no explanation. Operators had to shell into
the sidecar and re-run the hook by hand to find out why a clone was
being refused (e.g. upstream SSH unreachable, missing credentials).
Route the hook's stderr/stdout through the existing log_message
channel before sending the 403, one log line per output line so the
default request-log format stays readable. When the hook exits
non-zero with no output, log the exit code so the line is still
informative.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Before this change, int() on a non-numeric Content-Length raised an
unhandled ValueError, crashing the request handler. There was also no
upper bound on how much memory a POST body could consume.
After this change:
- Non-numeric or missing Content-Length returns HTTP 400.
- Negative Content-Length returns HTTP 400.
- Bodies declared larger than 1 MiB (_MAX_BODY_BYTES) return HTTP 413,
matching the cap already in supervise_server.py.
Closes#138