MICHAEL MORALEPORTFOLIO SET · 6 SHEETS
LINKEDIN

DETAIL 1 / E-201 — BENCHPROJECT 3 OF 7

Family Screen Bank

SELF-HOSTED WEB APP · HOUSEHOLD

A screen-time bank: minutes are earned, banked and spent in timed sessions.

DETAIL
1 / E-201
SHEET
E-201 · BENCH
STATUS
PILOT · IN FAMILY USE
ZONE / LAYER
BENCH · SOFTWARE
BUILT WITH
TypeScript, Fastify, SQLite
FIG. 3 — TRY THE LEDGER · SAMPLE VALUES

BALANCE, FOLDED

175 MIN

BASE
100
EARNED
25
BANKED
50

NO SESSION RUNNING

LEDGER · APPEND-ONLY
EntryMinutesBucketNote
#001+100BASEsample base
#002+25EARNEDsample earn
#003+50BANKEDsample carryover

SAMPLE VALUES FOR THE DEMO. NOT MY FAMILY’S DATA OR RULES.

PROBLEM

I wanted screen-time rules that are easy to follow: the balance is a fact, not a verdict; running out is never shown in red; quiet hours read as rest, not lockout.

BUILD

A Fastify and TypeScript API over SQLite with a React PWA, self-hosted. Balances are never stored; they’re folded from an append-only ledger with base, earned and banked buckets. A daily reset moves unused base minutes to the bank. Sessions work as leases, so ending early refunds unused whole minutes.

STATUS

In use by my family as a pilot. The latest phase was built September 8, 2026.

STACK

  • TypeScript (strict)
  • Node 24
  • Fastify 5
  • SQLite (WAL)
  • Drizzle ORM
  • Luxon
  • React 19
  • Vite PWA
  • Web Push
  • Server-Sent Events
  • Vitest

KEYNOTES · BUILD NOTES

  1. A property-style test checks derived balances after 1,000 random operations.
  2. The daily reset is idempotent and tested across spring-forward and fall-back. One test seeds a 169-hour week.
  3. Design tokens are tested code: OKLCH colors with 73 contrast and gamut assertions, and a lint against hard-coded colors.
  4. Built with AI coding agents from phase specs with numbered acceptance tests: 108 commits, 19 merged pull requests.

MICHAEL MORALE · DETAIL 1 — FAMILY SCREEN BANK

DETAIL

1 / E-201

SHEET

E-201