Make the server embeddable: ServerContext for multi-instance hosting#7
Make the server embeddable: ServerContext for multi-instance hosting#7yail259 wants to merge 3 commits into
Conversation
… config
createContext() bundles the Postgres pool with the config that previously
lived in module-level env reads (apiKey, webhookSecret, publicUrl, leaseMs)
and is threaded through createApp(), reconcileTick(), dispatchRecovery(),
and listEvents(). One process can now host many isolated Tidebase instances
— one per database — which is the contract multi-tenant hosting builds on.
- src/lib.ts + package exports: consume @tidebase/server as a library
- default boot path (index.ts) and createApp()/createApp({apiKey}) are
behavior-identical for self-hosters; all 82 existing invariants green
- new embed.test.ts invariants: cross-context run invisibility, per-context
API keys, reconciler ticks scoped to their own database
- migrations dir resolves package-local as a fallback; prepack ships a copy
so the package is publishable later
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d61fa76c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const after = Number(c.req.query('after') ?? 0) | ||
| return streamSSE(c, async (stream) => { | ||
| for (const event of await listEvents(runId, after)) { | ||
| for (const event of await listEvents(runId, after, ctx.pool)) { |
There was a problem hiding this comment.
Scope live SSE listeners to the server context
When hosting multiple contexts in one process, this only scopes the initial SSE replay to ctx.pool; the live subscribe(runId, ...) immediately below still uses the module-global listener map keyed only by runId. If two tenant databases contain the same run id (for example after restoring/seeded databases or direct inserts), an event appended in one context is broadcast to the other context's /runs/:runId/events stream, violating the isolation this change is adding. Key the subscription/notification path by context (or database) as well as run id.
Useful? React with 👍 / 👎.
Reality is ahead of the claim — the suite grew with session runs and the embed contract; the number should not undersell it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`npx @tidebase/sdk signup` provisions Tidebase Cloud and writes
TIDEBASE_URL + TIDEBASE_API_KEY + TIDEBASE_WEBHOOK_SECRET into .env —
zero Docker, zero Postgres. GitHub device flow by default (agent-
friendly: relays a code, human approves once), `--email` for magic link.
Polls the gateway's start→poll endpoints (v1/signup/{github,email}/*),
degrades to self-host guidance if the cloud endpoint isn't reachable yet
(so it's honest in the pre-launch window, not a stack trace).
`init` now offers both paths — hosted via signup, or self-host — in the
injected AGENTS.md snippet and the next-steps output, closing the
zero-infra adoption loop the D12 experiment proved converts.
upsertEnv is unit-tested (idempotent merge, preserves other vars, no
prefix-clobber); end-to-end signup verified against a mock gateway.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Introduces
ServerContext— the pool plus per-instance config (apiKey,webhookSecret,publicUrl,leaseMs) that previously lived in module-level env reads — and threads it throughcreateApp(),reconcileTick(),dispatchRecovery(), andlistEvents(). Addssrc/lib.ts+ packageexportsso@tidebase/serveris consumable as a library.Why
One process can now host many isolated Tidebase instances, one per database. This is the embedding contract the hosted tier (cloud gateway) builds on, and it's an OSS story on its own: embed the Tidebase server in your own Node app.
Compatibility
index.ts) andcreateApp()/createApp({ apiKey })are behavior-identical for self-hosters; all 82 existing invariants pass unchanged.migrate/pendingMigrations/tx/listEventsgained optional pool/target params with the previous singletons as defaults.New invariants (
embed.test.ts)Publish prep (not flipped in this PR)
files+prepackship a package-local copy ofmigrations/;db.tsresolves it as a fallback."private": truestays until we decide to publish.🤖 Generated with Claude Code