Skip to content

Tracking: Go HTTP Daemon — REST #10

Closed

Description

@neversettle17-101

⚠️ Disclaimer: This issue is primarily focused on defining interfaces and service boundaries. Implementation details, data models, and internal wiring are intentionally pushed to later phases — the goal right now is to agree on the shape of the system before writing any code.


Tracking issue for the Go HTTP Daemon lane of the backend rewrite.

Goal

Replace the current Next.js API routes with a single Go binary on 127.0.0.1:3001 that serves:

  • /api/v1/* — REST endpoints (25 routes, path-parity with current TS)

The daemon is spawned as a sidecar child process by the Electron main process, supervised over loopback only. No auth, no CORS, no public URL.

Approach: phase-by-phase, contract-first

feat/go-http-daemon is the lane integration branch: every sub-PR branches off it and merges into it; the whole lane lands on main as one unit once complete.

Design doc & HTML overview: https://htmlpreview.github.io/?https://gist.githubusercontent.com/neversettle17-101/00c659adf978b80b1e2eb600d451932a/raw/ao-backend-rewrite-phase1.html


Startup sequence

1. Electron main spawns ./ao-daemon
2. Daemon binds :3001, starts listening
3. Main polls /healthz until ready
4. Renderer connects and loads UI

TODO — open question: How are agent sessions & projects loaded on daemon startup?
Warm cache from disk, lazy-load on first request, or explicit restore step?


PR sequence

Phase 1 — HTTP server skeleton + Electron integration

  • 1a — Skeleton — Go module init, config (port, env), 127.0.0.1 bind, 5-middleware stack (recoverer → request ID → logger → real IP → timeout), /healthz + /readyz, graceful shutdown (10s timeout, SIGTERM/SIGINT)

  • 1b — Route shell — All 25 routes registered under /api/v1 as 501 Not Implemented stubs; subrouter layout (/events, /mux, /api/v1, /) with correct middleware applied per surface

  • 1c — Electron integration — embed.FS static serving, Electron main process spawns + supervises daemon, health-check polling before renderer connects, dev proxy (Next.js :3000 → Go :3001), packaged build with extraResources

  • Sessions — list (with ?project, ?active, ?orchestratorOnly, ?fresh), get, spawn, kill, message, send, restore

  • Projects — list, get, reload

  • Issues, backlog, PRs — GET /issues, GET /backlog, POST /prs/:id/merge

  • Meta — GET /version, POST /update, POST /webhooks/*

  • Unclear routes remain explicit no-ops — /remap, /patches, /orchestrators, /observability, /verify, /setup-labels


Key decisions (settled)

# Decision
Framework chi — http.Handler-compatible, subrouters, standard middleware ecosystem
Port strategy Single port localhost:3001 — same-origin, no CORS, one token, one lifecycle
State push SSE for backend → frontend; WS reserved for terminal (/mux) only
Route surface 25 routes at exact path parity with current TS under /api/v1; unclear routes ship as no-ops
Port binding Bind 127.0.0.1 only; default 3001; fail fast on conflict
Shutdown signal.NotifyContext; 10s hard timeout; SSE connections just closed (EventSource auto-reconnects)
Middleware recoverer → request ID → logger → real IP → timeout (timeout on /api/v1 only)
Future split 6 disciplines adopted day-one so a port split stays a ~50-line change, not a rewrite

Open items

  • TODO — How are agent sessions & projects loaded on daemon startup? (warm cache, lazy-load, or explicit restore)
  • Q1.3 — Error envelope shape + /api/v1 versioning strategy
  • Q1.5 — OpenAPI: hand-written spec vs. code-first vs. spec-first codegen
  • Q1.6 — Static asset embed.FS cache-control headers
  • Q1.11 — Filesystem routes (/api/v1/filesystem/browse) — native machine FS design pending
  • Phase 2 blocker — Event taxonomy (which events each topic emits, exact payload shapes) must be agreed before wiring frontend dispatch
  • Q2.x — WebSocket /mux full design (mux semantics, framing, backpressure, auth on upgrade)

Architecture overview

┌─────────────── Electron App ───────────────────────┐
│  Renderer (React)  ←IPC→  Main process (Node.js)   │
│                               │ spawn + supervise   │
└───────────────────────────────┼────────────────────┘
                                │ HTTP + WS
                                ▼ 127.0.0.1:3001
                    ┌───────────────────────────┐
                    │  Go daemon                │
                    │  /            static      │
                    │  /api/v1/*    REST         │
                    │  /events      SSE          │
                    │  /mux         WS terminal  │
                    └───────────────────────────┘

Open contract questions — for integration partners

  • Persistence — LifecycleStore adapter: does the storage layer expose what the Go daemon needs for session read-model? (Tom)
  • SCM facts — does the SCM poller output match what /api/v1/issues + /api/v1/backlog need to read? (adil)
  • API shape — are the 25 route response shapes OpenAPI-friendly as-is, or do we need a normalisation pass before Phase 3? (aditi)
  • Event taxonomy — which events does each SSE topic (sessions, prs, ci) actually emit, and what are the payload shapes? (all lanes)

Owner: @neversettle17-101

Activity

  1. harshitsinghbhandari commented on May 27, 2026

    @harshitsinghbhandari
    Collaborator

    Heads-up for the API/frontend lane — new draft value in the session enums (already landed in the LCM/SM lane, PR #13).

    The canonical model now has a draft PR substate, so two enums your OpenAPI client + frontend display layer consume gained a value:

    • PRState → none | draft | open | merged | closed
    • SessionStatus (the derived display status) → now includes draft

    Behavior to mirror in the UI:

    • A draft PR surfaces as draft status (it can't be reviewed or merged, so review/approval/mergeable states don't apply to it).
    • Exception: a draft with failing CI surfaces as ci_failed, not draft — CI still dominates so the auto-fix reaction fires on drafts too.
    • adil's poller just sets PRState = draft from GitHub's isDraft.

    Action for your lane: add draft to the generated/display status enum so it deserializes + renders, with its own badge/treatment (and ci_failed takes precedence when CI is red on a draft). No shape change to Session/SessionRecord — just the enum value.

  2. neversettle17-101 commented on May 28, 2026

    @neversettle17-101
    CollaboratorAuthor

    Phase 1 sub-issue added: #19 (OpenAPI spec + validation), follows #18 (route shell).

  3. changed the title [-]Tracking: Go HTTP Daemon — Phase 1 (REST + SSE + Static)[/-] [+]Tracking: Go HTTP Daemon — REST[/+] on Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

coreCore FunctionalitydaemonHTTP daemon lane

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions