The oll.in Next.js app is a thin BFF — it holds no auth/Stripe/model logic, it calls the frozen platform spines over HTTP. The web/src/lib/backend.ts module reads four backend base-URLs for v1: Core (identity + billing), oll-write (the dossier verbs), oll-memory (the grounding/citation), and foto (the photo add-on, v2.1). I probed each over TLS on 2026-07-08 — this is a real health check, not an assumption:
| Spine | Prod URL (BFF env) | Role for oll.in | Health probe (Jul 8) |
|---|---|---|---|
| Core | https://core.oll.am | magic-link auth · /me · billing checkout + webhook | ✅ 200 · {"status":"ok"} |
| oll-write | https://write.oll.am | cover-letter · tailor-cv · interview-prep verbs | ✅ 200 · {"service":"write"} |
| oll-memory | https://memory.oll.am | corpus ingest + cited retrieval (the grounding) | ✅ 200 |
| oll-model | https://model.oll.am | Groq gateway — oll-write calls it, oll.in does not | ✅ 200 (at model.oll.am, not oll-model.oll.am) |
| foto | https://foto.oll.am | guest headshot add-on (CHF 29) — v2.1 only | ⚠️ no response — not deployed yet |
| oll-scout / Radar | https://scout.oll.am | Career Radar — v2, off the first-franc path | ⚠️ no response — v2, expected |
/api/health means the container is up and reachable; it does not prove, from the outside, that Core has its live Stripe key + webhook secret, that the CHF 9 oll_pro/one_time price exists live, or that oll-write's Groq key is set. Those are confirmed only by the smoke test below (§3). Treat the ✅ as "reachable," not "ready to charge."This is the fork to settle before anything else, because oll.in has no git remote today (verified: git remote -v is empty; local branches are master + feat/v2-guest-photo). Both realistic hosts deploy from git, so step 0 for either option is: create a private GitHub repo (e.g. bytesbysamu/oll-in, mirroring the ollwrite pattern) and push.
@ → the VPS). Its BFF can reach the spines either over the public HTTPS URLs (already TLS, already proven healthy) or, if joined to the ollam Docker network, over internal DNS with no public hop. Cost: it's a Node/Next.js runtime, not the static-nginx pattern the site uses, so it needs its own Dockerfile (a standard Next.js standalone build). Everything Sam already knows how to operate.0. Create bytesbysamu/oll-in (private) and push. 1. Create the app (Coolify app or Vercel project) from that repo, branch master. 2. Set the server-side env — these are the exact vars the BFF reads (from web/src/lib/backend.ts / session.ts), all server-only, never NEXT_PUBLIC_:
# oll.in production env — the four v1 backends (all verified healthy Jul 8) OLL_CORE_BASE=https://core.oll.am OLL_WRITE_BASE=https://write.oll.am OLL_MEMORY_BASE=https://memory.oll.am OLL_FOTO_BASE=https://foto.oll.am # photo add-on; foto not deployed yet OLL_MEMORY_SERVICE_TOKEN=<must match memory.oll.am's token> # leave dev-session OFF in prod — it's hard-disabled unless you opt in: NODE_ENV=production # disables ALLOW_DEV_SESSION path # v2 / distribution — not needed for the CHF 9 path: # RADAR_BASE=https://scout.oll.am POSTIZ_BASE=https://postiz.oll.am/api
Confirm the app boots and /api/health-style pages render. The dossier UI lives at /app; the marketing landing is /.
The BFF's /api/checkout relays the session JWT to Core's POST /api/billing/create-checkout-session with {product:"oll_pro", plan:"one_time"} — a pay-once (Stripe payment mode) purchase, not a subscription. So the entire money path is Core-side config, not an oll.in code change. Finish it:
a. In Stripe, create the live webhook endpoint https://core.oll.am/api/billing/webhook (event checkout.session.completed at minimum) → copy its signing secret. b. Set STRIPE_WEBHOOK_SECRET=whsec_… on the Core Coolify app. This is the gate from CLAUDE.md: without it, a payment succeeds but the plan never flips to pro. c. Swap Core's STRIPE_SECRET_KEY from sk_test_… to the rotated sk_live_… and confirm the live CHF 9 price is set (Core reads STRIPE_PRO_PRICE_ID, or a per-product STRIPE_PRICE_OLL_PRO_ONE_TIME if defined — verify which name Core resolves for product=oll_pro). d. Redeploy Core.
The /photo funnel is a no-login guest flow: BFF /api/photo/checkout → foto's POST /api/foto/checkout {email, tier:"starter", style, input_photo_urls} → foto asks Core's guest-checkout endpoint to open Stripe → returns the hosted URL. To turn it on: a. merge PR #84 (feat/core-guest-checkout-reland — the Core guest-checkout endpoint pair; currently OPEN, needs your merge-gate go + green CI). b. Deploy foto (foto.oll.am is not up yet) with FOTO_PUBLIC_BASE_URL=https://foto.oll.am and a real REPLICATE_API_TOKEN (or leave FOTO_PROVIDER=mock to test the money path keyless first). c. Set the headshot Stripe price on Core (a STRIPE_PRICE_{HEADSHOT}_{…} id — confirm the exact tier/price: the funnel sends tier="starter", the brief says CHF 29, the env-matrix example shows a CHF 49 tier — reconcile before charging).
1. Login: on /app, enter your email → magic-link email arrives → paste the link → a session cookie is set → the header shows you signed in (/me = free plan). 2. Compose a real dossier: paste a CV + a job posting → the cover-letter / tailor-cv / interview-prep verbs return grounded text (oll-write live), and the grounded-receipt cites real corpus chunks (oll-memory live). 3. Pay — test first: click take-it → you land on a cs_test_… Stripe Checkout → pay with card 4242 4242 4242 4242 → the webhook flips your plan to pro → /me now reads pro. 4. Pay — live: repeat with live keys — you should get a cs_live_… session → pay a real card → plan flips → that's the first franc. 5. Download: the finished dossier PDF downloads via /api/download.
| Key | Lives on | Action | Blocks first franc? |
|---|---|---|---|
| STRIPE_WEBHOOK_SECRET | Core | create the live core.oll.am/api/billing/webhook endpoint, set whsec_… | YES — the blocker |
| STRIPE_SECRET_KEY | Core | rotate sk_test_ → sk_live_ (treat the old test key as burned) | yes (for a real charge) |
| STRIPE_PRO_PRICE_ID | Core | confirm the live CHF 9 pay-once price for product=oll_pro | yes |
| AUTH_JWT_SECRET | Core | already set (Core is live + SSO works) — no action | no |
| OLL_MEMORY_SERVICE_TOKEN | oll.in | set to memory.oll.am's token (X-Service-Token) | grounding fails without it |
| GROQ_API_KEY | oll-model | verify it's set (write.oll.am + model.oll.am are up; key is invisible from outside) | verses fail without it |
| STRIPE_PRICE_{HEADSHOT} | Core | the CHF 29 headshot price — add-on only; confirm tier/amount | no (photo add-on) |
| REPLICATE_API_TOKEN | foto | real headshots — add-on only (mock runs keyless) | no (photo add-on) |
| ALLOW_DEV_SESSION | oll.in | do NOT set in prod — the no-auth pro-JWT mint is hard-off unless this = 1 | security — keep off |
bytesbysamu/oll-in GitHub remote + push.STRIPE_SECRET_KEY to sk_live_..in (or .ch) DNS at the app..env.example → prod mapping kept in sync with the BFF code.oll_pro/one_time price exists live, or that oll-write's Groq key is present. The smoke test is the only proof.model.oll.am, not oll-model.oll.am (the latter did not respond). oll.in doesn't call it directly — oll-write does — but confirm the naming so nobody wires the wrong host.tier="starter", the brief says CHF 29, the service env-matrix example shows a CHF 49 tier. Confirm the real Stripe price before charging for photos.CORE_UNAVAILABLE error rather than faking success) until it merges on your go + green CI..in domain gives no Swiss geo-signal (from the GTM brief) — a positioning/DNS decision, not a deploy blocker, but worth settling alongside publish.