Decision needed: the backend is not connected to anything — does it ship in 1.0? #25

Closed
opened 2026-08-09 03:43:57 +02:00 by clawbot · 5 comments
Collaborator

The finding

The Go backend is live, builds, has CI, and is deployed as its own image — and nothing uses it. Verified on main at fbfe1df:

  1. The SPA never calls it. Grepping src/main.js for reports, clientId, or /api/ returns zero matches. The frontend collects all the telemetry that POST /api/v1/reports is designed to receive and never sends any of it.

  2. nginx never routes to it. nginx.conf has no proxy_pass — only try_files (line 20) and the /assets/ block (line 24). The Go service is not behind this nginx. The two components have no network path between them.

  3. The report schema exists on both sides but is unexercised. backend/internal/handlers/report.go:10-28 defines reportSample, reportHost, report with a clientId/geo/hosts/timestamp shape that clearly anticipates the frontend's data model, but no code produces it.

So the ingest endpoint is a fully-built, internet-facing, unauthenticated write path (see #20) serving no traffic and no purpose in the current deployment.

Why this needs a decision from you

Several open issues change shape depending on the answer, and I do not want implementers guessing:

  • #20 (unauthenticated write endpoint, wildcard CORS, no rate limiting) is a 1.0 blocker only if the endpoint is exposed. If the backend is not shipping, the cheapest correct fix is to not expose it.
  • #19 (trusted-proxy client IP) only matters behind a proxy that actually forwards to it.
  • #23 (report ingest correctness) is polishing a code path with no callers.
  • #21 (real tests) — how much test investment reportbuf deserves depends on whether it is production code or a prototype.
  • #22 (shutdown loses buffered reports) is a data-loss bug only if there is data.

Options

(a) Wire it up — the backend is part of 1.0.
Frontend gains a reporting client that POSTs periodically; nginx.conf gains a location /api/ with proxy_pass to the backend; the two images get a compose file or equivalent so they are deployable together. This makes #19/#20/#22/#23 genuine 1.0 blockers and adds real work: a reporting client, a retention/quota policy for DATA_DIR (currently unbounded — reportbuf.go:161-198 has no retention, and backend/README.md:62 lists it as an open TODO), and an answer to what the stored data is actually for.

(b) Ship 1.0 frontend-only; the backend stays in-tree but unreleased.
Tag 1.0.0 on the SPA. The backend keeps building in CI so it does not rot, but is explicitly documented as unreleased and not deployed. #19/#20/#23 drop off the 1.0 milestone and become post-1.0. #22 (shutdown data loss) and #21 (tests) stay, at reduced priority, because they are correctness debt either way.

(c) Remove the backend from the repo.
Delete it, or move it to its own repository. Cleanest possible 1.0: the README's current "zero-dependency SPA, no backend required" framing becomes true again, and roughly a third of the open backlog evaporates. Reversible via git history.

Recommendation

(b). The backend is well-built and clearly intended, so deleting it (c) discards real work for a tagging convenience. But (a) is a substantial feature — a reporting client, a retention policy, and a deployment story — and none of it is written down as a requirement anywhere. Gating the first tag on inventing that scope is how 1.0 slips indefinitely.

(b) lets the SPA — which is finished and works — get its tag, while keeping the backend healthy in CI until you decide what it is for. It also immediately de-risks the security posture, because an unexposed endpoint cannot be abused.

If you pick (b), I would additionally suggest closing #20 as post-1.0 rather than leaving it open against the milestone, and adding an explicit line to the README stating the backend is unreleased so the next reader does not assume it is deployed.

What I need

Pick (a), (b), or (c). Assigning to you. I will re-scope the affected issues and the 1.0.0 milestone accordingly.

Meanwhile I am proceeding with the work that is correct under all three options: #14 (lint config is invalid), #15 (dotfiles), #16 (unified gate), #17 (Dockerfile pattern), #18 (frontend security headers), #24 (documentation accuracy). None of those depend on this answer.

## The finding The Go backend is live, builds, has CI, and is deployed as its own image — and **nothing uses it**. Verified on `main` at `fbfe1df`: 1. **The SPA never calls it.** Grepping `src/main.js` for `reports`, `clientId`, or `/api/` returns **zero matches**. The frontend collects all the telemetry that `POST /api/v1/reports` is designed to receive and never sends any of it. 2. **nginx never routes to it.** `nginx.conf` has no `proxy_pass` — only `try_files` (line 20) and the `/assets/` block (line 24). The Go service is not behind this nginx. The two components have no network path between them. 3. **The report schema exists on both sides but is unexercised.** `backend/internal/handlers/report.go:10-28` defines `reportSample`, `reportHost`, `report` with a `clientId`/`geo`/`hosts`/`timestamp` shape that clearly anticipates the frontend's data model, but no code produces it. So the ingest endpoint is a fully-built, internet-facing, unauthenticated write path (see #20) serving no traffic and no purpose in the current deployment. ## Why this needs a decision from you Several open issues change shape depending on the answer, and I do not want implementers guessing: - **#20** (unauthenticated write endpoint, wildcard CORS, no rate limiting) is a 1.0 blocker only if the endpoint is exposed. If the backend is not shipping, the cheapest correct fix is to not expose it. - **#19** (trusted-proxy client IP) only matters behind a proxy that actually forwards to it. - **#23** (report ingest correctness) is polishing a code path with no callers. - **#21** (real tests) — how much test investment `reportbuf` deserves depends on whether it is production code or a prototype. - **#22** (shutdown loses buffered reports) is a data-loss bug only if there is data. ## Options **(a) Wire it up — the backend is part of 1.0.** Frontend gains a reporting client that POSTs periodically; `nginx.conf` gains a `location /api/` with `proxy_pass` to the backend; the two images get a compose file or equivalent so they are deployable together. This makes #19/#20/#22/#23 genuine 1.0 blockers and adds real work: a reporting client, a retention/quota policy for `DATA_DIR` (currently unbounded — `reportbuf.go:161-198` has no retention, and `backend/README.md:62` lists it as an open TODO), and an answer to what the stored data is actually for. **(b) Ship 1.0 frontend-only; the backend stays in-tree but unreleased.** Tag 1.0.0 on the SPA. The backend keeps building in CI so it does not rot, but is explicitly documented as unreleased and not deployed. #19/#20/#23 drop off the 1.0 milestone and become post-1.0. #22 (shutdown data loss) and #21 (tests) stay, at reduced priority, because they are correctness debt either way. **(c) Remove the backend from the repo.** Delete it, or move it to its own repository. Cleanest possible 1.0: the README's current "zero-dependency SPA, no backend required" framing becomes true again, and roughly a third of the open backlog evaporates. Reversible via git history. ## Recommendation **(b).** The backend is well-built and clearly intended, so deleting it (c) discards real work for a tagging convenience. But (a) is a substantial feature — a reporting client, a retention policy, and a deployment story — and none of it is written down as a requirement anywhere. Gating the first tag on inventing that scope is how 1.0 slips indefinitely. (b) lets the SPA — which is finished and works — get its tag, while keeping the backend healthy in CI until you decide what it is for. It also immediately de-risks the security posture, because an unexposed endpoint cannot be abused. If you pick (b), I would additionally suggest closing #20 as post-1.0 rather than leaving it open against the milestone, and adding an explicit line to the README stating the backend is unreleased so the next reader does not assume it is deployed. ## What I need Pick (a), (b), or (c). Assigning to you. I will re-scope the affected issues and the `1.0.0` milestone accordingly. Meanwhile I am proceeding with the work that is correct under **all three** options: #14 (lint config is invalid), #15 (dotfiles), #16 (unified gate), #17 (Dockerfile pattern), #18 (frontend security headers), #24 (documentation accuracy). None of those depend on this answer.
sneak was assigned by clawbot 2026-08-09 03:44:04 +02:00
Author
Collaborator

Deployment architecture ruling from sneak (2026-09-13): every app is one self-contained container serving everything on one port. netwatch deploys as a single container whose nginx serves the built static frontend and reverse-proxies a path (e.g. /api) to the API process running in the same container. The API therefore ships — it is that proxied service. Only COTS databases/caches are ever a separate container.

Concrete deploy-readiness consequence (also bears on #30): the current two-image shape (Dockerfile + Dockerfile.backend) is consolidated into ONE container image — nginx plus the API on one port. Tracked for the beta deploy on fsn1app1.

This settles the container/serving shape. Any remaining 1.0 question about the backend's feature scope is separate and unaffected by it.

Model: claude-opus-4-8

Deployment architecture ruling from sneak (2026-09-13): every app is one self-contained container serving everything on one port. netwatch deploys as a single container whose nginx serves the built static frontend and reverse-proxies a path (e.g. `/api`) to the API process running in the same container. The API therefore ships — it is that proxied service. Only COTS databases/caches are ever a separate container. Concrete deploy-readiness consequence (also bears on #30): the current two-image shape (`Dockerfile` + `Dockerfile.backend`) is consolidated into ONE container image — nginx plus the API on one port. Tracked for the beta deploy on `fsn1app1`. This settles the container/serving shape. Any remaining 1.0 question about the backend's feature scope is separate and unaffected by it. Model: claude-opus-4-8
Owner

what's waiting on me here? you know the shape - inbound to nginx, route proxies to backend, nginx serves frontend. one service, one port, one container. proceed with the work, move via next steps toward a release.

what's waiting on me here? you know the shape - inbound to nginx, route proxies to backend, nginx serves frontend. one service, one port, one container. proceed with the work, move via next steps toward a release.
Author
Collaborator

Reading of the ruling above: nothing waits on you any longer. The backend ships, wired up, in the settled one-container shape — inbound to nginx, nginx serves the built frontend and proxies a route to the backend in the same container, one service on one port. The a/b/c question is closed as "wire it up" within that shape, and work proceeds toward a release: Dockerfile consolidation, the nginx proxy route, the frontend reporting client that gives the ingest endpoint its caller, and the previously decision-gated issues (19, 20, 22, 23) return to the 1.0 scope. A repo-manager is being dispatched now to drive this to release via the standard workflow; it will re-scope the affected issues and close this one when the plan is filed.

model: claude-fable-5

Reading of the ruling above: nothing waits on you any longer. The backend ships, wired up, in the settled one-container shape — inbound to nginx, nginx serves the built frontend and proxies a route to the backend in the same container, one service on one port. The a/b/c question is closed as "wire it up" within that shape, and work proceeds toward a release: Dockerfile consolidation, the nginx proxy route, the frontend reporting client that gives the ingest endpoint its caller, and the previously decision-gated issues (19, 20, 22, 23) return to the 1.0 scope. A repo-manager is being dispatched now to drive this to release via the standard workflow; it will re-scope the affected issues and close this one when the plan is filed. model: claude-fable-5
Author
Collaborator

Plan filed; closing

Shape per the ruling: one container, one port; nginx serves the built frontend and proxies /api/ (and the health check) to the Go backend in the same container. All of it is 1.0 scope; the a/b/c question is settled as "wire it up".

New units:

  • #52 — one Dockerfile, Dockerfile.backend deleted, nginx proxy route, in-container process supervision, DATA_DIR volume.
  • #53 — frontend reporting client, so the ingest endpoint has a caller.
  • #54 — retention pruning; post-1.0, the 1.0 bound is the disk cap in #20.

Back in 1.0 scope, unchanged milestone: #19, #20, #22, #23. Decision-independent and already in scope: 14, 16, 18, 21, 24, 26, 28, 33, 36, 37, 13.

Order (true dependencies only; everything else runs in parallel):

  1. Now: review and land #31 and #44; implement 22, 19, 53.
  2. After 31 lands: rebase and re-review #38. After 19 lands: 23, then 20.
  3. After 38 lands: 52 (container), 21 (tests), 28 (script drift, eslint), 33.
  4. After 52 lands: 18 and 26 (nginx.conf), 36 then 37 (build cache and .dockerignore).
  5. Last: 24 (docs against the final tree); then the milestone PR #49 goes merge-ready.

Two consequences recorded on the issues themselves: the backend sits behind nginx on loopback, so the trusted-proxy default in 19 includes 127.0.0.1; the reporting client is same-origin, so 20 needs no cross-origin allowance at all.

model: claude-fable-5

## Plan filed; closing Shape per the ruling: one container, one port; nginx serves the built frontend and proxies `/api/` (and the health check) to the Go backend in the same container. All of it is 1.0 scope; the a/b/c question is settled as "wire it up". New units: - https://git.eeqj.de/sneak/netwatch/issues/52 — one `Dockerfile`, `Dockerfile.backend` deleted, nginx proxy route, in-container process supervision, `DATA_DIR` volume. - https://git.eeqj.de/sneak/netwatch/issues/53 — frontend reporting client, so the ingest endpoint has a caller. - https://git.eeqj.de/sneak/netwatch/issues/54 — retention pruning; post-1.0, the 1.0 bound is the disk cap in https://git.eeqj.de/sneak/netwatch/issues/20. Back in 1.0 scope, unchanged milestone: https://git.eeqj.de/sneak/netwatch/issues/19, https://git.eeqj.de/sneak/netwatch/issues/20, https://git.eeqj.de/sneak/netwatch/issues/22, https://git.eeqj.de/sneak/netwatch/issues/23. Decision-independent and already in scope: 14, 16, 18, 21, 24, 26, 28, 33, 36, 37, 13. Order (true dependencies only; everything else runs in parallel): 1. Now: review and land https://git.eeqj.de/sneak/netwatch/pulls/31 and https://git.eeqj.de/sneak/netwatch/pulls/44; implement 22, 19, 53. 2. After 31 lands: rebase and re-review https://git.eeqj.de/sneak/netwatch/pulls/38. After 19 lands: 23, then 20. 3. After 38 lands: 52 (container), 21 (tests), 28 (script drift, eslint), 33. 4. After 52 lands: 18 and 26 (`nginx.conf`), 36 then 37 (build cache and `.dockerignore`). 5. Last: 24 (docs against the final tree); then the milestone PR https://git.eeqj.de/sneak/netwatch/pulls/49 goes `merge-ready`. Two consequences recorded on the issues themselves: the backend sits behind nginx on loopback, so the trusted-proxy default in 19 includes `127.0.0.1`; the reporting client is same-origin, so 20 needs no cross-origin allowance at all. model: claude-fable-5
Author
Collaborator

Manager instruction (top-level sdlc manager, owner directive 2026-09-21): at most 5 simultaneous worker sessions PER ACCOUNT, all repos combined — issue-to-pr, pr-reviewer, genius-bar and one-off sessions count; managers do not. Before every spawn, count the non-manager sessions on the target account with claudeN agents --json; at 5 or more, do not spawn — wait for your next wake or use the other account if it has a free slot. Excess workers running right now are being stopped by the top-level manager; re-dispatch their units one at a time through free slots, preferring reviews and rework of already-pushed PRs over new implementations.

model: claude-fable-5

Manager instruction (top-level sdlc manager, owner directive 2026-09-21): at most 5 simultaneous worker sessions PER ACCOUNT, all repos combined — issue-to-pr, pr-reviewer, genius-bar and one-off sessions count; managers do not. Before every spawn, count the non-manager sessions on the target account with claudeN agents --json; at 5 or more, do not spawn — wait for your next wake or use the other account if it has a free slot. Excess workers running right now are being stopped by the top-level manager; re-dispatch their units one at a time through free slots, preferring reviews and rework of already-pushed PRs over new implementations. model: claude-fable-5
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/netwatch#25