Back to Experiments

Experiment 016

Live React plus Express plus Postgres plus Caddy one door plus CI green

From Static Tracker to Enterprise System JobTracker EXP010 Rebuilt on a Home PC Without VPS Bills.

Can a static tracker be rebuilt to enterprise standards on a home private server with honest tradeoffs?

mimo v2.6 flash via cline cli · React plus Express plus Postgres plus Caddy · 8 verified captures · 0 VPS bill · 27 Sep 2026

Login in this app uses the SAME shared auth backend as EXP 011 plus 012 plus 013 plus 014 plus 015. Not a copy. Token key jobTracker.token. Backend detail stays in EXP 011, link it. This backend only reads auth via GET /api/me, never writes.

Source Code

The source code is public. Prove it yourself.

You get the full frontend. React plus Vite, ledger board, bar and donut tap to filter, plus table to cards. No secrets inside. The data API lives in its own repo with its own README.

github.com/robbyaliasaakbar/robbyaliasaakbar.github.io/tree/main/jobtracker

github.com/robbyaliasaakbar/backend-api-services-jobtracker

Receipts live in both READMEs, no secrets inside. Frontend README plus Backend README.

System Requirements

Live view

Modern browser + internet

Live app login

Akun auth :7002 (OTP email)

Live app jam

08.00 to 21.00 WIB

Local dev FE

Node 22+, npm 10, port 7013 kosong

Local BE

Node 22+, Docker, Postgres :5432, port 7012/7014 kosong

Funnel

Tailscale funnel :7443 to 127.0.0.1:7014

Test gate

lint 0, test hijau, build 38 modules

Model

mimo v2.6 flash via cline cli

First experiment developed with a hosted model instead of the local coder. The ledger runs on GitHub Pages, the data still lives on the home PC behind one encrypted funnel as EXP 010 through 015.

Screenshots

App First Then Terminal Green At The End

Eight captures in strict order. App first then terminal, green at the end for climax. Login plus dashboard plus modal plus mobile, then door plus contract plus lint backend plus lint frontend.

1

Login Session - Sample Ledger Panel

login-session.webp
JobTracker login page with sample ledger panel - Robby Aliasa Akbar
Login - ledger book plus REKAAN panel not user data
2

Dashboard - Strip Plus Bar Plus Donut

dashboard.webp
JobTracker dashboard with pipeline strip bar chart donut and ledger table - Robby Aliasa Akbar
Dashboard - daily driver 3 lamaran plus bar plus donut in sync
3

Add Modal - Solid White Plus Red Shadow

added-lamaran-form.webp
Add lamaran modal with solid white card and red offset shadow - Robby Aliasa Akbar
Modal - solid white plus red offset shadow one courage
4

Mobile - Table To Cards

mobile-version.webp
JobTracker mobile cards with pipeline charts and stacked rows - Robby Aliasa Akbar
Mobile - table to cards on HP 360px works
5

Door - Caddy One Door Plus Health

terminal-door.webp
Terminal showing Caddy one door routing and health checks - Robby Aliasa Akbar
Door - one door one funnel auth only passes through
6

Contract - Seven Enum Plus Fail Closed

terminal-contract.webp
Terminal showing seven status enum and fail closed token rejection - Robby Aliasa Akbar
Contract - identical PHP contract plus fail closed without real token
7

Backend - Lint Clean Plus 16 Tests

lint-backend.webp
Backend lint clean and sixteen tests green - Robby Aliasa Akbar
Backend - BE 16 tests green plus PHP date parity
8

Frontend - Lint Plus 21 Tests Plus Build

lint-frontend.webp
Frontend lint test build green with relative assets and zero localhost - Robby Aliasa Akbar
Frontend - FE 21 tests plus relative build plus 0 localhost

Live Demo

Live ledger plus home backend, no VPS bill

The ledger is still /jobtracker/ from EXP 010 rebuilt. This experiment proves a static tracker can run enterprise habits on a home PC. Try the app live, then compare with the terminal shots below.

Open Live App - /jobtracker/ Live frontend, home PC backend

Backend home PC 08.00 to 21.00 WIB. If the computer or funnel door is closed, the app shows an honest offline card, which is expected and not broken. Test rows were cleaned where shown. Demo uses live frontend plus home backend only.

For those of you unable to read this data from technical standpoint, here is the conclusion:

1. My job notebook was messy but useful, so I rebuilt it to look calm and work daily

2. You log in the same way, your old notes still fit, nothing to move by hand

3. One door for the browser, one key check, no new passwords written anywhere

4. My home computer sleeps at night, when it is off the app says so honestly

5. The move kept every row, counts match and samples match, old file kept seven days

6. Everything that broke is written here, eighteen small stories, no hidden magic

Experiment Details

Same Contract New Backbone Plus Honest Tradeoffs

Massive upgrade from EXP010. Same shape plus same errors, new backbone plus one door plus green gate. Here are the five decisions that move a static tracker to enterprise habits on a home PC.

Same Contract, New Backbone

validate.js plus lamaran.js

