README.md. The Markdown file stays the canonical GitHub landing (GitHub needs the .md); this is the same content in the house style, wired into the site so it lives in the timeline and the artifact index. If the two ever drift, the README.md in the repo root wins.What it is
oll.am is an umbrella platform for shipping small AI products on shared infrastructure. It is one monorepo with a frozen Core — auth, billing, and email, deployed and live at core.oll.am — plus a set of small product services that own only their product logic and call Core (and the oll-model gateway) over HTTP for everything else. Products carry zero auth / Stripe / email code, so Core can be built once, deployed once, and never touched again, while each product stays tiny and disposable.
Positioning: any AI model, pay once — no subscription.
The exit strategy is modular: each service is its own deploy unit, so you can sell one product or the whole platform.
Architecture in one picture
core.oll.ammodel.oll.am- Core is frozen infrastructure — build once, deploy once, never touch.
- The gateway (oll-model) is a frozen model-call relay — one OpenAI-compatible surface in front of swappable providers ("any model, pay once").
- Product services are product logic only — they delegate auth/billing/email to Core and (where they need an LLM) the model call to the gateway, over HTTP.
Service map
| Service | Dir | Purpose | Key endpoints | Deploy unit (Coolify) |
|---|---|---|---|---|
| Core frozen | core/ |
Auth + billing + email spine. Magic-link passwordless auth (72h HS256 identity JWT), Stripe checkout/webhook/portal, transactional email. Owns its own Neon DB (oll_core). |
POST /api/auth/magic-link · POST /api/auth/verify · GET /api/auth/me · POST /api/auth/refresh · POST /api/billing/create-checkout-session · POST /api/billing/webhook · GET /api/billing/status · POST /api/billing/verify-session · POST /api/billing/portal · POST /api/email/send · GET /api/health{,/neon,/stripe} |
Base Directory /core, Watch Paths core/** → live at core.oll.am |
| oll-model frozen | services/oll-model/ |
The oll-model gateway — an identity-free, OpenAI-compatible model-call relay. Default provider Groq (hosted Llama etc.); swap to claude or local ollama via LLM_PROVIDER. Adding a provider = one entry in llm_client._PROVIDERS. No database. |
POST /api/text/complete (requires X-Service-Token) · GET /api/text/health |
Base Directory /services/oll-model, Watch Paths services/** |
| write product | services/write/ |
Writing-improvement product (the Core client behind humaniz.me). Honest clarity/rewrite tool only — never a detector-bypass. Delegates the model call to the gateway and billing/email to Core. | POST /api/write/improve · GET /api/write/health |
Base Directory /services/write, Watch Paths services/** |
| foto product | services/foto/ |
The headshot product (next-dollar ship). Guest checkout, no login: pay once, generate via Replicate, deliver by email — billing/email delegated to Core. | POST /api/foto/checkout · GET /api/foto/fulfill · GET /api/foto/health |
Base Directory /services/foto, Watch Paths services/** |
# cd core — or services/oll-model · services/write · services/foto
cd core
pip install -r requirements.txt -r requirements-dev.txt
pytestRepo conventions
Monorepo, one Dockerfile per service, independent deploys
One repo, unified version control. Each service folder has its own Dockerfile, requirements*.txt, openapi.yaml, tests/, and pyproject.toml. There is no shared Python package — services integrate only over HTTP, never by importing each other or touching another service's database (database-per-service on Neon; stateless services have none).
HTML is the interface
Live project status is not tracked in prose. The single source of truth is state/state.json, rendered by scripts/state.py into the Control Room at site/oll-am-status.html. site/ is the deployed build-in-public landing + docs (flat static HTML, served by the root Dockerfile → nginx). specs/ holds the API contract; CLAUDE.md carries the full living context.
CI/CD — the "dark factory" (merge ≠ deploy)
oll-core.yml → core/** (pytest + contract + Schemathesis + DTO-drift)services.yml → services/** (matrix pytest: foto · write · oll-model)site.yml → site/** (design-conformance gate)Merging a PR does not deploy it — deploy is a separate Coolify action.
- CI = GitHub Actions, path-filtered. A change only runs the jobs it touches — Core gates (
core/**), the services matrix (services/**), and the site design-conformance gate (site/**). - CD = Coolify on the Hetzner VPS. Each service is a separate Coolify app keyed on its Base Directory + Watch Paths, so only the changed service redeploys.
main= production, auto-deploy OFF = frozen. A stable service onmainis never disturbed. New work rides afeat/<service>branch → PR preview → merge graduates it to production. Merging a PR does not deploy it — deploy is a separate Coolify action.
stage integration line, the promotion ritual, the gate ladder, and the one-button dependency-ordered deploy — lives on The Factory.Run a service locally
cd services/foto # any service folder pip install -r requirements.txt -r requirements-dev.txt pytest # run its test suite python app.py # or: flask --app app run — boots the Flask app
Product services expect Core reachable over HTTP (locally via the shared ollam Docker network → http://oll-core:5000, in production → https://core.oll.am); write additionally calls the gateway (OLL_MODEL_BASE_URL). For the full local stack, see docker-compose.local.yml and docker-compose.core.yml.
Where the live status lives
- Control Room —
site/oll-am-status.html(generated fromstate/state.json): what's running, branch, DBs, what's awaiting a decision, and the current single next action. CLAUDE.md— the full living project context + operating procedure.core.oll.am— the live, frozen Core.