This is a walkthrough of the Personal OS starter kit — a template, with fictional sample data.
public domain · email only, no account · used once to know who's building

Wake up to a dashboard
that already did the work.

Portfolio vs plan, health trend, people you owe a reply, the week's reading — written to your phone overnight by an AI working from plain text files you own. This page shows the whole system behind it: a strict filing vault, a set of AI playbooks, and the self-updating dashboard. Copy any piece.

Knowledge base · 3-layer vault Skills + router · assistant playbooks Dashboard · self-updating phone view

Built and used daily by Kelvin Chen  ·  LinkedIn  ·  GitHub

All names, numbers, pets, and tickers below are fictional sample data.

Receipts

Three entries from the actual log

Everything else on this page is fictional sample data — these three are real, lightly redacted entries from the system's own log. This is what "did the work" means, concretely.

[2026-06-05 · evening] analysis · condo listing evaluation
pulled the building's full deed history from NYC Open Data
→ the listing's key "comp" turned out to be a withdrawn sale — no deed ever recorded
→ fair-value model said overpriced · decision: keep renting
evaluation framework updated, so the next listing gets the same treatment automatically

[every Saturday · 10:00] cron · weekly digest
read the week's inbox notes + newsletter emails
→ distilled to 7 insights, filed into the wiki with citations
→ vault architecture lint: 2 drift violations found → 2 task cards opened
owner status during all of this: at breakfast

[every morning · 07:00] cron · morning brief
rewrote the dashboard while the owner slept
→ portfolio vs plan · sleep + recovery trend · two people owed a reply
read on a phone with coffee · typing required: zero

The one principle

Separate things by what they are. Then never mix them.

Every design choice in this system is a consequence of one rule: decide what a thing is before deciding where it goes. Two splits do all the work.

🗂️

In the vault: knowledge vs. action vs. raw data

A lab PDF is raw data. Your one-paragraph read of the trend is knowledge. "Book a follow-up by March" is an action. Three folders, one test, no exceptions — that's what stops a vault from rotting into a junk drawer.

🔐

In the dashboard: data vs. presentation, public vs. private

The assistant writes data (JSON); a template renders presentation (HTML). And sensitive data is deployed to a physically separate, gated hostname — "private" never means "hidden in the page," it means "never shipped to the public origin at all."

The shape of it

Three folders, loosely coupled

Adopt them in any order. The vault alone is useful with zero automation; the dashboard is the most involved piece.

# your repo / iCloud folder personal-os-starter/ ├── knowledge-base/ the 3-layer vault (open in Obsidian) │ ├── ARCHITECTURE.md THE rule — raw vs. knowledge vs. action │ ├── raw_data/ Layer 1 · original source material │ ├── knowledge/ Layer 2 · distilled notes + rendered views │ ├── operational/ Layer 3 · goals, Triage board, configs, cron output │ └── memory/ durable facts the assistant reloads each session ├── skills/ the assistant's playbooks │ ├── CLAUDE.md entry point: read ROUTER → load shared → load skill │ ├── ROUTER.md keyword → skill routing table │ └── health/ finance/ visualize/ one skill.md per domain └── dashboard/ the phone dashboard (Cloudflare Pages) ├── deploy/ build.js + deploy.sh + schedule template ├── private/ gated target · your real data └── public/ open target · sanitized/empty

System 1 — Knowledge base

The strict 3-layer vault

Before saving any file, answer one question: is this raw data, knowledge, or an action? The answer is the folder.

Layer 1 · raw_data/ — original material

Primary source files, one subfolder per domain. As close to the original as possible — no interpretation.

Example: raw_data/health/2026-01-15_bloodwork_SAMPLE.md — a raw lab table (Vitamin D 22 = low, LDL 118 = high). Test: could you have received this exact file from someone else? → raw data.

Layer 2 · knowledge/ — distilled understanding

One note per topic, written to be re-read. Links back to its Layer 1 sources. Optionally a rendered HTML view for phone reading.

Example: knowledge/wiki/notes/Bloodwork Trends.md — "most markers stable; Vitamin D low and LDL mildly high, both diet-addressable, recheck next draw." Test: is this my understanding, written to re-read? → knowledge.