The v1 vanilla app was 4 files with zero build. The v2 React app is full new with 2 screens, yet shape plus 7 enum plus error messages stay plek PHP legacy (src/validate.js plus src/api/lamaran.js). Because the contract never changed, old notes still fit and migration stays smooth.

Development used hosted model mimo v2.6 flash via cline cli, not local and not opencode. Bang Rob led research plus architecture plus review plus test, Udin typed code plus suggestions.

One Door Plus One Funnel

Caddy :7014 plus funnel :7443

Caddy on :7014 is one door. /api goes to :7012, /auth passes to :7002 frozen, /health answers 200. One funnel on :7443 forwards to the door. Auth is only read via GET /api/me, never written, Knex never connects to auth.

Proof is infra/Caddyfile plus curl to 4 endpoints. Local plus funnel plus status all green, auth without token answers 401 asking for token.

Cutover Without Losing A Row

migrate plus COUNT match

Backup first, then read SQLite read only from backup, then insert to Postgres with idempotent signature (scripts/migrate-lamaran.js). COUNT matches plus 5 samples match. Old SQLite file stays for 7 days after cutover.

Date parity was fixed with raw string parser for pg DATE so save plus read never shifts a day. Parser was patched in server plus cutover together.

Ledger UI That Stays Calm

5 auth modes plus strip bar donut

Paper token plus hairlines plus one brave accent. Five auth modes plus dashboard strip 7 plus bar plus donut tap to filter in sync, table to cards on mobile, solid modal, toast, offline card that says the server is sleeping.

Proof is screenshots plus design-token.css. No icon library, no router, no heavy store, by decision.

Green Gate Before Push

lint 0 plus test 21 plus build 38

Lint 0 plus test 21 of 21 plus build 38 modules plus copy dist plus relative assets plus 0 localhost. CI adds Postgres service so green means tests really ran, not fake green.

Proof is Actions YAML plus logs. Frontend frontend.yml plus backend backend.yml both green before push.

Evidence Log

Standalone Evidence Log: One Door Plus Contract Plus Green Gate

Every claim about one door plus contract plus green gate is backed by verifiable command line outputs from 27 Sep 2026. Passwords and full tokens never appear, raw status plus counts plus file listings remain exact.

E1. One Door Answers Local

ok true Local

Hitting BE plus Caddy plus Caddy to BE proves one door routes correctly.

curl -s http://127.0.0.1:7012/health
{"ok":true}

curl -s http://127.0.0.1:7014/health
ok

curl -s http://127.0.0.1:7014/api/health
{"ok":true}

Result: BE plus door plus door to BE all ok.

E2. Funnel Forwards Plus Auth Asks Token

401 Ask Token

Hitting public funnel without token proves forwarding works and auth stays defensive.

curl -sk https://aispec.tail06293c.ts.net:7443/health
ok

curl -sk https://aispec.tail06293c.ts.net:7443/api/health
{"ok":true}

curl -sk https://aispec.tail06293c.ts.net:7443/auth/api/me
{"error":"Token tidak sah atau kedaluwarsa"}

Result: funnel to door ok, auth answers 401 asking for token.

E3. Contract Enum Plus Fail Closed

7 Enum Intact

Grep proves 7 stages intact plus fake token stays rejected.

grep STATUS src/validate.js
baru, interview-hr, technical-test, interview-user, offering, diterima, ditolak

curl token ngawur
{"error":"Token tidak sah atau kedaluwarsa"}

Result: identical PHP contract, fail closed without real token.

E4. Backend Green 16 Pass

16 Pass 0 Fail

Lint clean plus suite green plus date parity with PHP.

npm run lint
clean 0 problems

npm test
16 pass 0 fail

Result: BE green, date alias plek PHP.

E5. Frontend Green 21 Plus Build Relative

21 Passed Relative

Lint plus vitest plus build proves Pages ready with zero localhost.

npm run lint
clean

npx vitest run
3 files 21 passed

npm run build
38 modules 137ms css 35.93KB js 256.59KB
src="./assets/index-CbM5JKKw.js"

grep localhost:7002 dist -r
0

Result: FE green, relative assets, 0 localhost safe for live.

Failure Log

Eighteen Failures Written Honestly

Eighteen stories plus 4 near miss plus 12 UI notes. Five deep dives below, thirteen honest list after. Three found before live, one on live, all fixed with receipts.

Tier 1 Incidents Five Deep Dive Case Studies

1. Dates Shift A Day

TZ Silent Bug DEV HIGH

Symptom: Save 2026-09-26 came back as 2026-09-25. Silent, no error, only visible on read back.

Diagnosis: pg DATE became Date at midnight local plus toISOString to UTC shifted back 7 hours for WIB.

Fix: Raw string parser for type 1082 in pgtypes plus same patch in cutover. Save plus read shielded by integration test.

Lesson: Timezone is silent, prove with save plus read, never trust local midnight.

2. Build Green But Runtime Crash

Silent Killer DEV HIGH

Symptom: Build exit 0 yet warning about getToken undefined. Production screen never finished loading.

