MALTE WAGENBACH22 Feb 2026 12:52:25

SQLite as a Personal OS: 20 Tables That Run My Business

February 22, 2026

At some point I was paying for Notion, Airtable, Todoist, a habit tracking app, and a separate tool for finance tracking. None of them talked to each other. I could not query across them. Each one had its own export format, its own data model, and its own monthly subscription. The data I actually cared about was scattered across six products I was paying to maintain.

I already knew SQL. The question was why I was not using it.

The SaaS Sprawl Problem

The productivity tool industry is built around the idea that you need a different product for every category of information: one for tasks, one for notes, one for CRM, one for goals, one for finance. Each product is optimized for that category. None of them is designed to work with the others.

This creates a problem that gets worse the more tools you add. You cannot write a query that joins your deals table with your tasks table to show you all open tasks for active deals. You cannot calculate your cost per lead by joining your outreach data with your finance data. You cannot see whether weeks where you did your habits consistently correlated with more revenue. That analysis is impossible when the data lives in five separate products.

SQL solves this completely. If all your data is in one database, you can ask any question you can formulate as a query. The join that would be impossible across SaaS tools takes three lines of SQL.

SQLite is the right choice for personal use because it is a file, not a server. No Docker, no connection strings, no managed service. The entire database is a single file you can back up with any file sync tool, open on any platform, and query from any language. It is the most deployed database in the world because it is so simple to operate.

The Schema: 20 Tables

The database has grown to 20 tables across five categories. Here is the full picture.

Database Schema: 20 TablesSalesdealscontactsmeetingsOperationsprojectstaskskpis_weeklyopencode_tasksGoals and Habitsgoalshabitshabit_logweekly_reviewsFinancefinancesgoals (revenue)Content and IdeascontentideasPersonaldaily_lognotessessionstravelzeroslide.db — single SQLite file

Sales: deals, contacts, meetings

The deals table is the core of the pipeline: id, client, project, value, status (lead, proposal, negotiation, won, lost), next_action, notes, created_at, closed_at. Every active opportunity lives here. The contacts table stores leads and clients with enrichment fields including lead_status, outreach_stage, source, and country. The meetings table links back to contacts and deals with outcomes and next steps captured after every call.

Operations: projects, tasks, kpis_weekly, opencode_tasks

projects tracks every active initiative with its current status and type. tasks is the task manager: title, project, status (pending, in_progress, done), priority, due date. kpis_weekly is the weekly scorecard with columns for outreach sent, replies, calls booked, revenue closed, and new leads. opencode_tasks tracks coding tasks delegated to AI with their prompts, status, and result summaries.

Goals and habits: goals, habits, habit_log, weekly_reviews

goals tracks quarterly and annual targets with target_value and current_value columns so progress is always computable. habits defines what to track with frequency and target. habit_log is an append-only record of every habit entry. weekly_reviews captures wins, misses, lessons, and focus for the following week.

Finance: finances

A single finances table with type (income or expense), amount, category, client, and description. Simple enough to capture everything, structured enough to query by category and client.

Content and ideas: content, ideas

content tracks everything published or in progress: title, type, platform, status, published date, URL. ideas is a capture table for anything worth remembering: title, description, category, priority, created date. Ideas that get acted on become content or tasks.

Personal: daily_log, notes, sessions, travel

daily_log is a daily entry with summary, mood, energy, wins, and blockers. notes is a freeform capture table. sessions is the most important one: it stores project work summaries with focus area, open questions, next steps, and files touched, so every project has a queryable history. travel is a wishlist and record of trips.

The Database-First Rule

The discipline this system requires is simple: whenever anything happens, the database gets updated first. Not a markdown file, not a Notion page. The database.

Task completed: UPDATE tasks SET status = 'done' WHERE id = ?

Deal moved to proposal: UPDATE deals SET status = 'proposal', next_action = ? WHERE id = ?

Meeting happened: INSERT INTO meetings (title, contact_id, outcomes, next_steps, date) VALUES (?, ?, ?, ?, ?)

Revenue received: two updates, one to goals to increment current_value, one to finances to record the transaction.

Habit done: INSERT INTO habit_log (habit_id, date, value) VALUES (?, date('now'), ?)

The rule sounds rigid but it is what makes the system valuable. A database where updates are inconsistent is not a source of truth, it is a suggestion. The whole point is that you can trust the numbers you query because every event that should have updated them did.

How AI Mediates Access

The database-first rule only works in practice because the AI handles the SQL. I do not write INSERT statements manually for every event. I say "log this meeting" and the AI constructs and runs the query. I say "show pipeline" and the AI runs the deals query and formats the result. I say "idea: build a status dashboard for outreach" and the AI inserts it into the ideas table.

This works because the schema lives in my CLAUDE.md file. The AI knows the table names, column names, and data types for every table. When I give it a natural language instruction, it has enough context to generate the correct SQL without me specifying it.

