One-click deploy (Render + Fly.io) — and the four first-run fixes it surfaced - #471
Open
SecurID wants to merge 5 commits into
Open
One-click deploy (Render + Fly.io) — and the four first-run fixes it surfaced#471SecurID wants to merge 5 commits into
SecurID wants to merge 5 commits into
Conversation
render.yaml backs a 'Deploy to Render' button: middleware + admin UI from the published GHCR images, managed Postgres 17 (pgvector), a persistent /data disk, and a generated VAULT_KEY — the /setup wizard collects the LLM key at runtime, so a deploy needs zero pasted secrets. MIDDLEWARE_URL reaches the web-ui via fromService/envVarKey RENDER_EXTERNAL_URL. Fly has no blueprint-style button, so fly/deploy.sh is the one-command equivalent: three apps (pgvector/pgvector:pg17 private-only, middleware with /data volume, web-ui), volumes, and generated secrets. Fly's own Postgres offerings don't fit (postgres-flex ships without pgvector, Managed Postgres gates the vector extension behind a dashboard toggle), hence the same pgvector image the compose stack uses. Smoke-tested end to end on a live Fly org. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…replace baked rewrites Next evaluates rewrites() at build time and freezes the destinations into routes-manifest.json. The published web-ui image was built with ARG MIDDLEWARE_URL=http://middleware:8080, so every browser call through /bot-api proxied to a hostname that only exists on the compose network — on Fly/Render/any other host the proxy died with ENOTFOUND and every operator page showed 'GET … failed: 500'. Runtime MIDDLEWARE_URL was silently ignored (only server-side fetches honored it, which is why page shells still rendered). /bot-api/* and /p/* are now catch-all route handlers backed by app/_lib/middlewareProxy.ts, resolving MIDDLEWARE_URL per request: streaming both directions (SSE, plugin-ZIP uploads), multiple Set-Cookie preserved, redirects passed through, hop-by-hop headers stripped. One image now runs anywhere; the compose default still works because compose sets the same value as runtime env. Consequences swept: Dockerfile build ARG removed (with a warning against reintroducing it), CI workflows no longer pass the inert build-arg, the compose build overlay drops it, and desktop-apps.yml stops pretending to bake the kernel URL — the desktop supervisor already injects it at spawn (this also fixes the latent wrong-port proxy when the kernel doesn't get its preferred port). Verified: 6 regression tests (core assertion: same handler, changed env, changed target), plus a live Fly deployment where /bot-api/v1/auth/providers went 500 → 200 and login started working. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…olders; unbreak next dev Three first-run findings from smoke-testing the one-click deploy: 1. Readiness banner. A fresh install has no LLM key, so the orchestrator never publishes chatAgent@1/orchestratorRegistry@1 and every operator surface answers 503 multi_orchestrator_unavailable — the UI showed raw 'GET … failed: 503' strings with no hint at the cause. RuntimeReadinessBanner (mounted next to SessionWatcher) probes one operator route, and on the structured 503 shows a card naming the cause with a link to /admin/settings. Re-probes on focus + heartbeat while visible, so it clears itself the moment the key is saved. 2. t.rich placeholder syntax (regression from #447). 17 placeholders across 12 message keys used ICU argument syntax ({toolName}) while the component passes chunk functions — next-intl splices the raw function into the render output and React throws ('Functions cannot be passed to Client Components'), hard-crashing the routines page, login no-providers state, onboarding modal, builder preview, and four admin pages whenever those paths render. The locale-parity check couldn't catch it because both locales were consistently wrong. All converted to tag syntax (<toolName></toolName>) in en+de; audited to zero via a t.rich/message cross-check sweep. 3. next dev was 500ing on every page: Tailwind v4 scans comments as class candidates, and two test-file comments containing a literal bracket-class with var(...) compiled into invalid CSS (dev-only; prod builds tolerate it). Reworded the comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SecurID
marked this pull request as ready for review
July 9, 2026 09:40
Contributor
Author
|
Important Extension for future PR: routines is the only feature that can't hot-enable on key save (it registers at boot, hence a restart is needed), and the middleware log even hints at it. Making it late-mount like chat does would remove the last manual step from first-run |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes omadia one-click deployable to a visitor's own cloud account, and fixes everything the live smoke test surfaced along the way. The full flow was verified end to end on a real Fly.io org: deploy → login → readiness banner → key in → agents/channels/skills live → restart → routines live.
1. Deploy paths (new)
render.yaml— backs a Deploy to Render button: middleware + admin UI from the published GHCR images, managed Postgres 17 with pgvector, persistent/datadisk,VAULT_KEYgenerated by Render (generateValue), regionfrankfurt. Zero pasted secrets: the/setupwizard collects the LLM key at runtime. The web-ui reaches the middleware viafromService/envVarKey: RENDER_EXTERNAL_URL.fly/deploy.sh+ three machine configs — Fly has no blueprint-style button, so the equivalent is one command. Provisions three apps: a private-onlypgvector/pgvector:pg17(Fly's unmanaged postgres-flex ships without pgvector; Managed Postgres gatesvectorbehind a dashboard toggle at a much higher price), middleware with a/datavolume, and the web-ui — plus generated secrets. ~$10/month.2.
MIDDLEWARE_URLwas frozen at image build time (fix)Next evaluates
rewrites()at build time; the published web-ui image hadhttp://middleware:8080baked into its routes manifest, so every browser call through/bot-apifailed withENOTFOUNDon any non-compose host while runtimeMIDDLEWARE_URLwas silently ignored./bot-api/*and/p/*are now catch-all route handlers (app/_lib/middlewareProxy.ts) resolving the env per request — streaming both ways (SSE, ZIP uploads), multi-Set-Cookiepreserved, redirects passed through. Build ARG removed from the Dockerfile, CI workflows, and the compose build overlay;desktop-apps.ymlstops pretending to bake the kernel URL (the supervisor injects it at spawn — this also fixes the latent wrong-port proxy there). 6 regression tests.3. Fresh installs now say what's missing (new)
Without an LLM key the orchestrator publishes nothing and every operator surface 503s — the UI showed raw
GET … failed: 503strings.RuntimeReadinessBannerprobes one operator route and, on the structuredmulti_orchestrator_unavailable, shows a card naming the cause with a link to/admin/settings; it clears itself once the key lands. EN/DE, 6 tests.4. 17 broken
t.richplaceholders (fix, regression from #447)Messages used ICU argument syntax (
{toolName}) where components pass chunk functions — next-intl splices the raw function into the output and React hard-crashes the page (RSC: "Functions cannot be passed to Client Components"). Affected: routines (crashed on a live instance), login no-providers, onboarding modal, builder preview, and four admin pages. All 12 keys converted to tag syntax in both locales; a sweep audit now finds zero. The locale-parity check couldn't catch this class (both locales consistently wrong) — a follow-up issue could add an argument-vs-tag cross-check against component usage.5.
next devwas broken on main (fix)Tailwind v4 scans comments as class candidates; two test-file comments containing a literal bracket-class with
var(...)compiled into invalid CSS and 500'd every page in dev (prod unaffected). Comments reworded.Verification
tsc --noEmit, ESLint,i18n:checkcleannext build✓, standalone server with runtimeMIDDLEWARE_URL✓ (proxy + routines verified against a stub middleware), Docker image ✓ (built 3× on Fly's remote builder, live-verified), compose ✓ (sets the env at runtime), desktop ✓ (supervisor sets it at runtime)Notes for review
:latestGHCR images — they only work for real visitors once a release containing the proxy fix is published. The omadia.ai/docsdeploy section (separate PR inomadia-homepage) should merge after that release.t.richmismatch class.🤖 Generated with Claude Code