PortLev chevron_right Build Logs chevron_right Command Center
build Build Log #005

How I Built
The Command Center
in a Weekend

A Portfolio Executive's walkthrough of building a double-click portfolio cockpit plus a Shabbat-aware morning brief that lands in your inbox before you wake up. No server, no SaaS, no recurring cost.

YK

Yuri Kruman

Portfolio operator across 8 properties · Jun 2026

0

properties tracked in one view

$0

/month to run

0

file you double-click to open

0m

setup, including Gmail App Password

bolt

The 30-Second Version

The Command Center is a single offline HTML file plus a Node morning-engine. The HTML is your portfolio cockpit (six tabs, click-to-edit, localStorage persistence). The engine runs each morning via Windows Task Scheduler, refreshes RSS feeds and the KB digest, rewrites data.js and emails you a five-minute brief. It is Shabbat-aware: Friday-evening through Saturday it refreshes data silently and suppresses the email.

report

The Problem

Portfolio operators waking up to 14 dashboards, three Notion pages and a wall of RSS noise.

memory

The Stack

Static HTML + vanilla JS + localStorage + Node + Nodemailer + Windows Task Scheduler.

block

What It Doesn't Need

No server, no database, no SaaS subscription, no auth, no JavaScript framework.

timer

Build Time

One weekend for v1. A second weekend for the morning engine and scheduler.

If you are a portfolio operator who has ever opened five browser tabs at 7 am to figure out what changed overnight, this is for you. The point is not that you should clone this. The point is that your cockpit should be one click away, work with the WiFi off and remember your edits without a backend.

Part 1

Why a Single HTML File, and Why a Morning Email

I run a portfolio of eight properties (DueDrill, AI HR Pilot, BookToCourse.AI, the Beast Score / Leverage Brief stack, Ohr Vishua, the 92 Percent Fund, the PE-fund-website business and the consulting practice). Plus the Fortune-500 fractional CHRO work. Plus the book. Plus the podcast. By Q2 2026 I had:

14

dashboards across Stripe, Vercel, Supabase, Notion, beehiiv, ConvertKit, Beehiiv, GitHub, Plausible, Hubspot, Linear, Lattice, Google Analytics

~40 min

to do my "where am I" check each morning before any actual work

$0

that I was willing to add to monthly burn for ANOTHER SaaS dashboard

1

non-negotiable: it had to respect Shabbat without any thinking on my part

The instinct was to spin up a Next.js dashboard on Vercel. I started doing exactly that and stopped after four hours. The problem with the framework-and-server approach is that for an audience of one (me), the cost-of-running is 100% friction and 0% value. I do not need auth. I do not need realtime. I do not need a database. I need a thing I can double-click on a Sunday and trust.

  1. 1An audience of one is a different product than an audience of a thousand. Stop building like you have users.
  2. 2The browser is a perfectly good runtime. A static HTML file with localStorage will outlive most of my SaaS subscriptions.
  3. 3The morning brief is the whole product. The dashboard is for forensics. The email is the daily ritual.
starThe Single Most Important Design Call

Static HTML for the cockpit. Node for the morning brief. Nothing in between.

A server is a thing you babysit. A static HTML file is a thing you ignore. The morning engine is a script that runs and dies. If anything breaks, the dashboard still opens. If the dashboard breaks, the email still arrives. Two independent failure domains beat one always-on service every time.

Part 2

The Stack (and Why Each Piece)

Click each layer for the reasoning.

description

Cockpit

Single static HTML file

expand_more

No build step, no framework, no transpiler. One index.html with inline Tailwind via CDN and vanilla JS. The file is the artifact. You email it to yourself, you back it up to Dropbox, you carry it on a USB stick.

save

Persistence

localStorage + JSON export

expand_more

Edits live in localStorage. Top-right buttons export your overlay to JSON for backup and re-import on a new machine. No cloud sync. No race conditions. Your data never leaves your browser unless you choose.

code

Morning Engine

Node.js + Nodemailer

expand_more

Pure ESM Node scripts. Nodemailer sends through Gmail using an App Password (not your real password). No framework, no orchestrator, no queue. scripts/daily-brief.mjs is the entrypoint. Read it top to bottom, understand it in 10 minutes.

rss_feed

Market Intel

4 RSS feeds, parsed in Node

expand_more

AI+HR, AI Wage Gap, VC funding, HR Tech. Fetched at 07:00, parsed with a tiny XML reader, top 3 from each feed make the brief. Anything older than 24h is filtered out. No webhooks, no third-party aggregator.

schedule

Scheduling

Windows Task Scheduler

expand_more

No cron service, no Vercel job, no AWS Lambda. install-scheduler.ps1 registers a Sun-Fri 07:00 task. On Linux or Mac the same script becomes a single cron line. The OS already has a scheduler. Use it.

do_not_disturb_on

Shabbat Logic

Day-of-week + sundown check

expand_more

Scheduler skips Saturday. Friday evening / Saturday the script still runs at 07:00 but the brief composer emits no email; data still refreshes silently so Sunday morning is fresh. One-line check, infinite halakhic upside.

Part 3

The Six-Phase Build Sequence

Each phase is 2-8 hours. Sequence them in order; don't try to parallelize until Phase 4.

Schema Dashboard Refresh Brief Schedule Edits
1
2-3 HOURS · PAPER

The Portfolio Schema

One Node module: scripts/data.mjs. It is the single source of truth for portfolio defaults: companies, deals, OKRs, recurring tasks, build status. The dashboard and the email both read from this file. Edit once, both surfaces update.

