Building the vault — a private layer on a public site
Why the vault exists, what's inside, and the design choices that kept it simple
- vault
- auth
- storage
Update, 2026: The model described below is superseded. The vault is now a folder tree with the terminal as its admin surface. See Turning the vault into a filesystem for what changed and why.
The problem
I wanted a place to put things that aren't for everyone.
My public site is the front — projects, a bit of who I am, some links to reach me. That's the version of me that goes on a resume. But there's a whole other layer of stuff I actually want to keep — climbing photos I like, one-line notes I want to remember, journal entries I write when I need to think out loud. None of that belongs on the homepage. It also doesn't belong in a random Notes app that I'll lose access to in six years when I switch phones or cloud providers.
So I built the vault. Same domain, same database, same design language — but gated. Nobody sees a thing without the code.
The approach
Two auth boundaries, not one. The admin session (email + password + TOTP) already gates /admin. But the vault isn't just for me editing content — it's for me reading my own stuff on any device without having to sign in every time. So the vault gets its own separate cookie, keyed off a simple access code stored in an env var.
That means the flow is:
- Visit
/vaultfrom anywhere → prompt for the code - Type it → vault-session cookie is set, valid for a long time
- Now
/vault/homeand all its sub-pages render normally
The admin session isn't required to view the vault. It IS required to edit it — everything lives under /admin/vault. Two doors, two purposes, one shared UI once you're inside.
The data model is boring on purpose. Three tables:
vault_photos (id, filename, url, caption, taken_at, created_at)
vault_notes (id, slug, content, created_at, updated_at)
vault_journal (id, entry_date, content, created_at, updated_at)
Photos live in Vercel Blob under vault/photos/…, referenced by URL from Postgres. Notes are short — a slug and a body of markdown, nothing more. Journal entries key off a date string so I can write "2026-09-28" and it stays scoped to that day.
I resisted the urge to build tags, filters, categories, or a fancy folder system. Every feature I could add would be one more surface I'd want to design for and one more thing I might never actually use. The MVP is: photos, notes, journal, chronological. That's what I would actually reach for.
What shipped
The vault gate lives at /vault. If your cookie's not set, you see a code prompt and nothing else — no leaked page structure, no hints. The gate itself:
itscollinvo/jiggletacoexpress
Pull request #13
Once you're through, /vault/home is the entry point. From there:
/vault/home/photos— grid of uploaded images with captions and dates/vault/home/notes— short markdown entries, sorted by most recent/vault/home/journal— longer entries keyed by date
The admin side — where I actually add content — is at /admin/vault. Requires both the admin session AND handles all three types in one dashboard: a photo upload form, a note editor, a journal editor. Delete happens with a confirm dialog because I've deleted the wrong thing before.
The follow-up PR added notes + journal to the schema and wired the admin CRUD:
itscollinvo/jiggletacoexpress
Pull request #15
Public routes read directly from the DB via getVaultPhotos(), getVaultNotes(), getVaultJournal() — all force-dynamic, no static generation. The vault isn't cacheable in any interesting way and the traffic is one person, so there's no reason to bother.
What's next
A few things are on the list but deliberately not built yet:
- Search — right now I scroll. Fine at 20 notes, annoying at 200.
- Journal auto-timestamps — I type the date manually today. Wouldn't take much to pre-fill and let me override.
- Photo albums or grouping — resisting this hard until I have a use case that isn't hypothetical.
- Rate limiting on the vault gate — the admin gate has it via Upstash; the vault code prompt currently doesn't. Low priority since the code is my own but worth adding.
The vault is the piece of this site I'll interact with the most, so it'll keep evolving. What's shipped now is the shape of the thing; the next few months of using it will decide what to actually build.