Diagnosis: AppStore called a helper never exported from auth. Exit 0 hid the broken bootstrap.

Fix: Re export getToken from auth plus read warnings as errors. Bootstrap verified in browser.

Lesson: Exit 0 is not success, read warnings line by line.

3. Container Hits Itself To 503 Live Login

Localhost Trap LIVE HIGH

Symptom: Live login 503. BE container shot to its own localhost, not host auth.

Diagnosis: Localhost inside container means itself, not host. Auth lives outside the container network.

Fix: Host env plus compose service name for auth URL. Live login verified after restart.

Lesson: Localhost changes meaning inside containers, use explicit host names.

4. Absolute Base To 404 Blank White

Base Path PRE-LIVE HIGH

Symptom: Pages showed blank white with 404 on assets. Absolute base broke on project Pages.

Diagnosis: Base /jobtracker/ expects domain root, Pages serves from sub path.

Fix: Relative base plus VITE_BASE removed plus assets ignored by ESLint. Relative proved safe.

Lesson: Relative base survives Pages, absolute base dies quietly.

5. CORS Plus Stale Bundle Double Trap

Dev Plus Bundle PRE-LIVE HIGH

Symptom: Dev on 7013 shot funnel 7443 and got preflight without allow header. Dev also served old funnel artifact.

Diagnosis: Funnel origin not in frozen allowlist plus dev served stale bundle with embedded funnel URL.

Fix: Dev env to localhost, refuse to add origin to frozen. Restore from index template plus restart. Prove with curl plus grep bundle.

Lesson: Prove with outputs, never guess origin plus bundle issues.

Tier 2 Log Thirteen Honest List Plus Near Miss Plus UI Notes

F1 DEV small: test command read as file path to plain run. Lesson: 0 run once mistaken as tested.

F4 DEV medium: event hack while context existed to rewrite. Lesson: stay with context.

F5 DEV small: unused disable twice wrong fix to remove directive. Lesson: target 0 warnings.

F6 DEV small: duplicate SampleLedger import to tidy. Side effect F7 split.

F7 DEV workflow: editor refused big file plus overwrite to split AuthPage to 3 files cleaner.

F8 DEV workflow: pkill matched own shell twice to anti self pattern.

F9 DEV false alarm: parallel ls read stale to sequential check.

F10 DEV small: SQL one liner quote trap to binding.

F11 DEV small: grep handle miss to read caller router first.

F12 DEV prevented: skeleton Caddy 2 bugs to host plus strip prefix before run.

F13 DEV prevented: fake green CI to add Postgres plus TEST_URL.

F14 DEV workflow: long sleep hit timeout twice to split poll.

PRE-LIVE recap F16 to F18: 3 found before push, 0 users hit. If leaked: blank plus no login plus wrong diagnosis.

Near miss 4: live CORS checked before push plus TZ patch also in cutover plus E2E account canceled to protect frozen auth plus 8 compromises openly admitted.

UI notes G1 to G12: funnel artifact rewrite plus dark tone plus moss fix plus solid modal plus hover border plus paper ink stamp tokens plus AuthPage REKAAN plus strip 28 plus boxed toolbar plus 500ms interactivity plus sleep card plus lint test build tracking.

FAQ

Frequently Asked Questions

Is this a new login? ▼

No. Same shared auth as EXP 011. Old notes still open with the same key.

Did my old data move? ▼

Yes. Read only copy, counts match plus five samples, old file kept seven days.

Why one door? ▼

Browser remembers one address. Data goes one way, auth goes another, health says ok.

Why does it sleep at night? ▼

Home PC by decision, no VPS bill. Off means honest card, data stays safe.

Why Postgres? ▼

Real relations plus safe concurrent plus VPS ready, reuse container, zero cost.

Why so many failures? ▼

Eighteen on record. Three found before live, one on live, all fixed with receipts.

Live Status

Live Status - Live Ledger, Home-PC Backend

✅ What works now

  • React ledger live /jobtracker/ with CRUD 7 stages
  • Bar plus donut tap to filter in sync
  • Caddy one door 7014 plus funnel 7443
  • Postgres cutover verified with COUNT plus samples
  • CI green for frontend plus backend

🚧 What is not production yet

  • Not 24/7, local PC runs 08.00 to 21.00 WIB
  • Token 1h, Postgres single container reuse
  • No rate limit page yet
  • Hardening is a separate project
Ledger live on GitHub Pages. Backend runs on my personal PC (funnel :7443 to Caddy :7014 to :7012 plus :7002 frozen). Off means offline card, expected. Transcripts plus file listing show how it works.

Disclaimer

Disclaimer - Live Ledger, Home Backend, Not Production-Hardened

Built with mimo v2.6 flash via cline cli hosted, not local and not opencode. Test rows cleaned where shown. Passwords, tokens, and OTP codes are never shown.

Shared backend: EXP 011 (:7002) · v1 story: EXP 010 (vanilla) · This: EXP 016 (v2 enterprise) · Machine: EXP 001