Active · May 31, 2026
곁 · Beside
A private, installable PWA for keeping the encouragement of the people who love you — voice, photo, a kind word, a short clip — and surfacing it on the hard days. One codebase: zero-account local to full production infra by env var alone.
6,822 LOC · 26 routes · local→prod by env var · Terraform
- Role
- Solo (AI-pair-programmed with Claude Code)
- Stack
- Next.js 16 · React 19 · TypeScript · PostgreSQL · Drizzle · PWA · Web Push · S3 / R2 · Terraform · Tailwind v4

A private "support box" you fill with the things that steady you — a loved one's voice note, a photo, a line from a book you love, a twenty-second clip of someone telling you it's going to be okay — and open, calmly one at a time, when you need them. People who care about you can add a message through a link, no account and no install. The product is small on purpose; the engineering underneath is where the work is.
What it does
- Two ways to fill the box: you add directly (voice 30s / photo / words + optional source / video 20s), or a loved one leaves one note via an unguessable link — no signup.
- One way to open it — the surfacing moment: one item at a time, a fresh order each time, like opening a locket. Or a gentle Web Push reminder at a time you pick.
- Installable PWA with an iOS Add-to-Home-Screen flow; full ownership — hide / delete (media really deleted), export everything as a zip, or delete the whole account.
Why
It's built for the moment you're low, so it has to open fast and feel calm, not clinical. The key product insight: because you can fill it yourself, it's useful from day one even if no one else ever contributes — a deliberate answer to the cold-start problem that kills most "ask your friends" apps.
The one idea worth stealing
The same code runs locally with zero external accounts — embedded Postgres (PGlite), local-disk media, auto-generated session & VAPID secrets — and flips to full production infrastructure per environment variable, per key: DATABASE_URL → node-postgres, S3_BUCKET → S3/R2 with 5-minute presigned URLs, VAPID_* → fixed push keys. No code change, no rebuild. Both DB drivers are lazy dynamic imports, so dev never bundles pg or the AWS SDK, and the connection is globally memoized to survive Next 16's multi-worker build.
Engineering bits worth pointing at
- Security-reviewed, not just shipped. A multi-agent adversarial pass found and fixed six real issues: a production auth bypass (dev-login now 404s in prod), a service worker that cached private HTML across accounts, a media route that broke iOS audio by ignoring HTTP
Range(now206/416, or a presigned302on S3), missing CSRF / same-origin checks (proxy.ts), an upload MIME allow-list against stored XSS, and per-IP + per-token rate limiting with an earlyContent-LengthDoS guard. - Platform depth. iOS Web Push only works from an installed PWA;
MediaRecorderMIME differs across browsers (Safariaudio/mp4vs Chromeaudio/webm), so capture is capability-probed with an optional ffmpeg transcode; iOS evicts PWA storage after seven days, so the server — never the browser — is the source of truth. - Decisions written down. Every non-trivial fix is dual-written to a troubleshooting index and a dated narrative, including an architecture record that argues against a premature multi-region DB split (shard for compliance, not for unmeasured latency).
- Shipped on Vercel + AWS — RDS Postgres in Seoul, S3 presigned media, EventBridge cron — with Terraform IaC and a Docker path for persistent hosts.
Honest limitations
No automated test suite yet (verification was manual — a clean production build and a one-off 30-concurrent-write check); rate-limit and idempotency are in-memory (single-instance); ffmpeg transcoding only runs on the persistent / Docker path, so a webm clip recorded on desktop Chrome may not play on iOS; the Terraform infra is a dev posture, with the hardening path noted in comments.
Project log
Chronological record of troubleshooting, retros, and updates while building this.
Decisions & milestones
Evaluating an OCI always-free migration — and why we stayed on AWS
DecisionJun 4, 2026 · 3 min
Tried to move the backend off AWS (12-month free) onto Oracle Cloud's always-free Arm tier (free forever). Wrote and adversarially-verified the OCI Terraform, but hit three walls — a VCN-count limit, a manual-deletion dependency-order trap, and the chronic 'Out of host capacity' for Always-Free Ampere A1. The PAYG upgrade that fixes capacity stalled too. Decision: hold the migration, keep the workload on AWS free tier, and keep the validated OCI config for a future retry.
Multi-region DB decision: US/Korea users, should we split the DB in two?
DecisionJun 1, 2026 · 4 min
Discussing whether to split the DB in two for US/Korea users. The conclusion is 'one DB for now'. Splitting data by region is a compliance (data residency) solution, not a speed solution, and it breaks our app's cross-user contribution links. Speed is solved by function/DB colocation -> media CDN -> (later) read replicas. Splitting before measuring is self-harm. (Discussion memo — not an implementation.)
Build log
Week of Jun 1, 20262 entries · 1 Troubleshoot · 1 UX retro
Deploying the backend on Vercel + AWS: RDS, S3, and every wall in order
TroubleshootJun 4, 2026 · 4 min
First real backend deploy — Terraform-provisioned RDS (us-east-1) + a private S3 bucket, wired to the existing Vercel app by environment variable. The walls hit, in order: the Free-Tier backup limit, the RDS SSL handshake (sslmode=require is verify-full), Kakao KOE006 (missing platform domain), the KOE237 rate limit, and the OAuth account-linking surprise.
Design pass: dressing the app in locket artwork, and shifting copy from 'crisis' to 'encouragement'
UX retroJun 1, 2026 · 5 min
Wired six locket illustrations generated with Nano Banana across the whole app (optimized with sharp), fixed the app icon being hidden behind the default favicon, and overhauled the emotional tone of the copy from a 'when you're falling apart' crisis frame to 'encouragement from the people who care about you.' Same reason we dropped the suicide-prevention hotline — not treating people as patients, but as a friend at their side.
Week of May 25, 20267 entries · 1 Update · 1 Troubleshoot · 4 Tech retro · 1 UX retro
Adversarial Security Review: 11 Confirmed Flaws and What We Fixed Now
Tech retroMay 31, 2026 · 2 min
After the build, we ran a multi-agent adversarial review (security/auth/data/capture/PWA/spec) and confirmed 11 flaws. Fixed immediately the ones that break the demo/spec; documented the 2 production-scale items.
Kakao Login Live Integration: from KOE006 to KOE010, and the decision to do OAuth directly
TroubleshootMay 31, 2026 · 2 min
Implemented the spec's 'Kakao via generic OAuth' directly without Better Auth, slotting it into the lib/auth interface. Cleared two gates with real keys — KOE006 (redirect not registered) and KOE010 (missing client_secret) — and logged in successfully.
Beotimok: An Overnight Autonomous MVP Build — From a One-Page Spec to a Working App
UpdateMay 31, 2026 · 2 min
A record of taking a single work.md spec and building the 'Beotimok' (a private keepsake box you open when you're falling apart) PWA MVP all the way to done overnight. Designed to run immediately with 0 external accounts.
PGlite Embedded DB: The Full Story of Concurrency, Corruption, and Build Conflicts and How They Were Solved
Tech retroMay 31, 2026 · 3 min
Three traps hit with PGlite (WASM PostgreSQL), chosen for zero external DBs — concurrent queries, disk corruption, and build multi-worker conflicts — and the convergence onto lazy-init + a process guard.
Every Stack Decision: What, Why, Alternatives, and the Production Switch Point
Tech retroMay 31, 2026 · 3 min
A rundown of every tech-stack decision made in the 버팀목 MVP, with rationale, alternatives, and production switch points. The core pattern: 'put local defaults behind an interface, and swap in production keys by changing just one file.'
Trust comes from 'feeling, control, relationship,' not an encryption explainer — and multitenancy
UX retroMay 31, 2026 · 3 min
A discussion and decision on how to handle 'I'm scared my data is stored outside' in a sensitive product. Where E2EE fits, prioritizing felt trust, implementing export/delete-everything, and proving multitenancy isolation.
Video as a Fourth Content Type — §1.1 A Deliberate Departure from Simplicity
Tech retroMay 31, 2026 · 2 min
By owner decision, short video (20s) was added on top of voice/photo/text. Flagged as a deliberate departure from the spec's simplicity guardrail, extending the existing capture pattern as-is. Unresolved playback compatibility caveat noted.