oll.am · Solution Architecture & Implementation Plan · 2026-07-07

oll.in — the job-application agent, as a solution architecture

The Swiss job-application agent, drawn as a system: which platform capabilities it reuses frozen, which it genuinely extends, and a POC-first plan that proves feasibility in parallel lanes before any build-out. Verify, then build only what passes.
Product: oll.in — dossier engine + Career Radar + get-found presence Thesis: ~80% already exists as frozen spines Method: parallel POCs → verify → chunk the build-out

What's inside

  1. The product surface — four deliverables, two engines
  2. The thesis — mostly assembly, not construction
  3. The reuse map — every surface → capability
  4. The solution architecture — one picture
  5. The platform extensions needed — precise list
  6. Identity, corpus & billing model
  7. POC-first plan — parallel feasibility lanes
  8. Build-out — chunked, gated on POC results
  9. Compliance & the honesty line
  10. Decision points for Sam

The product surface what oll.in actually is

oll.in is not one feature — it's a career service with three distinct surfaces, and they carry very different build risk. Naming them precisely is the whole point of an architecture: it lets us reuse aggressively where the risk is already retired, and spike hard only where it isn't.

Surface 1 · the dossier engine

Pay-once, no subscription

  • 📸 Bewerbungsfoto — a studio-grade application photo from phone snaps.
  • 📄 Lebenslauf — a Swiss-format CV (A4, photo, permit/nationality fields, DD.MM.YYYY).
  • ✉️ Motivationsschreiben — a cover letter for that one job, from the applicant's real work.
  • 🎯 Vorstellungsgespräch — interview prep tailored to the role.
Surfaces 2 & 3 · the always-on engines

Subscription — Career Radar & get-found

  • Career Radar — daily scouting of legitimate Swiss/DACH listings, scored against the applicant's profile, with alerts + salary benchmark.
  • Get-found presence — drafts LinkedIn posts in the applicant's voice, scheduled via official LinkedIn OAuth (Postiz).
  • The agent prepares; the human reviews & sends. Never auto-apply, never scrape, never a bot on the send button.

The critical read: Surface 1 is almost entirely reuse of what oll.am already runs. Surfaces 2 & 3 carry the genuinely-new backend and the real feasibility risk — which is exactly why they go behind POCs, not into a build-now list.

The thesis lead with this

oll.am is already three frozen spines and a set of product services. oll.in is mostly a matter of pointing them at a new problem shape — a job-seeker instead of a writer, a job description instead of a manuscript. The genuinely-new code is small and well-bounded; the rest is assembly and one thin frontend.

oll.in is ~80% assembly of frozen capabilities. The 20% that's new is where the risk lives — so we prove that 20% in parallel POCs before we commit a single sprint to build-out. The method Sam set — verify feasibility, chunk to parallel sections
Ship-vs-build honesty. This is Phase-2 work. The nearer franc still lives on already-built assets (CiteBible, ollwrite, humaniz). oll.in earns its place only because its MVP reuses the same oll-write + memory + foto engines and is Sam's own itch (he dogfoods it on his real applications) — so it de-risks the platform rather than starting a net-new build. The POC-first framing keeps it honest: no build-out spend until a POC proves the value.

The reuse map every surface → platform capability

Each oll.in surface maps onto an existing capability, an additive extension of one, or a genuinely-new service. This table is the heart of the architecture — read the right-hand column as the actual scope of work.

oll.in surfaceBacked byStatus todayWork
Bewerbungsfotofoto-service → ReplicateScaffolded (services/foto, 12 tests); Replicate model stubbedUnstub
Swiss CVoll-write verb + BFF-grounded (memory)oll-write LIVE but stateless; the BFF retrieves & passes cited context in; no CV verb yetNew verb
Cover letteroll-write verb + BFF-grounded (memory)Same — one new JD-aware promptNew verb
Interview prepoll-write verb + BFF-grounded (memory)Same — one new JD-aware promptNew verb
Applicant profile / groundingoll-memory (memory.oll.am)LIVE — per-user corpus, cited retrieval, honest abstainReuse
Sign-in / identityCore (core.oll.am)LIVE — magic-link, 72h identity JWTReuse
Pay-once dossier packCore billing (guest checkout)One-time payment mode works; guest pair proposed (PR #21)Guest pair
Email deliveryCore email → ResendLIVE (guest-email allow-list needed)Allow-list
Any-model generationoll-model (model.oll.am)LIVE — Groq default, provider-switchableReuse
Agent runtime / MCP driveoll-mcp + OpenCLAWThin seam built + proven (feat/oll-mcp-thin); OpenCLAW live on VPSAdd verbs
Career Radar (scouting)oll-scout (NEW)Does not existNew service
Radar subscription billingCore billing (subscription mode)Subscription mode already the DEFAULT (grounding-verified); needs a Radar price + an event-id dedupe tablePrice + dedupe
Get-found presencePostiz + LinkedIn OAuth + oll-writePostiz not deployed; draft-post verb absentDeploy + verb
oll.in web appthin frontend (Next.js/BFF)Landing exists; app does notNew client
Count the reds. The genuinely-new backend is: 3 oll-write prompt verbs, one oll-scout service, a small event-id dedupe table on Core, and a Postiz deploy. Everything else is reuse or a small additive extension already on a branch. That is a remarkably small new surface for a product this ambitious — the whole reason to build oll.in on oll.am. Grounding verified this against the live code — see the plan → execution spec for the two corrections it forced (oll-write is stateless; Core already does subscriptions).

The solution architecture one picture

Same visual language as the platform diagrams: solid-black boxes are frozen spines / live services, dashed-red are genuinely-new for oll.in, dashed-blue is an external runtime. The seam is MCP. Nothing about the frozen layer changes — oll.in is a client of it.

oll.in web app + OpenCLAW
Next.js BFF · agentic runtime · cron
Applicant uploads CV, picks a job, reviews & sends. OpenCLAW runs the always-on loop.
HTTPS · Core JWT (BFF) · MCP/stdio+HTTP (agent)
the seam · MCP
oll-mcp
FastMCP adapter · zero new logic · holds one Core JWT
clarify · rewrite · draft · tailor_cv · cover_letter · interview_prep · headshot_order · scout_status
HTTP · Bearer JWT · internal service token
frozen
Core LIVE
core.oll.am
auth · billing · email
+ event-id dedupe (new)
frozen
oll-model LIVE
model.oll.am
any-model gateway · Groq default
frozen
oll-memory LIVE
memory.oll.am
per-user CV corpus · cited retrieval · abstain
product services call the spines over HTTP
live
oll-write LIVE
write.oll.am
+ 3 JD-aware verbs (prompts only)
foto-service
services/foto
guest-checkout headshot · unstub Replicate
new
oll-scout
own Neon oll_scout
pull legit listings · score vs profile · daily digest
new
Postiz
self-host OSS
LinkedIn OAuth · scheduled drafts
frozen spine / live service genuinely-new for oll.in external runtime LIVE in production

The platform extensions needed the "in more detail" ask

Each extension is additive, respects the frozen-Core principle (never weaken an existing route), and follows the house pattern for its kind. Ordered by how much oll.in leans on it.

1 · oll-write — three JD-aware verbs new prompts, not architecture

Add tailor_cv(profile, job_description), cover_letter(profile, job_description), interview_prep(profile, job_description). Each reuses the existing _authorize_and_gate → _run_model pattern — new prompt constants only, no new architecture (per the agent-seam plan). Grounding: the applicant's CV/work history is ingested into oll-memory; retrieval supplies cited passages so output is "from real work, not buzzwords," and abstains rather than inventing when the corpus is thin. Prompts follow llm-call-contract (named constants, JSON-schema-in-prompt, fence-strip parse, input caps).

2 · foto-service — unstub Replicate extend

The scaffold, tests, guest-checkout wiring and Core-shaped error envelope already exist. The remaining work is replacing the stubbed Replicate model + photo-upload path with a real headshot model tuned for the Swiss Bewerbungsfoto (neutral background, head-and-shoulders). Feasibility of the output quality is a POC (below), not an assumption.

3 · Core — guest-checkout pair + email allow-list additive, branch exists

Both billing routes are @require_auth and email send is recipient-scoped to the authed user — so a no-login dossier sale needs the additive guest-checkout-session + guest-verify-session pair plus a guest-email allow-list (per guest-checkout-grant; PR #21). One-time payment mode already works. Never weaken the existing authed routes.

4 · Core — Radar price + event-id dedupe smaller than first drawn

Grounding correction: Core already runs Stripe subscription mode as its default (mode = "payment" if plan=="one_time" else "subscription"), and the webhook already handles invoice.payment_succeeded + customer.subscription.deleted. So Career Radar billing is mostly reuse — add a Radar price/plan and turn it on. The one real gap the grounding found: there is no event-id dedupe table (idempotency relies on naturally-idempotent upserts today). Add a ProcessedEvent table before a recurring paid plan rides the webhook (idempotent-paid-fulfillment). Entitlement stays identity-in-JWT, plan-from-/me (token-identity-not-entitlement).

5 · oll-scout — the one new service new · own Neon DB

A small service (per ADR-008 database-per-service: own Neon oll_scout) that on a cron: pulls fresh roles from legitimate job-board APIs / feeds only, stores them, scores each against the applicant's oll-memory profile via oll-model, and emits a daily digest through Core email. Stateless-to-the-user, thin, disposable. Its single hard unknown is data-source feasibility — which listing sources are ToS-compatible, well-structured, and cover Swiss/DACH with usable volume. That is POC-C, and it gates whether Radar is buildable at all.

6 · Postiz + LinkedIn OAuth new · deferred

The get-found engine self-hosts Postiz (OSS, already identified in the constellation audit as a zero-code deploy) and connects LinkedIn via official OAuth. oll-write drafts posts in the applicant's voice; Postiz schedules them after human approval. This is the highest ToS-sensitivity surface and the lowest near-term revenue — deferred to last, behind its own compliance POC.

7 · oll.in frontend new thin client

A thin product frontend that reuses ollwrite's memory-BFF pattern verbatim: the browser never names a collection or holds a service token; the BFF derives collection: mem:user:{id} from the Core JWT. Flow: upload CV → ingest to memory → paste/select a job → generate dossier (the 3 verbs) → order photo (foto guest-checkout) → pay → deliver. Reuses drag-to-cite + honest-abstain from the proactive-memory-sidecar pattern.

Identity, corpus & billing model how a job-seeker maps onto the spines

Identity & corpus

The applicant is a memory tenant

  • Sign-in = Core magic-link → 72h identity JWT (sub=user id).
  • The uploaded CV + work history = the user's private corpus mem:user:{id}, ACL-scoped by the BFF from the JWT.
  • The job description is ephemeral context passed to the verb — not stored in the corpus.
  • Guest (photo-only) buyers need no account — the foto guest-checkout pattern delivers by email.
Billing

Own the tool, subscribe to the work

  • Photo-only — CHF ~29, guest one-time, no login.
  • Active Search Pass — CHF ~99–149, one-time, the full dossier engine (account, so the corpus persists across applications).
  • Career Radar — CHF ~19/mo, Stripe subscription mode (the new Core capability).
  • Entitlement always live from /me, never trusted from the browser.

The honest pricing line the landing already commits to: a tool you own is pay-once; a service that works for you daily is a subscription. The architecture mirrors it exactly — the dossier engine is stateless-per-purchase; Career Radar is the only thing that needs a recurring relationship, and it's the only thing that carries subscription billing.

POC-first plan parallel feasibility lanes

Sam's method: prove feasibility in multiple POC integrations before building out, chunked so the lanes run in parallel. Each POC below states a hypothesis, the smallest integration that tests it, and a binary pass signal. They share nothing at runtime, so they can be worked concurrently — and each one independently green-lights (or kills) a chunk of build-out.

critical value
POC-A

Grounded dossier quality

Hypothesis
oll-write + oll-memory can produce a send-worthy, cited Swiss dossier (CV + cover letter + interview prep) from a real CV and a real job posting.
Smallest test
Ingest one real CV to memory; add the 3 verbs as prompts on dev oll-write; run against 3 real Swiss postings.
Pass signal
Sam judges ≥1 output good enough to send under his own name — the founder-note test. Citations resolve; no invented facts.
Lane: oll-write prompts · owner-dogfood
critical feasibility
POC-C

Job-scout data source

Hypothesis
A legitimate, ToS-compatible job-listing API/feed exists covering Swiss/DACH roles with usable volume and structured fields (title, description, company, deadline).
Smallest test
A throwaway fetch script against 1–2 candidate sources; inspect fields, volume, terms.
Pass signal
≥1 source returns enough well-structured roles under compliant terms to feed a daily digest. If none pass, Career Radar is re-scoped or killed.
Result (verified)
GREEN — with a flag. Jooble API is the only self-serve API with real CH coverage (+DE/AT) and redisplay-with-link-back terms; supplement with Arbeitnow (open, no-key). All government sources (Job-Room/SECO, EURES, German BA) and LinkedIn/Indeed are RED. Concentration risk: Jooble is effectively the single load-bearing source — confirm CH field depth with live calls before designing the digest around it.
Lane: research spike · done — see execution spec
quality
POC-B

Bewerbungsfoto output

Hypothesis
A Replicate headshot model yields a Swiss-acceptable application photo (neutral background, head-and-shoulders) from a few phone snaps.
Smallest test
Unstub the Replicate call in services/foto; generate from Sam's own snaps.
Pass signal
Output passes an eyeball test against real Swiss Bewerbungsfoto standards; consistent across ≥3 subjects.
Lane: foto-service · independent
billing
POC-D

Radar billing + event dedupe

Hypothesis
Reframed by grounding: subscription mode already exists on Core. So: a Radar price + a new ProcessedEvent dedupe table flip the plan exactly once, additively, without disturbing the frozen paths.
Smallest test
Feat branch on dev Core: test-mode subscription checkout → invoice.payment_succeeded/me reports plan; replay the same event.id and assert a no-op.
Pass signal
Plan flips once, replay is inert, existing billing tests stay green (adversarial-reviewer pass).
Lane: Core billing · gated by merge-gate
mostly proven
POC-E

Agent loop, end-to-end

Hypothesis
OpenCLAW can drive the dossier verbs over MCP for one real application, human-on-send.
Smallest test
Apply services/oll-mcp/docs/openclaw-wiring.md; add the 3 verbs to the thin seam; run one full loop.
Pass signal
OpenCLAW produces a review-ready dossier via --http seam. (Seam already proven both transports — this extends it.)
Lane: oll-mcp · builds on proven seam
compliance
POC-F

Presence via official OAuth

Hypothesis
Postiz can post to LinkedIn through sanctioned OAuth — draft, approve, schedule — with zero scraping or automation.
Smallest test
Deploy Postiz; connect one LinkedIn account via OAuth; schedule one human-approved post.
Pass signal
A drafted post publishes through the official API within LinkedIn's terms. Deferred — lowest near-term revenue, highest ToS care.
Lane: Postiz · deferred to last
Convergence gate. POCs are independent, but build-out is not started until the value POCs are green. POC-A (dossier quality) is the master gate for the MVP; POC-C (scout data source) is the master gate for Career Radar. A red on either doesn't stall the others — it re-scopes exactly one chunk.

Build-out chunked, each gated on its POC

Only after a POC passes does its chunk get built out. The chunks map 1:1 to POC lanes, so the same parallelism carries through. Sequence reflects revenue proximity and risk, not a rigid waterfall.

Chunk 1 · Assisted Dossier Pack — the MVPgated on POC-A + POC-B

Ship the pay-once dossier: 3 oll-write verbs + memory grounding + foto photo + Core guest-checkout + email delivery, behind a thin oll.in frontend. No autonomy, no scraping, no subscription. Genuinely-new code = 3 prompts + 1 frontend + foto unstub. Everything else frozen. This is the build-in-public MVP and Sam's own itch.

Chunk 2 · Career Radargated on POC-C + POC-D · after first franc

Build oll-scout (own Neon DB, cron, score-vs-profile, digest email) + Stripe subscription mode on Core. Recurring CHF ~19/mo. Only worth building if POC-C found a compliant data source and a stranger has paid for the dossier pack first.

Chunk 3 · Agent runtimegated on POC-E · parallel-able with Chunk 2

Wire OpenCLAW to the seam for the always-on prepare loop (human-on-send). Reuses the proven thin MCP seam; extension is the 3 verbs + openclaw-wiring. Low risk — can proceed alongside Radar.

Chunk 4 · Get-found presencegated on POC-F · deferred

Deploy Postiz + LinkedIn OAuth + an oll-write draft-post verb. Highest ToS care, lowest near-term revenue — last, and only if the compliance POC is clean.

Compliance & the honesty line non-negotiable

What oll.in does

Prepare, ground, hand over

  • Prepares a review-ready dossier from the applicant's real work.
  • Scouts only legitimate listings / APIs that permit it.
  • Posts only via official LinkedIn OAuth (Postiz), on human approval.
  • Cites its sources; abstains rather than inventing.
What it never does

The bright lines

  • Never auto-applies, mass-applies, or submits on anyone's behalf — the human hits send.
  • Never scrapes sites that disallow it or auto-connects on LinkedIn.
  • Never claims "Swiss-hosted" or data-residency — "Swiss format" means the Bewerbungsdossier layout only.
  • Never stores another user's corpus — ACL derived server-side from the JWT.
"100 tailored applications an hour" is a contradiction we refuse to sell. Ten sharp, cited, genuinely-tailored applications beat a hundred generic ones — and that's the only promise the architecture can honestly keep. The agent makes ten effortless; the human stays on every send.

Decision points for Sam batched, with recommendations

  1. Kick off which POCs now? A (dossier quality), B (photo), C (scout source) are independent and parallel-able today. Rec: start A + C first — they're the two master gates.
  2. oll.in frontend stack — reuse ollwrite's Next.js + memory-BFF verbatim, or the house Angular/Ionic? Rec: Next.js — reuse the ollwrite BFF, drag-to-cite, honest-abstain wholesale.
  3. Dossier billing — guest pay-once (no login, foto pattern) or account-based for the pack? Rec: guest for photo-only; account for the full pack so the corpus persists.
  4. Approve the additive Core subscription mode? Needed for Career Radar; additive, behind POC-D + the merge-gate. Rec: yes, but only build after POC-C proves a data source and after the first franc.
  5. Career Radar data source — this is the real unknown; POC-C is a research spike. Rec: run POC-C as a pure feasibility spike before committing any Radar build.
  6. Presence / Postiz — deploy now or defer to Chunk 4? Rec: defer — lowest revenue, highest ToS care.
What I'll do on a green light: stand up the two master-gate POCs in parallel — POC-A (add the 3 verbs as prompts on dev oll-write, ingest a real CV, run against real postings) and POC-C (a throwaway fetch spike against candidate Swiss/DACH listing APIs) — each on its own feat branch, each reporting a binary pass/fail, nothing deployed. No build-out until they report.