The morning standup is the clearest example. One word triggers a sequence of queries that produce a complete briefing: active deals with next actions, tasks due today, last week's KPI numbers, the last session for any project I was working on, and any habits due today. The AI assembles this from six separate queries and presents it as a concise summary. Without the database, this would require opening six different tools.

The AI as Query InterfaceHumannatural languageAI Layerschema from CLAUDE.mdgenerates SQLSQLSQLitezeroslide.db20 tablesrowsResultsformatted outputresults returned to human as natural language summary

The Queries That Actually Matter

Here are the queries that run most often, either manually or triggered by the AI.

Morning pipeline view:

SELECT
    client,
    project,
    value,
    status,
    next_action,
    CAST(julianday('now') - julianday(created_at) AS INTEGER) AS days_in_pipeline
FROM deals
WHERE status NOT IN ('won', 'lost')
ORDER BY value DESC;

Weekly KPI summary:

SELECT
    week,
    outreach_sent,
    replies,
    calls_booked,
    revenue_closed,
    ROUND(100.0 * replies / NULLIF(outreach_sent, 0), 1) AS reply_rate_pct
FROM kpis_weekly
WHERE year = 2026
ORDER BY week DESC
LIMIT 8;

Revenue vs goal:

SELECT
    g.title,
    g.target_value,
    g.current_value,
    ROUND(100.0 * g.current_value / g.target_value, 1) AS progress_pct,
    g.unit
FROM goals g
WHERE g.year = 2026
    AND g.type = 'revenue'
ORDER BY g.quarter;

Habit streak calculation:

SELECT
    h.name,
    COUNT(hl.id) AS entries_this_month,
    MAX(hl.date) AS last_logged
FROM habits h
LEFT JOIN habit_log hl
    ON h.id = hl.habit_id
    AND hl.date >= date('now', 'start of month')
GROUP BY h.id, h.name
ORDER BY entries_this_month DESC;

Last session for a project:

SELECT
    summary,
    focus,
    open_questions,
    next_steps,
    date
FROM sessions
WHERE project = 'zeroslide'
ORDER BY id DESC
LIMIT 1;

Cross-table analysis, tasks for active deals:

SELECT
    d.client,
    d.project,
    d.status AS deal_status,
    t.title AS task,
    t.priority,
    t.due_date
FROM deals d
JOIN tasks t ON t.project = d.project
WHERE d.status NOT IN ('won', 'lost')
    AND t.status != 'done'
ORDER BY d.value DESC, t.priority;

This last query is the one that is impossible across SaaS tools. It requires joining two tables that in any other system would live in different products. In SQLite, it is five lines.

Performance and Portability

SQLite is fast enough for everything described here by a significant margin. With the right indexes, a table with ten million rows handles aggregate queries in milliseconds on a laptop. The entire database for my use case is under 50 MB. Performance is not a consideration until you are doing things that SQLite was never designed for, which does not include personal productivity at any reasonable scale.

The portability matters more. The database is a single file. I back it up to cloud storage automatically. I can open it from Python, from the SQLite CLI, from a GUI tool like TablePlus, or through the AI. It runs on every operating system without any setup. There is no migration script to run when I switch machines, no credentials to manage, no service that could go down.

What This Replaced and the Honest Tradeoffs

The tools this replaced: Notion for notes and project tracking, Airtable for CRM and content calendar, Todoist for task management, a separate habit app, manual spreadsheets for finance tracking. The monthly cost of those tools combined was over $80. The cost of SQLite is zero.

The honest tradeoffs: there is no pretty UI by default. TablePlus or a similar SQLite GUI helps, but it is not as polished as Airtable. The AI interface removes most of the friction for daily use, but writing a new query from scratch requires knowing SQL. If you are working with non-technical collaborators, a shared SQLite file is not the right choice.

For someone who writes code, the tradeoffs are worth it. The ability to query across every category of data you track, the zero ongoing cost, the portability, and the fact that the AI can be the interface for routine operations add up to something that no combination of SaaS tools can match.

The Compounding Effect

The most underappreciated aspect of this setup is that every row is permanent institutional memory. The sessions table means I never lose project context. When I come back to a project after two weeks, the last session record tells me exactly where I left off, what was unresolved, and what comes next. That context used to live in my head or in scattered notes. Now it is queryable.

The goals table makes progress visible in a way that is hard to feel without the numbers. Seeing current_value move toward target_value over weeks and months is motivating in a way that a Notion checklist is not, because the numbers are real and connected to the actual work tracked in the other tables.

The database gets more valuable every month. A year of kpis_weekly data lets you see seasonality and trends. A year of habit_log data lets you correlate consistency with outcomes. A year of deals data lets you see which sources produce the best clients and what the actual close rate is by stage. None of that analysis is possible when the data is spread across products that do not share a schema.

Start with five tables. Add more when you need them. The schema above took about a year to arrive at through iteration, not a single design session. The important thing is that all the data is in one place, and the queries you need are three lines of SQL away.