oll.am · oll.in deploy runbook · the one unlock only Sam can do · 2026-07-08

oll.in — the one-step deploy runbook

Everything else about oll.in is built and verified by running. The single thing standing between it and a stranger's first franc is a deploy — and that's the one act only Sam can do. So this is the exact, ordered, frictionless sequence to get v1 live and charging: the backend dependency map (grounded in a live health probe — what's already up vs what still needs verifying), the one decision to make first (where the Next.js app publishes), the money path, and a smoke-test you can follow literally. Where I'm not certain, it says so — nothing here is invented.
First franc path: the CHF 9 dossier (its backends are already live) Known blocker: the LIVE Stripe webhook + secret on Core Only Sam can: publish · set secrets · rotate keys · click a real payment

The runbook, in order

  1. Dependency map — what must be live
  2. Decision #1 — where oll.in publishes
  3. The ordered steps to first franc
  4. Keys to set / rotate
  5. Sam-only vs automatable
  6. Honest flags & uncertainties

Dependency map what oll.in v1 needs live to earn a franc

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:

SpineProd URL (BFF env)Role for oll.inHealth probe (Jul 8)
Corehttps://core.oll.ammagic-link auth · /me · billing checkout + webhook✅ 200 · {"status":"ok"}
oll-writehttps://write.oll.amcover-letter · tailor-cv · interview-prep verbs✅ 200 · {"service":"write"}
oll-memoryhttps://memory.oll.amcorpus ingest + cited retrieval (the grounding)✅ 200
oll-modelhttps://model.oll.amGroq gateway — oll-write calls it, oll.in does not✅ 200 (at model.oll.am, not oll-model.oll.am)
fotohttps://foto.oll.amguest headshot add-on (CHF 29) — v2.1 only⚠️ no response — not deployed yet
oll-scout / Radarhttps://scout.oll.amCareer Radar — v2, off the first-franc path⚠️ no response — v2, expected
The good news: every backend the CHF 9 dossier money path needs — Core, oll-write, oll-memory (and the oll-model gateway behind write) — already returns healthy over HTTPS. The first-franc path is not blocked on standing up backends. It's blocked on (a) publishing the frontend and (b) finishing the Stripe money path on Core. foto is only needed for the CHF 29 photo add-on, which is a separate, later on-ramp.
Honest caveat — health 200 ≠ fully configured. A green /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."

Decision #1 where does the oll.in Next.js app publish?

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.

  1. Coolify on the existing Hetzner VPS recommended — one more Coolify app in the same constellation as every other service. Same ops surface, same bill, same DNS pattern (A @ → 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.
  2. Vercel — native Next.js, zero-config, instant preview deploys, edge CDN. Genuinely the fastest path to a running URL. Cost: a second platform to operate and hold secrets in, and the BFF→backend calls go over the public internet (fine — the spines are already public HTTPS). Splits the constellation across two providers.
Recommendation: Coolify on the VPS — keep the whole platform on one operational surface, one bill, and the option of private-network backend calls; it's the same muscle memory as core/write/memory/foto. The only extra work vs Vercel is a Dockerfile for the Next.js standalone build (automatable — I can prepare it in a PR). If Sam would rather trade platform-consistency for the absolute least setup tonight, Vercel is a defensible fast path since the backends are already public HTTPS. Either way, the gate is the same: create the git remote first.

The ordered steps to first franc follow literally

Publish the frontend + point its env at the live backends Sam · ~15 min

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 Stripe money path — the known blocker Sam · ~3 min · Stripe + Coolify console

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 guest photo add-on (CHF 29) — optional, after the dossier Sam + merge · later

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).

Smoke test — prove the loop end-to-end Sam · test then live

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.

Keys to set / rotate everything secret, in one table

KeyLives onActionBlocks first franc?
STRIPE_WEBHOOK_SECRETCorecreate the live core.oll.am/api/billing/webhook endpoint, set whsec_…YES — the blocker
STRIPE_SECRET_KEYCorerotate sk_test_sk_live_ (treat the old test key as burned)yes (for a real charge)
STRIPE_PRO_PRICE_IDCoreconfirm the live CHF 9 pay-once price for product=oll_proyes
AUTH_JWT_SECRETCorealready set (Core is live + SSO works) — no actionno
OLL_MEMORY_SERVICE_TOKENoll.inset to memory.oll.am's token (X-Service-Token)grounding fails without it
GROQ_API_KEYoll-modelverify it's set (write.oll.am + model.oll.am are up; key is invisible from outside)verses fail without it
STRIPE_PRICE_{HEADSHOT}Corethe CHF 29 headshot price — add-on only; confirm tier/amountno (photo add-on)
REPLICATE_API_TOKENfotoreal headshots — add-on only (mock runs keyless)no (photo add-on)
ALLOW_DEV_SESSIONoll.indo NOT set in prod — the no-auth pro-JWT mint is hard-off unless this = 1security — keep off

Sam-only vs automatable who does what

Sam-only (console / irreversible)
  • Create the bytesbysamu/oll-in GitHub remote + push.
  • Create the Coolify app (or Vercel project) + set all secrets.
  • Create the live Stripe webhook + set its secret.
  • Rotate STRIPE_SECRET_KEY to sk_live_.
  • Merge PR #84 (the merge-gate is yours).
  • Click through a real live-card payment to confirm first franc.
  • Point the .in (or .ch) DNS at the app.
Automatable (I can prepare)
  • The exact env manifest (above) — done.
  • A Next.js standalone Dockerfile + Coolify config for option (a), in a PR.
  • A one-command smoke-test script (health probes + a scripted test-mode checkout).
  • Re-verifying every backend health endpoint on demand.
  • The .env.example → prod mapping kept in sync with the BFF code.
  • Everything up to — but not including — the console clicks + the merge + the live charge.

Honest flags & uncertainties where I am NOT certain