Interactive demo — every person, company, and deal is fictional. Changes you make are real but reset periodically.AboutREADMEArchitecture
Siryous CRM
← About

Siryous CRM — Architecture

This document describes the architecture of the production system, then notes what the public demo substitutes. Names, data, and examples here are fictional.

Design goals

  1. Local-first, single source of truth. One SQLite file on one machine. No cloud service, no accounts, no sync protocol — and therefore no staleness: every client reads and writes the same rows.
  2. AI drafts, human confirms. Model output never mutates the database directly. Every AI feature terminates in a review screen; the confirm action is an ordinary form post handled by an ordinary server action.
  3. Least-privilege AI. Each AI feature runs with the minimum read-only tool grant it needs (web search, or Zoom/Gmail/Calendar readers). No granted tool can send, write, or delete anything external.
  4. Boring web tech. Server components render from the database on every request; forms are plain HTML posting to server actions. No client state libraries, no ORM, no API layer between the UI and the data.

System overview

┌─────────────┐   ┌────────────────────┐   ┌────────────────┐
│   Web app   │   │   Claude Desktop   │   │  Claude Code   │
│  (Next.js)  │   │  via MCP server    │   │  sessions in   │
│  port 3000  │   │  (16 CRM tools)    │   │  the repo      │
└──────┬──────┘   └─────────┬──────────┘   └───────┬────────┘
       │                    │                      │
       ▼                    ▼                      ▼
              ┌───────────────────────────┐
              │    data/crm.db (SQLite)   │
              │  WAL mode · single writer │
              │      source of truth      │
              └───────────────────────────┘
                     ▲
                     │ spawn: claude -p (headless, JSON out)
       ┌─────────────┴──────────────────────────────┐
       │        Built-in AI features (web app)      │
       │  ✨ Intake   → tools: web search/fetch      │
       │  ✎ Log      → tools: Zoom read OR Gmail read│
       │  🗓 Prep     → tools: Calendar+Gmail+web    │
       └────────────────────────────────────────────┘

Components

Web app (Next.js App Router)

  • Rendering: React Server Components; every page is force-dynamic and queries SQLite synchronously at request time (better-sqlite3 is synchronous, which suits per-request server rendering well).
  • Writes: server actions (lib/actions.ts) — complete/reopen action items, log interactions (which bump last_touch), edit profile sections in place, move pipeline stages, create people/companies/deals.
  • Read layer: lib/queries.ts — typed query functions (roster ordering, follow-up scoring, dashboard aggregation, cross-entity search, map data).
  • Schema: people, companies, interactions, action_items, opportunities, connections, outreach, plus AI working tables (intake_drafts, log_drafts, briefs). Idempotent CREATE TABLE IF NOT EXISTS plus tolerant column migrations at startup.

MCP server (Claude Desktop path)

A small stdio MCP server registered with Claude Desktop exposes ~16 tools over the same database: search, full profile reads, agenda ("what's overdue?"), pipeline listing and updates, interaction logging, profile building/updating with arbitrary markdown sections, action-item management, and deal management. This makes conversational workflows ("log yesterday's call with the Atlas COO and mark the readout item done") first-class without opening the web app.

Built-in AI features

Each AI page spawns the locally installed Claude Code CLI in headless mode (claude -p --output-format json), using the user's existing Claude subscription — no API key. The prompt embeds the relevant CRM context and the CLI is passed an explicit --allowedTools list:

Feature Context injected Read-only tools Output
Intake Full roster (for dedup + connection-finding) Web search/fetch JSON: person, company, profile sections (markdown), connections, suggested actions, confidence flags
Log Roster + open action items Zoom readers or Gmail readers (per source) JSON: dated entries per matched contact, next-action updates, resolved item ids, unmatched participants
Prep Full dossier: profile, log, deals, open items Calendar + Gmail readers + web Markdown brief with fixed sections and inline source attribution

Results are stored in draft tables with pending/ready/error status and rendered on review screens where every field is editable. Confirming runs the same code paths as manual entry. Two safety properties hold everywhere: tool grants are read-only, and external content (emails, transcripts, web pages) is treated as data to summarize, never as instructions.

Backups & export

The web app and the MCP server each ensure a daily online-backup snapshot of the database (newest 30 kept) — a backup happens on any day the data can change, because only those two processes change it. A separate script can regenerate a human-readable markdown archive (profiles, index, pipeline) on demand.

Data model highlights

  • Status pipeline shared by people and companies: New → Exploring → Active → On hold → Client → Won / Lost / Dormant, each with a free-text status note.
  • Urgency buckets for action items (overdue / log-gap / this-week / awaiting / high-potential / keep-warm / venture / data-gap) — the dashboard is largely a rendering of these buckets.
  • Cadence scoring: each person can carry cadence_days; the Keep Warm view computes days_since_last_touch − cadence and sorts by how overdue a touch is. Logging an interaction resets the clock via last_touch.
  • Connections are person-to-person labeled edges ("made intro", "owed intro", "colleague") rendered by the force-directed map.
  • Profile sections are markdown columns (relationship_intel, business_context, engagement_strategy, notes, extra_sections) — the house style is bullet lists like - **How we met / origin:** …, editable in place.

What the public demo substitutes

Concern Production This demo
Storage SQLite (better-sqlite3), WAL In-memory store (lib/store.ts) seeded from lib/seed.ts; resets when the serverless instance recycles
Queries SQL in lib/queries.ts Same function signatures in plain TypeScript
Writes SQL server actions Same server actions mutating the in-memory store
AI runs Headless Claude CLI, 1–4 min, live tools Scripted simulations (lib/simulated.ts), ~3 s, clearly labeled
MCP server Registered with Claude Desktop Described but not included
Data Real relationships Entirely fictional dataset (16 people, 9 companies, 10 deals) with dates generated relative to "now"

Everything else — pages, components, review screens, server-action flows — is the production code.