Layer 3 · operational/ — the doing system

What to DO and the machinery that drives it: goals, the Triage board, configs the scheduled tasks read, and the outputs they write.

Example: operational/goals/interim/Triage.md — the live Kanban (see below). Test: does it tell me what to do, or capture work in progress? → operational.
Plus a memory sibling. memory/ holds durable facts the assistant reloads every session ("has a cat named Mochi," "goal: get Vitamin D in range") — one fact per file, with MEMORY.md as the index. It's a small hot cache, not Layer 2 knowledge.

System 2 — Skills + router

How the assistant becomes a specialist

When you ask for help, the assistant reads a keyword table, loads shared rules, then loads exactly the one playbook that fits — instead of winging it as a generalist.

You ask
"Analyze my latest bloodwork"
a normal request
Router
ROUTER.md
keyword "bloodwork" → Health
Always load
shared/
formatting + privacy rules
Load skill
health/skill.md
the domain playbook
Act
Reads raw_data, updates the note
ends with next steps
ROUTER.md (excerpt)
| request contains | route to | load | | "labs","bloodwork"... | Health | shared + health | | "portfolio","thesis" | Finance | shared + finance| | "render this note" | Visualize| shared + visualize|
A skill is just a markdown playbook
  • When to use — the trigger phrases
  • Key info / data sources — where to read in the vault
  • Process — the steps to follow
  • Output — the shape of the answer
Add a domain = copy a skill.md, add one router row. The router is a lookup; the skill holds the brain.

System 3 — Daily reports dashboard

From a JSON file to a card on your phone

A scheduled task writes a tiny JSON each morning. A build step bakes it into a static page. Cloudflare Pages serves it behind a login. A plain-text digest hits your iMessage with a link in.

06–07am
Scheduled task
writes data/<report>/YYYY-MM-DD.json
build.js
Bake
inlines newest JSON into the page + manifest
wrangler
Deploy
pushes private/ to Cloudflare Pages
Cloudflare
Serve (gated)
behind Access login, on your phone
iMessage
Nudge
plain-text digest + link in

The data → presentation split, in one picture

data/morning-brief/2026-06-10.json
{ "report_type": "morning-brief", "date": "2026-06-10", "biometrics": { "weight": { "value": 158.2, "avg7":158.9 }, "weight_goal": { "baseline":168, "target":140, "current":158.2 }, "body_fat": { "value": 19.4, "unit":"%" }, "muscle_mass": { "value": 119.0, "unit":"lb" }, "bone_mass": { "value": 7.2, "unit":"lb" }, "body_water": { "value": 56.1, "unit":"%" } }, "fitness": { "muscle_goal": "visible lean muscle · +6 lb lean mass" }, "logs": [ { "job":"Spending log", ... }, { "job":"Reflection", ... } ] }
Pages grow with report types, not days. One morning-brief.html forever; each day adds a small JSON. That archive is your trend history. Add a biometric key → it renders automatically.
…renders to this card (example)
Morning brief
Wed, Jun 10
Body composition
Weight · scale
158.2 lb
7-day avg 158.9 ↓
Body fat
19.4 %
target 14%
Muscle mass
119.0 lb
building
Body water
56.1 %
hydrated
Weight goal
168 start158.2 now140 goal
💪 visible lean muscleBody fat → 14%
Also logged by other tasks
Spending log · within daily cap
Reflection · 2-min note captured
☁️

Hosting: Cloudflare Pages, two targets

A private project (your real dashboards) gated by Cloudflare Access — free email/GitHub login, up to 50 users — and a separate public project that only ever receives sanitized content. Two hostnames = a hard wall, not a path rule that can slip.

💬

Channel: an iMessage digest

iMessage is a poor surface for tables and charts, so the scheduled task sends a short plain-text digest that links into the dashboard. The rich view lives on the phone page; the message is just the nudge.

☀️ Morning brief · Jun 10
Weight 158.2 (7-day avg ↓), body fat 19.4%, muscle up. 2 actions today, 1 due in 7 days.
→ open dashboard