scripts/data.mjs
export const portfolio = {
  companies: [
    { id: 'duedrill',    name: 'DueDrill',
      stage: 'live',      mrr: 0,
      health: 7, owner: 'YK' },
    { id: 'aihrpilot',   name: 'AI HR Pilot',
      stage: 'pilot',    mrr: 0,
      health: 8, owner: 'YK' },
    // ...6 more
  ],
  deals: [
    { co: 'Finesse', stage: 'verbal',
      arr: 114000, due: '2026-06-09' },
  ],
  okrs: [
    { id: 'q2-mrr', label: 'Q2 MRR',
      target: 5000, current: 0 },
  ],
  tasks: [
    { id: 'edgar', label: '92% Fund EDGAR',
      done: false, daysOpen: 5 },
  ]
};
terminalExact Prompt Used

"I run a portfolio of [N] properties / projects. Help me design a JavaScript module that's the single source of truth for: (a) per-company state (stage, MRR, health 0-10, owner), (b) open deals (counterparty, stage, value, due date), (c) quarterly OKRs (label, target, current), (d) recurring tasks (label, done bool, daysOpen). Output as a default-export const so the same file can be imported by Node scripts AND read as a script tag in a static HTML page."

Part 4

What I'd Do Differently Today

1

Resist the Next.js variant entirely

An earlier richer-but-server-dependent version still lives in app/, components/, lib/. It works, requires npm run dev, and is the version I never open. The static index.html is the real product. Delete the framework variant on day one.

2

Write the brief in plaintext FIRST, HTML second

I built the HTML email first, then bolted on plaintext for fallback. Half my reads are on my phone in mail.app's plaintext-favored preview. Build the plaintext brief first; the HTML is a render of it, not the reverse.

3

Move the data module to a shared package, not a sibling file

As the portfolio added a 9th and 10th property, I wanted the same schema in the CEO Command Center and the Newsletter OS. Today I'd publish @yk/portfolio-schema as a private npm package on day one.

4

Add a one-line CHANGELOG to every refresh

After 30 days you want to know what changed. The refresh now logs to refresh.log. I should have added that on commit #1, not commit #84.

Part 5

Adapt This for YOUR Cockpit

The architecture (schema → static dashboard → morning refresh → brief composer → OS scheduler → localStorage overlay) is the template for any audience-of-one operating dashboard. Five adaptations off the same skeleton:

Cockpit For Schema Morning Brief Sections Persona
Solo GPFunds, LPs, deals, OKRsPipeline, port co alerts, IC prepSolo investor
Fundraising leadDonors, gifts, meetings, OKRsToday's outreach, stale relationshipsNonprofit ED
Sales operatorAccounts, opps, AE quotasToday's calls, slip risksSales leader
FounderProducts, exps, hiring, runwayBurn, exp delta, hires dueMulti-product founder
Portfolio executive8 properties + consultingTop 3 actions + market + KBYuri Kruman
Part 6

Starter Prompts for Claude / Cursor

If you want to start today, these are the four prompts that took me from zero to running.

PROMPT 1Schema

"Write scripts/data.mjs as the single source of truth for a portfolio cockpit. Export a default const with: companies (id, name, stage, mrr, health 0-10, owner), deals (counterparty, stage, value, due), okrs (label, target, current), tasks (label, done, daysOpen). Make it importable from Node AND readable as a script tag in static HTML. Pre-populate with [YOUR LIST] entries as starter rows."

PROMPT 2Static dashboard

"Build index.html as a single self-contained file with inline Tailwind via CDN and vanilla JS. Six tabs: Portfolio, Pipeline, OKRs, Tasks, Feeds, Builds. Read window.__DATA__ from data.js. Overlay localStorage edits keyed by entity id. Every cell click-to-edit. Top-right toolbar: export-JSON, import-JSON, reset (with confirm). No React, no Vue, no build step."

PROMPT 3Morning engine

"Write scripts/daily-brief.mjs (ESM Node) that: (1) fetches [N] RSS feeds in parallel, (2) reads my KB daily digest from disk, (3) rewrites data.js as window.__DATA__ = {...}, (4) composes an HTML+plaintext email with sections [Top 3 Actions | Pipeline This Week | Per-Company | Market Headlines | KB Digest | Stale Warnings], (5) sends via Gmail App Password using Nodemailer, (6) suppresses the email between Friday 17:00 and Saturday end-of-day. Use top-level await, no orchestrator framework."

PROMPT 4OS scheduler

"Write setup/install-scheduler.ps1 that registers a Windows Task Scheduler task to run node scripts/daily-brief.mjs Sun-Fri at 07:00 local. Include a matching uninstall-scheduler.ps1. The task should run hidden (no popup), with the project directory as the working directory, even when the user is not logged in (require stored credentials)."

What the Command Center Is Not

It is not a CRM. It is not an analytics platform. It is not a team product — there is no concept of users, sharing or permissions. It does not sync between machines automatically; if you want your edits on a second laptop, you export and import. It does not replace Stripe, Vercel, Supabase or Plausible dashboards for forensic investigation.

What it is: a single-operator cockpit that gives you the one view you need at 7 am and the one email that tells you what to do. The narrowness is the point. Cockpits that try to become CRMs become Notion. Cockpits that stay cockpits stay opened daily.

The question is not
"do I need a dashboard?"

The question is:

"What three numbers, if I knew them by 7 am every day, would change what I work on by 9 am?"

Write down those three. The next 16 hours are the cockpit. The 16 after that are the morning brief. Then it runs forever, for $0/month, on your own machine.

This walkthrough is part of the Portfolio Leverage Co. Build Bench series. For the weekly operating brief, subscribe above. For the cohort where we build these tools together, apply here.