← Corpus / augment-it / issue
Refactoring for API Speed — a single-user app should boot in milliseconds, not a minute
Measured: shell mount to workspaces ready is ~543ms. The perceived minute was a stale deploy plus remotes hanging on `localhost:3XXX`.
- Path
- issues/Refactoring-for-API-Speed.md
- Authors
- Michael Staton
- Augmented with
- Claude Code on Claude Opus 4.8
- Tags
- Issue · Augment-It · Performance · Architecture · Refactor · Boot-Latency
Refactoring for API Speed
MEASURED — 2026-08-03 (the refactor premise was WRONG)
Step 0 ran (boot instrumentation shipped, shell redeployed, operator refreshed). The result overturns the diagnosis below:
- The backend is fast.
shell:mount → workspaces:ready = ~543mstotal:ws:open430ms,workspace.list:returned+110ms (2 workspaces, humain-vc active). There is no 60s retry race. The API/mesh is not the problem. - The perceived minute had two real causes, neither architectural:
- A stale shell deploy. The live build was from 2026-07-28; a fresh rebuild + redeploy alone made it “way faster” per the operator.
- A dozen undeployed federation remotes falling back to
localhost:3XXX. The shell eagerly loadshttp://localhost:3002…3015/remoteEntry.jsfor remotes that aren’t deployed (record-collector, PTM, response-reviewer, pack-runner, the resolvers, …); those fetches fail — and on some networks hang on a TCP connect timeout, which is where a minute can come from. The deployed remotes (chat 3006, org-workbench 3014, search-and-add 3016, corpora-curator 3017) load fine and were never in the failure list.
So the mesh-refactor design space below is RETRACTED as the cause. The
remaining, much smaller work is a build/federation-config cleanup: prune the
prod federation manifest to deployed remotes only, and/or lazy-load remotes
(fetch remoteEntry.js on demand when a Flow opens, not eagerly at boot) so no
boot ever waits on a doomed localhost fetch. Tracked as its own follow-up.
The design space below is kept for the record — it is what we would have wasted days on without Step 0.
Why Care? (original hypothesis — superseded by the measurement above)
On refresh, the workspaces the operator is logged into take 60s+ to appear. Interactions wait on microfrontends and microservices responding to — and “acknowledging” — each other. There is exactly one user. This should be milliseconds. The cost is not the work; it’s the coordination between too many independently-deployed parts.
The topology being paid for (with one user)
- 7+ separate Railway services:
shell(augment.didi.sh),corpora-curator,chat(+ other federation remotes: org-workbench, search-and-add, search-results…),workspace-service(WS gateway),record-surrealdb-resolver,content-ingest,prompt-runner. id.didi.sh— a separate identity service (different repo/runtime) for JWKS verification +/api/mememberships.- SurrealDB Cloud, aws-use1 (
wss://…surreal.cloud) — every query is a network round-trip to AWS us-east. - Each microfrontend opens its OWN WebSocket to workspace-service and does its own auth handshake (shell App.svelte notes “each federation remote also connects, those instances are separate”).
- Services talk service→service over NATS request/reply.
Every one of those boundaries adds fixed latency: TLS, cold start, DB round trip, NATS hop, and — worst — retry backoff.
Root causes (code-grounded, ordered by leverage)
- Retry-as-readiness is masking a boot race — the acute one. The recent
commit: “retry
workspace.active.requestedup to ~60s before crashing.”shell/src/App.svelte:375also backs off 8× onloadWorkspaces. That ~60s is the shell waiting for a dependency to become ready (cold service, DB connection warming, NATS responder not yet registered), not doing work. Suspected to be most or all of the observed minute. - Cold starts, in series. One boot call traverses shell → workspace-service → NATS → resolver → SurrealDB Cloud. A cold service anywhere on that path wakes on the request; multiple cold hops compound.
- Sequential remote-DB round trips on boot. Memberships, workspace list, active workspace, rows — each a separate cross-country round trip to aws-use1 if issued back-to-back.
- Cross-service auth on every connect. Each WS upgrade verifies the JWT and
fetches
/api/mefrom id.didi.sh (cached only ~60s,didi.ts:96) — and every remote repeats it. - Per-remote fan-out. Boot cost multiplies by the number of microfrontends, each with its own transport + auth + initial capability calls.
- NATS request/reply between co-located services is pure serialization overhead when there’s no concurrency to justify it.
The thesis
The system is a distributed, multi-tenant-shaped mesh serving one user. The fix is to right-size the architecture to actual scale — reserve the mesh for when concurrency demands it, and until then collapse boundaries so calls are in-process and boot is deterministic.
First default step — frontend boot timing (do this BEFORE anything else)
Measure, don’t guess. The wall-clock the operator feels lives in the
browser, and the shell isn’t timing itself. Add a dozen performance.now()
stamps at each boot milestone and dump elapsed ms to the console:
- WS connect start
- WS open
- auth verified (session frame accepted)
workspace.listsent → returnedworkspace.activesent → returned- each remote connected
No library, no platform. This turns “it’s slow” into “workspace.active took
58s and everything else was 300ms” — and decides whether this is a one-day
readiness fix or a real refactor. It is the default first move; every lever
below is gated on what it shows. Pair it with the browser Network waterfall
(free) and, if cross-service correlation is needed, a request_id threaded
shell → workspace-service → resolver (deferred until tier-1 proves insufficient).
For the server side of the same picture, view the service logs with
gonzo — a k9s-style real-time
log-analysis TUI (brew install gonzo, or nix run github:control-theory/gonzo
from the monorepo dev shell). Pipe the cross-service handshake into one pane
while the browser prints its boot timings:
docker compose logs -f | gonzo # local backend stack
railway logs --service workspace-service | gonzo # a deployed service
Gonzo’s OTLP receiver (--otlp-enabled, gRPC 4317 / HTTP 4318) is the bridge if
tier-1 timings + logs prove insufficient and we add real tracing. A full
observability stack (OTel pipelines/Prometheus/dashboards) is explicitly not
needed at one user — same right-sizing thesis. Boot instrumentation lives near
[[No-User-Visibility-Into-State-Needs-A-State-Inspector]] / the live-not-live
indicator work.
Design space (gated on what Step 0 shows), by leverage:
- Kill retry-as-readiness. Make services signal ready and the shell’s first call succeed deterministically (or a fast, bounded wait) — turns 60s into ms.
- Keep the request-path services warm (no scale-to-zero; a warm SurrealDB connection pool). Cheap, ops-level, big.
- One bootstrap call. A single
workspace.bootstrapcapability returning memberships + workspaces + active + initial state, instead of N sequential DB/NATS round trips. - Embed memberships/claims in the JWT so auth needs zero cross-service
/api/mefetch. - Share one transport across remotes (single WS, single auth verification) instead of per-remote handshakes.
- Collapse services. For single-user / small-team scale, fold resolver + content-ingest + prompt-runner (and possibly the WS gateway) into fewer processes so NATS request/reply becomes function calls. Reserve split services for a real concurrency/scale trigger.
- Optimistic boot. Render cached workspaces from localStorage instantly, revalidate in the background — perceived ms regardless of revalidation cost.
Explicitly NOT this issue
- Not a call to abandon the distributed design permanently — it’s a call to match it to current scale and make the boundaries cheap or absent until scale arrives.
- Not the auth-persistence / cookie-partitioning bug ([[Workspace-And-Corpora-Connection-Slow-To-Hanging-And-Auth-Wont-Persist]]) — related surface, different root cause; cross-linked, not merged.
See also
- [[Workspace-And-Corpora-Connection-Slow-To-Hanging-And-Auth-Wont-Persist]] — the production connection/auth issue on the same surface.
- [[Live-Not-Live-Indicator-Tooling-And-Cross-Service-Error-Surfacing]] · [[No-User-Visibility-Into-State-Needs-A-State-Inspector]] — where boot instrumentation would live.