What lives where, and roughly how big each piece is.
Two programs. cabinet-api is a Node process started by PM2 with
node dist/index.js; it opens port 3008 and stays up. The web client
is a React app built to static files that Caddy serves at cabinet.benloe.com.
index.ts is a script that runs once at startup — it opens the databases,
creates the objects, connects them to each other, and opens the port. After that
it's finished; the process only wakes when an HTTP request arrives or a cron timer fires.
apps/cabinet/ │ ├── server/src/ 17,500 │ │ │ ├── index.ts 250 startup: create everything, open the port │ │ │ ├── domains/ 4,400 18 files — one per life area │ │ money 1074 · credentials 299 · cravings 270 · healthcare 266 │ │ credentialCatalog 249 · adherence 235 · shopping 231 · misc 229 │ │ mealplan 225 · settings 213 · substances 174 · activity 168 │ │ units 167 · food · health · profile · symptoms · training │ │ │ ├── runtime/ 2,900 the agent loop │ │ agent 1040 · session · queue · prompt · register · router │ │ titler · rateLimits · perf · toolTruncate · mcpHealth │ │ │ ├── gateway/ 2,800 HTTP + live event stream │ │ app 878 · attachments · credentialRoutes · plaidRoutes │ │ settingsRoutes · surfaces · fold · sse · transcript · pendingTurn │ │ │ ├── mcp/ 880 the 63 tools the model can call │ ├── memory/ 1,500 prompt files, templates, lessons │ ├── scheduler/ 950 11 cron jobs │ ├── scripts/ 790 backfill-titles, wipe │ ├── tiers/ 520 what the agent is allowed to do │ ├── integrations/ 1,400 plaid · githubApp · secrets broker │ ├── episodic/ 360 search over past conversations │ ├── push/ 380 phone notifications │ ├── deploy/ 220 Cabinet redeploying itself │ ├── embeddings/ 150 │ └── db/ 110 + 22 migrations, 53 tables │ ├── server/test/ 12,300 53 files │ └── web/src/ 7,300 + 3,800 css ├── surfaces/ 4,400 7 screens │ Chat · Today · Brain · Money · Domains · Ops · Credentials ├── lib/ 1,700 API client, contracts, drafts └── components/ 820 shell (5) + instruments (7)
| Module | Lines | Plain English | Kind |
|---|---|---|---|
domains/ | 4,400 | The actual logic for food, weight, money, medications, meal plans, workouts, symptoms. This is what makes Cabinet different from a chat window. | PRODUCT |
mcp/ | 880 | Defines the 63 tools the model can call. Thin — nearly every tool just validates its arguments and calls into domains/. |
PRODUCT |
memory/ | 1,500 | Loads the markdown files that make up the prompt. About 850 of those lines are templates.ts, a second copy of those files used to seed a fresh install. |
PRODUCT |
web/surfaces/ | 4,400 | The seven screens in the browser. | PRODUCT |
runtime/ | 2,900 | Runs a turn: builds the prompt, starts the model, streams its output back. agent.ts alone is 1,040 lines. |
INFRASTRUCTURE |
gateway/ | 2,800 | The web server. ~50 HTTP routes plus the live stream the UI listens on. | INFRASTRUCTURE |
integrations/ | 1,400 | Banking (Plaid), GitHub tokens, and reading secrets from a separate service. | INFRASTRUCTURE |
scheduler/ | 950 | 11 cron jobs: heartbeat, morning briefing, evening check-in, weekly review, money sync, and one called idle-builder that has Cabinet build things unprompted. |
INFRASTRUCTURE |
episodic/, embeddings/ | 510 | Search over past conversations and documents. | INFRASTRUCTURE |
db/, push/ | 490 | Database setup and phone notifications. | INFRASTRUCTURE |
tiers/ | 520 | A policy deciding which files and commands the agent may touch, with approval queues for the rest. | MACHINERY |
scripts/ | 790 | wipe.ts (reset the system) and a one-off backfill. |
MACHINERY |
deploy/ | 220 | Tracking whether Cabinet's self-redeploy actually landed. | MACHINERY |
runtime/ odds and ends | ~700 | register, rateLimits, toolTruncate, perf, mcpHealth — measuring and constraining the agent rather than doing anything for you. |
MACHINERY |
The model cannot touch the database. It can only ask, in words, for one of 63 named tools to run. Everything Cabinet knows or records goes through this list.
model says: log_food{ description: "chicken bowl", kcal: 640, protein_g: 52 }
│
mcp/ │ check the arguments match the declared shape
▼
domains/food.ts logFood() — INSERT a row, return the day's totals
│
▼
model receives: { id: 3312, totals: { kcal: 1840, protein_g: 141, ... } }
mcp/ is a switchboard. Each of the 63 entries declares a name, a description,
and the shape of its arguments — then calls one function in domains/ and hands
back the result. Almost every tool is a few lines long.
The descriptions are prompt. Roughly 13 KB of them are sent to the model on every single turn, before any argument schemas. That is more text than the charter will be, and unlike the memory files nobody has ever read it end to end.
Grouped by the domains/ file each one calls. 49 map to a domain; the last 14
are infrastructure and belong to no life area.
| Tool | What it does |
|---|---|
| domains/food.ts — 4 | |
log_food | Log a meal with macros; returns running daily totals. |
update_pantry | Add or adjust a pantry item. |
decrement_pantry_for | Subtract a quantity from pantry stock, converting units. |
add_recipe | Save a recipe with per-serving macros and ingredients. |
| domains/mealplan.ts — 5 | |
plan_meal | Put a planned meal on a day. |
list_meal_plan | List planned meals over a date range. |
update_plan_entry | Mark eaten/skipped, change servings or slot. |
remove_plan_entry | Delete a planned meal. |
consume_plan_entry | Eat a planned meal: log the food and decrement pantry in one step. |
| domains/shopping.ts — 2 | |
generate_shopping_list | Meal-plan ingredients minus what's in the pantry. |
list_grocery_list | The full grocery list: plan-derived, staples, and manual rows. |
| domains/health.ts — 2 | |
log_health_day | Write a day of Apple Watch metrics (normally the iOS Shortcut does this). |
health_days | Recent steps, active kcal, resting HR, sleep. |
| domains/training.ts — 2 | |
log_workout | Log a workout with sets; flags personal records. |
log_body_metric | Log weight or body fat; weight returns the smoothed trend. |
| domains/activity.ts — 5 | |
plan_activity | Add a planned activity to the calendar. |
list_activity_plan | List planned activity over a date range. |
update_activity_entry | Mark done/skipped, attach the logged workout. |
remove_activity_entry | Delete a planned activity. |
seed_trainer_anchors | Top up the fixed Tue/Thu trainer sessions. |
| domains/adherence.ts — 2 | |
mark_habit | Mark a goal met or missed for a day, when no query can see it. |
adherence_report | Expected vs actual per goal, with streaks. |
| domains/substances.ts — 3 | |
log_substance | Log cannabis, alcohol, caffeine or nicotine with dose and route. |
substance_day | Every substance event for one day, in order. |
substance_nights | One row per day joining substance timing against sleep. |
| domains/cravings.ts — 3 | |
log_craving | Log a craving the moment it happens. |
resolve_craving | Close it out once you know how it ended. |
craving_report | Which redirect actually works, ranked by success rate. |
| domains/symptoms.ts — 2 | |
log_symptom | Record a symptom severity 0–10 for a day. |
symptom_days | Today's readings, or one symptom's trend. |
| domains/healthcare.ts — 4 | |
log_claim | Log an insurance claim; returns deductible and out-of-pocket totals. |
log_lab | Log a lab result and flag out-of-range values. |
log_medication | Add a medication with schedule and supply, for refill nudges. |
log_hsa_contribution | Record an HSA contribution; returns headroom against the IRS limit. |
| domains/money.ts — 7 | |
money_summary | Accounts, net worth, spending by category. The starting point for money questions. |
money_transactions | Recent transactions, newest first. |
money_categories | Spending grouped by category, largest first. |
money_trend | Net worth, total spend, and restaurant spend by day. |
money_holdings | Investment positions across linked accounts. |
import_transactions_csv | Import transactions from CSV when a bank won't link. |
upsert_manual_account | Track an account Plaid cannot reach. |
| domains/misc.ts — 8 | |
log_mood | Log a 1–5 mood, energy and stress check-in. |
add_journal | Append a free-form journal entry, indexed for later recall. |
upsert_task | Create or update a task or reminder. |
upsert_contact | Create or update a contact. |
upsert_goal | Set a measurable goal — the number a dial on the dashboard points at. |
upsert_constraint | Record a hard constraint a plan must never violate. |
list_constraints | List the active hard constraints. |
add_price_watch | Watch an item or URL for a target price. |
| No domain — infrastructure — 14 | |
query_db | Run a read-only SQL SELECT against the whole database. The workhorse. |
search_episodic | Semantic search over past conversations and journals. |
search_documents | Search uploaded PDFs — insurance, lease, tax documents. |
recall_lessons | Recall relevant stored lessons for the current context. |
add_lesson | Store a lesson, with evidence and a confidence score. |
retire_lesson | Retire a lesson that proved wrong or stale. |
list_promotable_lessons | List lessons durable enough to graduate into permanent memory. |
promote_lesson | Mark a lesson graduated after writing it into a memory file. |
update_memory | Rewrite one of the markdown memory files. Git-committed. |
render_widget | Render a rich card inline in the chat. |
enqueue_approval | Propose an action that needs approval. |
money_sync | Pull fresh balances and transactions from every linked bank now. |
plaid_institution_search | Check whether a given bank can be linked at all. |
usage_status | Where the Claude subscription stands right now. |
*_activity_* and five *_plan_entry
tools are the same four verbs twice over, and five of the seven money_* tools are
read queries that query_db could already answer.
One SQLite file, cabinet.db, built up over 22 migrations. 53 tables.
Only 14 foreign keys are declared — the schema is far flatter than the table count suggests.
MONEY plaid_item ──< financial_account ──< financial_transaction └──────────< holding >── security net_worth_snapshot budget (standalone) FOOD recipe ──< recipe_ingredient ├─────< food_log └─────< meal_plan_entry pantry_item grocery_list_item (standalone) TRAINING workout ──< workout_set ^────── activity_plan_entry HEALTHCARE insurance_plan ──< claim document ──< lab_result medication prior_auth hsa_contribution (standalone) GOALS goal ──< habit_event CONVERSATION chat ──< message SYSTEM task ──< build_run
──< means one-to-many. Everything not shown above has no declared key to anything.
Thirteen tables carry a local_day column and are joined on it rather than by
key: food_log, body_metric, health_daily,
substance_log, craving_event, symptom_log,
mood_log, journal_entry, habit_event,
workout, meal_plan_entry, activity_plan_entry,
net_worth_snapshot.
That is what makes "how did last Tuesday go" answerable in one query, and it is the
reason most of these tables need no foreign keys at all. It is also the seam that will
need a user_id if Cabinet ever has a second user.
| Group | Tables | Count |
|---|---|---|
| Food & kitchen | food_log recipe recipe_ingredient pantry_item meal_plan_entry grocery_list_item |
6 |
| Body & training | body_metric health_daily workout workout_set activity_plan_entry |
5 |
| Health tracking | substance_log craving_event symptom_log mood_log journal_entry |
5 |
| Healthcare | insurance_plan claim lab_result medication prior_auth hsa_contribution |
6 |
| Money | financial_account financial_transaction holding security plaid_item net_worth_snapshot budget subscription |
8 |
| Planning | task goal habit_event hard_constraint contact price_watch reading_item |
7 |
| Conversation | chat message document |
3 |
| Machinery | action_audit approval perf_span token_usage rate_limit_sample rate_limit_state retrieval_log build_run schema_migration app_setting credential push_subscription push_delivery |
13 |
| Path | Size | What it is |
|---|---|---|
data/cabinet/cabinet.db | 22 MB | Everything logged — ~50 tables. |
data/cabinet/episodic.db | 4 MB | Searchable history. |
data/cabinet/memory/ | 81 KB | 11 markdown files that become the prompt. Its own git repo, no remote, nothing reviews it. |
data/cabinet/documents/, chat-images/, backups/ | — | Uploads and backups. |
memory/ folder. On top of that sit
the 63 tool descriptions — several thousand more tokens that are also prompt, also read
on every turn, and have never been read as prose.