Skip to content

feat: serve the /api surface from an Express app embedded in the daemon (migration step 1) - #247

Draft
oxedom wants to merge 12 commits into
mainfrom
feat/express-backend-extraction
Draft

feat: serve the /api surface from an Express app embedded in the daemon (migration step 1)#247
oxedom wants to merge 12 commits into
mainfrom
feat/express-backend-extraction

Conversation

@oxedom

@oxedom oxedom commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Step 1 of the Next.js → Express → TanStack migration (design: docs/superpowers/specs/2026-07-14-nextjs-to-express-tanstack-migration.md, plan: docs/superpowers/plans/2026-07-14-express-backend-extraction.md).

The daemon's HTTP server gains an embedded Express 5 app that serves all 148 Next.js API route handlers verbatim — no per-route rewrites:

  • Generated static-import manifest (server/http/route-manifest.ts, checked in; npm run api:manifest[:check]) mapping src/app/api/**/route.ts → Express paths ([id]:id, [...path]*path), ordered so literals beat params beat wildcards.
  • Adapter (server/http/next-route-adapter.ts): Node req → NextRequest (streamed bodies, duplex: half), handler Response → Node res (streamed, multi Set-Cookie, source cancelled on client disconnect for SSE).
  • Auth gate (server/http/auth-gate.ts): mirrors src/proxy.ts semantics (KB_PASSWORD cookie / Cabinet Cloud ES256 JWT with x-cabinet-user injection + spoof-stripping / health & login carve-outs) plus daemon-bearer-token acceptance. Cloud path covered by tests against a real local JWKS.
  • Scheduler triggers now call the daemon's own /api with the bearer token — the kb-auth cookie-replication hack (ensureAuthEnvFromDotEnv inside putJson) is gone from the trigger path.

Status — draft while remaining tasks land

  • Task 1: route manifest generator
  • Task 2: Next-route → Express adapter
  • Task 3: Express auth gate (+ cloud JWT test coverage)
  • Task 4: buildApiApp() + daemon wiring (live-verified on an isolated port/data dir)
  • Task 5: scheduler triggers via bearer
  • Task 6: CABINET_API_VIA_DAEMON cutover rewrite in src/proxy.ts (opt-in)
  • Task 7: parity check script, default-on flip, docs
  • Task 8: build/bundle/packaging verification
  • Final whole-branch review

Note for reviewers

Commit 669db68 ("feat: add managed CLIProxyAPI connector") is an unrelated feature that a concurrent session committed onto this branch mid-flight. Pending a decision: rebase it out to its own branch (requires regenerating the route manifest to drop its route) or keep it. Every task-review diff range on this branch excluded it.

Each task was implemented by a fresh subagent, reviewed (spec + quality) by an independent reviewer, and fixed/re-reviewed where findings came back (stream-cancellation leak, cloud-JWT coverage). Full unit suite green at every commit; 8 new tests files-worth of coverage added along the way.

🤖 Generated with Claude Code

oxedom and others added 12 commits July 15, 2026 00:03
…on design spec

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Node's pipe() unpipes on destination close but never destroys the
source, so an early client disconnect left long-lived handlers (e.g.
SSE routes mounted through this adapter) producing forever. Destroy
the Readable on res "close" when it hasn't ended, which propagates to
the underlying web ReadableStream's cancel().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers the cloudGateActive() -> verifyCloudToken() branch of the
Express auth gate: valid cabinet_jwt cookie injects the verified
subject, missing cookie 401s, a token signed by a key outside the
JWKS 401s, and a spoofed x-cabinet-user header is overridden by the
verified subject.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Assembles buildApiApp() from the route manifest, adapter, and auth gate
(Tasks 1-3), and wires it into the daemon: /api/* requests are delegated to
the embedded Express app before the daemon's own bearer-token gate runs,
boot now ensures the auth salt and .env-sourced auth vars are in place
before the first request, and the one next/headers route (auth/check) is
rewritten to read cookies off NextRequest so it works under both Next and
the adapter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The initWhatsAppGateway/shutdownWhatsAppGateway calls were swept into
669db68 from a concurrent working tree, but server/whatsapp/ itself was
never committed on this branch, leaving the daemon importing a module
that doesn't exist in git. The connector lands properly via
feat/whatsapp-connector (PR #245).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er token

Instead of replicating a logged-in browser's kb-auth cookie, cron-scheduler
triggers now hit the daemon's own embedded Express /api with the daemon's
Authorization: Bearer token, since the daemon hosts the entire /api surface
itself (Task 4) and its auth gate accepts that bearer token (Task 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant