Skip to main content

case_study://careerwise-io

A Telegram-first AI career coach with a dedicated runtime for every paid user.

Careerwise is an AI career coach SaaS I built as a solo founder. The web app handles auth, billing, provisioning, and bot setup. The actual coaching product lives in Telegram, where each paid user gets their own Cloud Run runtime, private workspace, persistent memory, job-search flow, and PDF CV export pipeline.

Next.jsFirebase AuthStripeFirestoreCloud RunGCSSecret ManagerTelegram Bot APIZeroClawSQLiteDockerPython / WeasyPrint

Problem

Job seekers were juggling search, tailoring, tracking, and interview prep across too many disconnected tools.

Product move

Put the agent inside Telegram so the experience lived where users could message it every day without learning a new product.

Core system

Dedicated runtime + private workspace per user, instead of a shared assistant with shallow state.

Outcome

A career coach that can search, tailor, export, and coach from persistent user context rather than one-off prompts.

why_this_product

Built for the messy reality of job hunting, not just prompt demos.

The real pain in job search is not just finding one role. It is keeping context across many roles, rewriting documents quickly, remembering constraints, following up consistently, and staying coached while motivation drops.

Careerwise was designed around that operational mess. The system collects profile context into structured files, saves role-specific job folders, tracks applications, and keeps the conversation alive inside Telegram so the assistant can behave more like an always-on operator than a one-off chatbot.

what_i_built

Four product layers working together.

Web layer

Auth, billing, dashboard states, provisioning checks, and Telegram bot connection UI inside a Next.js app.

Runtime model

One dedicated Cloud Run service per paid user, instead of one shared agent backend for everyone.

Memory model

A two-layer system: markdown workspace files for durable context plus SQLite brain.db for programmatic recall.

Document pipeline

CV and cover-letter markdown exported to PDF through a separate Cloud Run worker and sent back in Telegram.

runtime_topology

01User signs in on the web app and hits the Stripe paywall.
02Webhook marks payment complete, initializes a workspace, and pre-warms the runtime.
03User connects a BotFather token in the dashboard. The token is validated and stored via Secret Manager.
04A dedicated Cloud Run service boots, syncs the user workspace from GCS into local /workspace, and starts the Telegram channel.
05The agent reads and writes USER.md, CV.md, MEMORY.md, APPLICATIONS.md, and per-role job folders while SQLite memory handles bounded recall.
06When a user requests a CV PDF, the runtime sends markdown to a separate PDF worker and Telegram returns the document.
[ web app ]
auth -> billing -> dashboard -> telegram connect
     |
     v
[ firestore ] ---- billing state / user metadata
     |
     v
[ cloud run ]
careerwise-{uid}
     |
     +-> sync workspace from GCS
     +-> inject telegram config
     +-> run agent with bounded context
     |
     v
[ /workspace ]
USER.md
CV.md
MEMORY.md
APPLICATIONS.md
jobs/{slug}/
memory/brain.db
     |
     +-> job search + scoring
     +-> tailored docs
     +-> pdf export worker
     |
     v
[ telegram ]

memory_and_workspace

The product state lived in files, not hidden magic.

One of the strongest parts of Careerwise is that the agent state is inspectable. The workspace is not a black box. It is a set of explicit files with clear jobs, plus a lightweight SQLite memory layer for recall.

USER.md

Identity, role targets, salary floor, location, and hard constraints.

CV.md

Master source of truth for experience, skills, and all later CV tailoring.

MEMORY.md

Coaching strategy, job-search plan, and recurring behavioral notes.

APPLICATIONS.md

Pipeline tracker for saved jobs, statuses, and follow-up work.

jobs/{slug}/

Per-role folder for JD, tailored CV, cover letter, notes, and PDFs.

memory/brain.db

SQLite memory store used for bounded programmatic recall.

key_decisions

Telegram-first, not browser-first

The product deliberately avoided building a chat UI in the web app. The web layer handled signup, payment, provisioning, and bot connection, while the actual coaching experience lived in Telegram where users already spend time.

Dedicated runtime per paid user

Each user got their own Cloud Run service and workspace. That kept bot tokens isolated, avoided shared-session confusion, and made the product feel like a personal agent rather than a shared SaaS inbox.

File-based workspace over heavier memory infrastructure

Instead of reaching for a vector database early, the system used markdown files like USER.md, CV.md, MEMORY.md, and APPLICATIONS.md, plus SQLite memory recall. That kept the product inspectable and cheap while still supporting persistent coaching.

Separate PDF worker

PDF export was split into its own worker so the agent runtime stayed focused on conversation and orchestration. The export path converted markdown directly to PDF through WeasyPrint rather than sending formatting back through an LLM loop.

truth_points

Details that mattered in the real build.

Active channel

Telegram is the only active user-facing messaging channel.

Provisioning

Stripe payment unlocks workspace initialization and Cloud Run provisioning.

Workspace

User data lives in a per-user GCS workspace synced into /workspace on container boot.

Search flow

Agent reads USER.md, runs job search, saves job folders, scores fit, and returns the best roles.

Export flow

Markdown CVs and cover letters become PDFs through POST /render-cv on a separate worker.

solo_founder_scope

I built the product and the operating system around it.

This was not just an LLM wrapper or a landing page. I owned the user journey from signup to payment to provisioning to messaging. That included product definition, UX, dashboard states, runtime provisioning, workspace architecture, job-search flow, PDF export, and the operational constraints around deployment and sync.

current tradeoffs

  • No in-product chat UI. Messaging happens directly in Telegram.
  • No transcript viewer in the dashboard.
  • Workspace sync is upload-only, so deletions do not propagate cleanly back to GCS.
  • Memory recall is BM25-based rather than vector-search based.
  • Refund handling is manual review after cancellation, not a fully automated Stripe refund flow.

why_it_belongs_here

A strong signal of end-to-end product ownership.

If you are hiring for product-minded AI builders, this project shows the parts I care about most: choosing the right interface, keeping state inspectable, making infra decisions that support real users, and turning a fuzzy workflow into a product with billing, runtime isolation, and operational discipline.