GitNexus/gitnexus/test
Shifra Williams 356032e2f6
feat(render): private-service topology, docs, and a CI check on render.yaml (#10)
* feat(render): private-service topology, docs, and a CI check on render.yaml

Rewrites the Blueprint around the topology Abhigyan called for: `serve` on a
private service, the public web service reverse-proxying /api/* to it, and the
generated token gating that edge.

`gitnexus-server` becomes a `pserv`, so it has no public URL. `gitnexus-web`
gets `GITNEXUS_UPSTREAM_URL` from `fromService … property: hostport`,
`GITNEXUS_SERVE_AUTH_TOKEN` with `generateValue: true`, and
`GITNEXUS_PROXY_TRUST_XFF: 1` for Render's load balancer. `autoDeploy: true`
becomes `autoDeployTrigger: off` so a push cannot restart an indexing server
mid-analyze.

Four variables come out:

- `GITNEXUS_PUBLIC_ORIGIN`: under the proxy the browser is same-origin with the
  web service and Origin is stripped, so it has no job — and since #2820
  `assertServeAuthForPublicOrigin` refuses to start when it is set without
  serve-native auth. The old topology no longer boots.
- `GITNEXUS_BACKEND_URL`: docker-server.mjs falls back to RENDER_EXTERNAL_URL,
  which is this service's own origin.
- `AZURE_DEVOPS_URL` / `AZURE_DEVOPS_PAT`: per Abhigyan, "a stranger can POST
  /api/analyze an Azure URL, have the server spend the operator's credential,
  then read the private repo back through the ungated /api/file and /api/grep."
  The edge token closes the stranger path, but the knob still should not ship in
  a one-click prompt. Operators who want Azure DevOps set both in the dashboard,
  on a deploy they have already secured.

README drops "with no API keys required" — it described missing authentication
as a convenience, and the deploy now generates a token — and replaces it with
the token-retrieval flow. It also corrects `plan` versus `sizeGB`: `plan` sets
RAM, `sizeGB` only matters when the disk fills. Both the README and a new
SECURITY.md section state plainly that the proxy strips Origin, so the CSRF
guard does nothing for proxied traffic and the token is the only control — not a
second layer behind the guard. SECURITY.md enumerates the routes a token holder
reaches with no origin guard, and notes that `POST /api/mcp` has no `authToken`
of its own as a pre-existing gap in `serve`.

Nothing in CI parsed render.yaml, so the couplings the file documents were
unenforced. `gitnexus/test/unit/render-blueprint.test.ts` asserts them:
mountPath equals GITNEXUS_HOME, PORT matches Dockerfile.cli's CMD, the server
stays a `pserv`, GITNEXUS_PUBLIC_ORIGIN appears in no YAML, the token is
generated, no AZURE_DEVOPS_* key, and the schema pragma is intact. Each was
confirmed to fail when its invariant is deliberately broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(readme): state what the Render deploy costs

The Deploy to Render section described the topology and the token but not
the bill. At the Blueprint's defaults it is ~$35/month: $25 for the
server's `standard` instance, $7 for the web service's `starter`, $2.50
for the 10 GB disk. Neither service scales to zero, which matters more
than the number — an idle deploy costs the same as a busy one.

Also trims the section's em dashes and drops "access token" to "token"
where the antecedent is one sentence away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: update readme

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:08:20 -07:00
..
fixtures fix(scope-resolution): fan out interface dispatch from Case 3b receivers (#2832) (#2842) 2026-08-05 15:50:08 +01:00
helpers perf(mcp): cut the analyze-only language-provider closure out of MCP server startup (#2802) (#2806) 2026-08-03 21:26:13 +01:00
integration fix(scope-resolution): fan out interface dispatch from Case 3b receivers (#2832) (#2842) 2026-08-05 15:50:08 +01:00
unit feat(render): private-service topology, docs, and a CI check on render.yaml (#10) 2026-08-05 15:08:20 -07:00
utils fix(hook): emit MCP query hint when server owns DB lock (#2396) (#2397) 2026-07-08 18:34:05 +01:00