Module 01Neill's Vibe · Tier 1 · Basic Vibe Coding~8 min

Where secrets actually live

Works on my laptop. Dead on the server. No error. The single most common way the machine lies to you.

01 · the scene

"I set the keys. I'm sure I did."

The app ran perfectly on the laptop. Moved it to the droplet to run on a schedule. It just… stopped. No crash. No error in the obvious place. Nothing.

droplet — 5:00am cron — March
~/app $ node bot.js
starting…
Error: 401 — OAuth token has expired / not provided
(works fine on the laptop. same code. same commit.)
Neill51% · the terrain

"It worked on my laptop. On the droplet — dead. I was sure the keys were fine; I'd set them. So why did it work here and not there?"

Claude49% · the execution

"You had set them — in the laptop's .env, which is gitignored and never travels with the code. I should've asked where faster. The keys didn't carry. That's the whole lesson."

02 · the why

Some values can't live inside your code.

Two kinds, specifically. Secrets — your API keys — because if they're in the code they go into git and leak the moment it's shared. And things that change by place — the database is different on your laptop vs. production. Hard-code either and you're stuck choosing between leaking secrets or editing code every time you move.

⌥

The variable

named, outside the code

A value the code asks for by name at runtime. The environment hands it over — it never lives in the code.

⊠

.env

gitignored, per-machine

Holds the values locally. Never committed. So it does not travel with the code to a new machine.

⇲

Every new place

set them fresh

Laptop, droplet, Vercel — each needs its own copy. That's the feature, not the bug.

The model: your code is a recipe that says "add salt from the jar labeled SALT." It never hard-codes the amount — it reads the jar. Different kitchens stock the jar differently. That's why the same code runs safely everywhere: the values aren't inside it.

03 · predict it

Same code. Works here, dead there. Why?

It runs on the laptop, fails on the droplet, no real error. You're certain you set the keys. What happened?

Not it. The code is identical and the network's fine — focus on what didn't make the trip from laptop to droplet.
Exactly. .env is gitignored, so when the code lands on a new machine the code arrives but the secrets don't. The droplet ran with empty keys → 401. Every new environment needs its own copy. (And nastier cousin: a value set with a hidden trailing newline reads as "true\n" ≠ "true" — silently false, prod only.)
04 · the walkthrough

The safe path, in four frames.

.env (gitignored)
step 1Keep secrets in .env locally. Confirm it's in .gitignore — never commit it.
process.env.KEY
step 2Code reads values by name at runtime — the key never appears in the code itself.
set on the droplet too
step 3New machine? Set the same vars there. The .env didn't travel; you bring it.
no NEXT_PUBLIC_ on secrets
step 4NEXT_PUBLIC_ bakes a value into the browser bundle — never put a secret behind it.
05 · prove it · gate

Teach it back to unlock the payoff.

Same rule as always: answer it right, or the module isn't done.

Why is it dangerous to hard-code your API key into the app instead of reading it from an env var?
// one correct answer unlocks Module 01
Not the load-bearing reason. Think about where a hard-coded key ends up the moment you commit.
That's it. Safe isn't about the machine being "local" — it's about the key being out of the code. Your key on Vercel isn't on your laptop at all, and it's perfectly safe there. Module unlocked. ↓
the durable idea

Safe ≠ local. Safe = not in the code. The environment hands the key to the code wherever it runs — so every new place needs its own.

06 · factory payoff
⊘ locked — pass the teach-back to claim this piece of the factory

The tool that would've saved me three hours.

A real check-env.js that fails loud before your app starts if a required variable is missing — or has a hidden trailing newline (the exact bug that bit me three times in one day). Plus a new-machine checklist so the keys never silently fail to carry again.

✓ Module 01 complete