Skip to content
← About

2025 — now

Crelyzor.

Four SaaS tools collapsed into one workspace for solo professionals — cards, scheduling, meeting intelligence and tasks that actually share state. Live with billing.

System architecture

Two frontends, one backend, one database — and three independent repos, not a workspace. Their deploy targets have nothing in common, and a shared lockfile would only couple them.

Clients
Async — off the request path
The queue is the seam — everything inside the dashed boundary runs after the HTTP response has already been sent. Hover, or tap, any box for its note.
  • Dashboard: The authenticated app — meetings, cards, tasks and the meeting-AI interface. Client-rendered, because nothing here is worth indexing and every view sits behind a login.
  • Public pages: Card pages and booking links, on their own domain with no auth at all. Server-rendered because these are the only pages that have to look right in a search result or a link preview. Its own repo and its own deploy — the one thing keeping dashboard bundle weight off a page a stranger loads.
  • calendar-backend: One service owning every route: auth, meetings, cards, the meeting-AI endpoints and the public read paths. Prisma talks to Postgres; nothing else does.
  • Cloud Storage: Raw audio lands here first and the worker reads it back out. Keeping recordings out of the database is what lets a transcript row stay small enough to query cheaply.
  • PostgreSQL: All 47 tables. The worker writes results back onto the same meeting row the API already served, which is the entire reason this is relational rather than document storage.
  • Bull queue: The seam. Upload enqueues a job and returns immediately, so a 30–120 second transcription never sits inside an HTTP request. Bull carries retry counts and job state, so a failure is observable rather than silent. Bull needs a real TCP connection to Redis, not a REST client — which is also why the worker can't be serverless.
  • Worker process: A separate always-on process. Pulls the job, fetches the audio, calls transcription then the model, and writes results back. Three attempts with exponential backoff before the meeting is marked FAILED.
  • Deepgram: Speech to text with speaker diarization on. Whisper was the obvious default and can't tell you who said what — the one property you cannot add afterwards in post-processing.
  • GPT-4o-mini: Turns the transcript into a summary, key points and action items. Structured extraction rather than reasoning, which is why the small model is the correct one — about 10× cheaper at the same usable quality.

The hard part: meeting AI that doesn't feel slow

Transcription takes 30 to 120 seconds. You cannot hold an HTTP connection open for that, and a spinner in front of it means nobody uses the feature twice — so none of it runs on the request path.

The price of going async is that slow and stuck start to look identical. So the state is written down rather than inferred:

NONEUPLOADEDPROCESSINGCOMPLETED
FAILED branches off PROCESSING as a real terminal state, instead of a row sitting there forever. The frontend reads the field, so a stuck job looks stuck rather than looking slow.

Failure modes

Two queues with deliberately different retry budgets. Transcription retries 3 times with 5-second exponential backoff — if Deepgram is down, it's down, and burning attempts costs money without changing the outcome. The Recall.ai webhook retries 8 times at 30 seconds, because a bot's recording genuinely isn't ready yet and the right response is to wait longer, not to give up sooner.

WhenWhat happensRecovery
Deepgram 5xx or timeoutJob retries, meeting holds at PROCESSING3 attempts, exponential from 5s → FAILED
Recording not ready at webhookJob re-queued, no partial write8 attempts, exponential from 30s
Worker process dies mid-jobBull redelivers on next worker bootJob is idempotent — re-transcribes cleanly
LLM returns unusable outputTranscript still persists; summary nullRegenerate endpoint re-runs only that step

Trade-offs

  • Postgres over MongoDB. Meetings have participants, recordings have transcripts, transcripts have segments. A document store would have meant denormalising every one of those and then keeping it in sync by hand.

  • Deepgram Nova-2 over Whisper. Whisper is the obvious default and has no speaker diarization. A transcript that can't tell you *who said what* is close to useless for summarisation, and it's the one property you cannot add afterwards in post-processing.

  • GPT-4o-mini over GPT-4o. Transcripts fit comfortably in the smaller context and the task is structured extraction rather than reasoning — roughly 10× cheaper at the same usable quality, with a one-string upgrade path if that stops being true.

  • Recall.ai over building the bot. A bot means OAuth apps, bot infrastructure and recording pipelines — none of which is the product. Recall streams to Deepgram under my own credentials, so the pipeline runs unchanged.

  • Google OAuth as the only login. Solo professionals all have Google accounts, and Calendar sync needs the OAuth grant anyway. No password storage, no reset flow, no credential-stuffing surface — the cheapest security posture is the one with nothing to steal.

Where it stands

Deployed, with subscription billing wired end to end. Ask AI streams over SSE — a first token in a few hundred milliseconds reads as faster than a complete answer four seconds later, even though it finishes at the same time.

Role
Solo — design, build, ship
Stack
Node 20 · Express 5 · Prisma 6
Database
PostgreSQL (Neon, serverless)
AI
Deepgram Nova-2, GPT-4o-mini
Async
Bull on Upstash Redis
Status
Live, billing enabled