Quick Start
This walkthrough takes you from nothing to a page that calls an API with a server-side key. Pick your platform as you go — the steps are otherwise identical.
All values shown are placeholders. Replace them with your own.
Step 1 — Create the project
mkdir myappcd myappenvgo initmkdir myapp && cd myappenvgo initmkdir myapp && cd myappenvgo initenvgo init writes five files: .env, .env.example, index.html,
.gitignore, and README.md.
Step 2 — Put a real key in .env
envgo init filled .env with placeholders (including commented MODE_PUBLIC/CONFIG). Open it and replace them:
MY_SECRET=some-real-valueOPENAI_API_KEY=sk-your-real-key-hereHOST=127.0.0.1PORT=8080# MODE_PUBLIC=false # true/public = public mode (needs envgo.routes.json)# CONFIG=envgo.routes.json # explicit path, overrides MODE_PUBLICUncomment MODE_PUBLIC=true to switch to public mode without flags. See CLI Commands.
HOST and PORT are different from the others
The first two keys become secrets that stay server-side. HOST and PORT do
not — they tell envgo run dev where to listen, so editing them changes how
you reach the server.
| Key | Value shown | What changing it does |
|---|---|---|
HOST | 127.0.0.1 | Interface to bind. 127.0.0.1 accepts connections from this machine only. 0.0.0.0 also accepts other devices on your network — or just add --qr, which sets this for you |
PORT | 8080 | Port to listen on, and the port the browser opens. Setting PORT=3000 means you get http://127.0.0.1:3000/ |
Change the port and run it again to see it take effect:
PORT=3000envgo run dev[envGo] using PORT from .env: 3000[envGo] envGo running -> http://127.0.0.1:3000/The browser opens on the new port, and the startup log confirms which value was picked up.
Three rules apply:
- They are read only by
envgo runandenvgo run dev. Public mode (--config …) ignores them completely, and a bareenvgonever starts a server at all — it just prints help. - A command-line flag wins over the file.
envgo run dev --port 4000uses 4000 and ignoresPORTin.env. - Lowercase keys work too (
host,port), and a leading colon is stripped, soPORT=:3000is also valid.
Full details are in CLI Commands.
Open the file
notepad .envSave as UTF-8. Do not use PowerShell’s > redirection to write .env —
Windows PowerShell 5.1 writes UTF-16, which the parser cannot read.
nano .envnano .envStep 3 — Start the server
The command is the same on every platform:
envgo run devrun dev reads HOST and PORT from .env and opens your browser at
http://127.0.0.1:8080/.
Prefer not to auto-open the browser? Use envgo run instead.
Watch the startup output — it tells you what loaded:
[envGo] loaded 4 variables from .env[envGo] envGo running -> http://127.0.0.1:8080/If the port is taken, envGo automatically tries the next one up (up to 20 attempts), so check the printed address.
Try it on your phone
Add --qr and envGo opens your network interface and prints a QR code you can
scan:
envgo run dev --qr[envGo] --qr: binding all interfaces so other devices on this network can connect[envGo] envGo running -> http://127.0.0.1:8080/[envGo] envGo network -> http://192.168.1.20:8080 (same network only)Scan the code with your phone camera and the site opens. The phone must be on the same Wi-Fi. If it scans but will not load, the port is probably blocked by the host firewall. Full details and troubleshooting: Sharing with your phone.
Step 4 — Check which keys are visible
envgo init created a page containing:
<h1>Hello from envGo!</h1><div id="MY_SECRET"></div><script src="/__env.js"></script>Because MY_SECRET exists in .env, the div is replaced at runtime with:
✓ MY_SECRET — Success (env exists, value hidden)If the id does not match any key, you get a red banner instead:
envGo — ID not match Key: 'MY_SECRETS' not found in .env.Did you mean 'MY_SECRET'? Check .env for 'MY_SECRET'The value of MY_SECRET is never sent to the browser. A message like this
confirms a key is present, nothing more.
Step 5 — Call an API with the key
This is a complete, working example. Every name and endpoint below is illustrative — adapt it to your API.
It takes two files that have to agree with each other. Here is the .env again
so you can see the pairing — it is the same file you edited in Step 3 (plus optional commented mode lines):
MY_SECRET=some-real-valueOPENAI_API_KEY=sk-your-real-key-hereHOST=127.0.0.1PORT=8080# MODE_PUBLIC=falseThe two files must line up in three places:
In index.html | Must exist in .env |
|---|---|
<div id="MY_SECRET"> | MY_SECRET |
<div id="OPENAI_API_KEY"> | OPENAI_API_KEY |
Bearer {OPENAI_API_KEY} | OPENAI_API_KEY |
If an id has no matching key you get a red ID not match Key banner. If a
{…} placeholder has no matching key the proxy call fails with
{"error":"missing env variable: OPENAI_API_KEY"}.
Also note that the button and output elements deliberately have no id, so
the presence checker leaves them alone.
<!DOCTYPE html><html><head> <meta charset="utf-8" /> <title>My App</title> <style> body { font-family: system-ui, sans-serif; max-width: 700px; margin: 3rem auto; } button { padding: 10px 18px; font-size: 15px; cursor: pointer; } pre { background: #0f172a; color: #7dd3fc; padding: 1rem; border-radius: 10px; overflow: auto; } </style></head><body> <h1>Hello from envGo!</h1>
<!-- Variable presence check --> <div id="MY_SECRET"></div> <div id="OPENAI_API_KEY"></div>
<!-- No id: these are not variables --> <button>Ask the API</button> <pre>Click the button…</pre>
<script src="/__env.js"></script> <script> const button = document.querySelector("button"); const output = document.querySelector("pre");
button.addEventListener("click", async () => { output.textContent = "Loading…"; try { // 1. Fetch the per-process session token const token = await fetch("/__envgo_token").then((r) => r.text());
// 2. Proxy the call; {OPENAI_API_KEY} is substituted server-side const res = await fetch("/proxy", { method: "POST", headers: { "Content-Type": "application/json", "X-EnvGo-Token": token, }, body: JSON.stringify({ target_url: "https://api.openai.com/v1/chat/completions", method: "POST", headers: { Authorization: "Bearer {OPENAI_API_KEY}" }, body: { model: "gpt-4", messages: [{ role: "user", content: "Say hello in one sentence." }], }, }), });
output.textContent = JSON.stringify(await res.json(), null, 2); } catch (err) { output.textContent = "Error: " + err.message; } }); </script></body></html>Two things about that code:
"Bearer {OPENAI_API_KEY}"is a placeholder. The literal text{OPENAI_API_KEY}is what the browser sends; the Go process swaps it for the real value before forwarding.- The host must be allowlisted. Restart the server with:
envgo run dev -a api.openai.comWithout --allow, envGo prints a warning on startup and refuses every proxy
request.
Step 6 — Watch hot reload
Leave the server running and change .env from another terminal:
Add-Content -Path .env -Value "NEW_KEY=hello" -Encoding utf8Reading it back with the same encoding matters more than you might expect —
Windows PowerShell 5.1’s default redirection writes UTF-16, which corrupts the
file. If a key ever shows up garbled, rewrite .env from Notepad saved as UTF-8.
PowerShell 7 handles UTF-8 by default.
echo "NEW_KEY=hello" >> .envecho "NEW_KEY=hello" >> .envWithin about 1.5 seconds the server logs:
[envGo] hot-reloaded 5 variables from .envNo restart needed.
Step 7 — Look at the dashboard
http://127.0.0.1:8080/__envgo_dashboardIt lists the loaded variable names, recent proxy requests with status and
duration, and any errors. Values are never shown. The same data endpoint is what
/__env.js reads to learn the variable names.
Complete command sequence
Assumes envGo is installed and on your PATH. Copy-paste the relevant block:
On Windows use PowerShell; on macOS and Linux use a POSIX shell.
# 1. Create a projectmkdir myappcd myappenvgo init
# 2. Edit .env with a real keynotepad .env
# 3. Run, allowing one API hostenvgo run dev -a api.openai.com# 1. Create a projectmkdir myapp && cd myappenvgo init
# 2. Edit .env with a real keynano .env
# 3. Run, allowing one API hostenvgo run dev -a api.openai.com# 1. Create a projectmkdir myapp && cd myappenvgo init
# 2. Edit .env with a real keynano .env
# 3. Run, allowing one API hostenvgo run dev -a api.openai.comCommon errors
These are identical on every platform:
| Response | Meaning | Fix |
|---|---|---|
{"error":"missing env variable: OPENAI_API_KEY"} | The key is not in .env | Add it, or fix the spelling |
{"error":"unauthorized"} | Token missing or stale | Fetch a fresh /__envgo_token |
{"error":"target host not in allowlist: api.openai.com"} | Host not allowed | Add it to --allow |
{"error":"only HTTPS targets are allowed"} | Target uses http:// | Use https:// |
Red banner reading ID not match Key | The element id does not match any key | Correct the id or the .env key |
Platform-specific installation problems — command not found, wrong
architecture, Gatekeeper, SmartScreen — are covered in
Installation.
Where to go next
- Add more routes and turn on rate limiting → Public Mode
- Understand every config field → Configuration
- Deploy it → Deploy to VPS
- Learn the security layers → Security Model