What I actually built

Four reports on one dashboard

Each is the same data→presentation split with a different producer behind it. Two are written by an assistant scheduled task; one (people) is a plain Python script that parses markdown. All numbers and names below are fictional.

Live demo — click around it

The actual dashboard, hosted here with its sample data

This is the exact hub the kit ships — all four reports work, including the People board: relationship cadence, who's overdue, last-touch history. Open it on your phone for the real feel. Every name and number is fictional.

Open the demo hub → Straight to People →
The nudge that links into it
🫶 People pulse · Tue
2 overdue: Robin (14d — you said you'd call back) and Morgan (31d — birthday on Friday).
→ open people board
📈

Daily portfolio

Shows: day change, a thesis board (which holdings strengthened / got challenged), allocation donut, position-cap gauges, and a holdings table where each row expands to its bet + sell-trigger + per-pillar status.

Built by: a pre-market assistant task that reads your holdings + written theses from the vault, pulls quotes/news, grades each pillar, writes the JSON. Honesty rule: valuation move ≠ thesis break; never invents a reason.

Config: caps, pillars, trigger text — in the prompt / portfolio.md, not the page.

☀️

Morning brief

Shows: body-composition grid (weight, body fat, muscle, bone, water, sleep, HRV, RHR), goal bar + lean-muscle chips, today's actions, what's due, and an "also logged by other tasks" feed.

Built by: a 7am task that reads biometrics, pulls due items off the Triage board, and folds in one-liners other overnight tasks wrote. Writes a markdown brief and this JSON.

Config: the fitness block, which biometrics you feed, which jobs report into logs[].

📰

Newsletter digest

Shows: a TL;DR, one "top read," a ranked shortlist (each scored + tagged to a goal, with a link in), and keep / monitor / unsubscribe decisions per source.

Built by: an early-am task that reads one newsletters inbox/label, distills + scores each, writes the JSON, and drops a line into the brief.

Scoring: R5/D4/N3/A3/C4 = Relevance / Depth / Novelty / Actionability / Credibility (1–5 each).

👥

People · friends network

Shows: relationships as four Dunbar circles (Anchors 21d ⊂ Confidants 35d ⊂ Companions 100d ⊂ Community 365d), a reach-out queue with conversation hooks, and a recent-gatherings feed.

Built by: a plain Python script (gen_people.py), no LLM — it parses curated.md + gatherings.md from the vault. Honesty rule: no signal → "unknown," never flagged overdue.

Config: the LAYERS list (cadence + target per circle) at the top of the script.

The friends-network model, and how it renders

gen_people.py reads curated.md + gatherings.md
# curated.md — one block per person ## Anchors ### Alex M. - Contact: iMessage - Last contact: 2026-05-02 — call # 39d ago # gatherings.md — pipe lines 2026-06-01 | Group hike | activity | Sam P. | Park # → script computes per-circle cadence, # flags Alex overdue (>21d), queues a # reach-out with his hook.
You feed it by living your life: jot a gathering line, update a last-contact. The script turns that into circles, a queue, and a memory feed — deterministically, no guessing.
…renders to this (example)
People
Wed, Jun 10
Circles (Dunbar layers)
⚓ Anchors1 overdue
6 / 5 · every 21d
🤝 Confidantson track
15 / 15 · every 35d
Reach-out queue
Alex M. Overdue
Anchors · iMessage · 39d ago
💬 training for a race this fall

Where the data comes from

🔌

Sources, per report

People: a hand-kept curated.md + an in-person log, with optional automatic signals from iMessage history, your calendar, and Contacts. Brief: a phone health export + smart scale. Newsletter: one email label. Portfolio: a hand-maintained holdings file + a brokerage CSV.

⬇️

Export your own data

The kit's DATA-SOURCES.md has step-by-step exports: iMessage (chat.db on a Mac), Contacts (vCard), Instagram ("Download your information"), Calendar (.ics), Health (auto-export), and the honest truth about WeChat (manual — no API). Rule of thumb: only ever export your own data, and keep it local.

Configuration & cadence

What runs, when, and where it lands

An example schedule — your assistant's scheduled tasks plus one host-side deploy job. Several tasks log into the morning brief. Times are illustrative; tune to your own morning.

JobWhen (example)What it doesWrites toChannel
Portfolio report 6:08am · Mon–Fri (pre-open) Pulls quotes + theses, flags cap breaches data/portfolio/…json + a markdown report in the vault dashboard
Morning brief 7:00am · daily Body composition, today's actions, what's due, what it noticed data/morning-brief/…json + operational/goals/daily/…md dashboard iMessage
Task capture 6:00am · daily Sweeps notes/inbox for new to-dos, files them as Triage cards operational/goals/interim/Triage.md vault
Spending log nightly Tallies the day's transactions vs. a cap; the count feeds the brief operational/goals/spending_log/…md vault → brief
Reflection evening Turns a short audio note into a dated reflection note operational/goals/reflection/…md vault → brief
Newsletter digest early am Distills subscribed newsletters into a ranked TL;DR knowledge/wiki/insights/…md dashboard → brief
Weekly digest 10:00am · Saturdays Distills the week's captures; drift-checks the vault knowledge/wiki/insights/…md vault
Deploy (host-side) 7:25am + 5:30pm + every 3h build.js bakes newest JSON, wrangler publishes Cloudflare Pages (private target) deploy
Why deploy runs on your machine, not in the assistant's sandbox. The deploy job runs where wrangler is already logged in — no long-lived Cloudflare token has to live inside an automation sandbox. The assistant only ever writes a JSON file; a dumb host-side job (macOS launchd / Linux cron / Windows Task Scheduler) builds and publishes. Clean separation of trust.

The to-do engine

How the Triage board stays alive on its own

Two files, one job each. The goals file holds the vision (no checkboxes). The Triage board holds the moving to-dos and is the single source of truth — and scheduled tasks keep it current so you don't have to.

operational/goals/goals.md

Vision — never checkboxes

Objectives and deadlines only: "Every holding has a written sell-trigger (Q2)." It describes where you're going. Cards link back to it with (goal:: 2).

operational/goals/interim/Triage.md

Triage — the live board

The only place active to-dos live. Lanes: Now / Next / Waiting / Done. Mixing the vision and the board into one file is the #1 way this layer rots — so they're kept apart.

Example board (fictional)

Now

goal:: 1
Recheck Vitamin D + LDL at next lab draw
↳ added by task-capture, 6am

Next

goal:: 2
Write sell-triggers for remaining holdings
↳ from morning brief

Waiting

Lab results to come back
↳ blocked, auto-kept

Done

goal:: 3
Set up the vault
Wrote ACME + GLOBEX triggers

Who touches the board, and when

TriggerAction on the board
Morning task-capture (6am)Scans your notes & inbox for new commitments and files them as cards in Now/Next, linked to a goal.
Morning brief (7am)Pulls the board's due-soon cards into the daily brief + iMessage so nothing is silently forgotten.
Evening passDe-dupes cards against what already exists, moves finished items to Done, keeps blocked ones in Waiting.
Weekly vault-doctor (Sat)Files any architecture-drift it finds as a (source:: vault-doctor) card — the board even tracks fixing the system itself.
The point: you add to-dos by living your life (jotting a note, mentioning it to the assistant). The scheduled tasks turn those into tracked, goal-linked cards and surface the right ones each morning. The board is never "maintained" as a chore — it's maintained as a side effect.

Getting started

Pick one entry point

Just the filing system

Open knowledge-base/ in Obsidian, read ARCHITECTURE.md, start dropping files in by the placement test. Useful with zero automation.

Add the assistant

Point your assistant at skills/CLAUDE.md, edit ROUTER.md to your domains, and copy a skill.md for each area of your life.

Stand up the dashboard

Two Cloudflare Pages projects, gate the private one with Access, wire your scheduled tasks to write JSON, then schedule deploy.sh. Full steps in dashboard/README.md.

Keep your real data private. Once you replace the samples, stop committing the sensitive folders (a starter .gitignore is included) or keep the whole vault out of any public git remote. The public/ deploy target must never receive private data.