Skip to content

Project Structure

envgo init is the scaffolding command. It writes a runnable project in one step so a new dev can go from mkdir myapp to envgo run dev without reading the source. This page documents exactly what is created, what each file contains and does, and every key that can go in .env according to main.go, envstore and gateway.

What the command does

Source: main.go:doInit — no network, no dependencies, five os.WriteFile calls.

Terminal window
# In the current directory
envgo init
# In a new subdirectory (fails if directory already exists)
envgo init -name myapp
# → myapp/.env, myapp/.env.example, myapp/index.html, myapp/.gitignore, myapp/README.md

Behaviour:

  • If -name is given and myapp/ exists, init exits with directory already exists — it never merges.
  • Otherwise it overwrites index.html, README.md, .gitignore, .env, .env.example in the target without asking. Back up first.
  • No flag is needed for envgo run dev afterwards — .env already holds HOST/PORT defaults.

Folder after init

after envgo init
myapp/
├── .env # 0600 — your real secrets (never commit)
├── .env.example # 0644 — safe template to commit
├── index.html # 0644 — minimal page with presence check
├── .gitignore # 0644 — ignores .env + keys
└── README.md # 0644 — quick-start notes
# optional, you add later:
├── envgo.routes.json # public-mode gateway config
└── deploy/ # generated by envgo deploy

0600 vs 0644: only .env is 0600 (owner read/write) so other users on the same machine cannot read it. Everything else is 0644.

File by file

.env — the only secret store

Default content (main.go:envContent):

.env (generated)
# envGo project
# Copy to .env and fill in real values. NEVER commit the real .env.
MY_SECRET=change-me
HOST=127.0.0.1
PORT=8080
# MODE_PUBLIC=false # true/public = public mode (needs envgo.routes.json), false/local = local mode
# CONFIG=envgo.routes.json # optional: explicit routes file (overrides MODE_PUBLIC)
LineKindWhat it does
MY_SECRETplaceholder secretExample. Replace with your real key. Any name matching [A-Za-z_][A-Za-z0-9_]* can be used as {MY_SECRET} in JS or <div id="MY_SECRET">.
HOSTinfra, not secretWhere envgo run binds. 127.0.0.1 = local only. 0.0.0.0 = LAN. Only read by envgo run / run dev when --host is default. Not redacted in logs.
PORTinfra, not secretPort envgo run listens on. 8080http://127.0.0.1:8080/. PORT=:3000 also works. Not redacted.
# MODE_PUBLICinfra, not secretCommented false by default = local mode. Uncomment true/public/1 to enable public mode without --config flag. Aliases: MODE, PUBLIC_MODE, ENVGO_MODE. Values true/1/yes/public = public, false/0/no/local = local. Flag --config always wins. See CLI Commands.
# CONFIGinfra, not secretCommented by default. Explicit routes path (envgo.routes.json, custom/myroutes.json). Aliases: ENVGO_CONFIG, ROUTES, ROUTES_PATH, CONFIG_PATH. Wins over MODE_PUBLIC. Relative paths resolved against .env dir and --dir.

HOST/PORT/MODE_PUBLIC/CONFIG are never treated as secrets — they appear in logs and are ignored in .env.example sync and logger redaction (envstore.go:filtered).

What else can you put in .env? Anything you want — every other key is a secret:

.env — add your own
MY_SECRET=change-me
OPENAI_API_KEY=sk-live-abc123
GEMINI_API_KEY=secret2
STRIPE_KEY=sk_test_...
DATABASE_URL=postgres://...
# any non-empty key is accepted, but only [A-Za-z_][A-Za-z0-9_]* can be used as {PLACEHOLDER}

Rules (envconfig):

  • Blank lines and whole-line # comments ignored.
  • export KEY=val accepted (prefix stripped).
  • Missing = or empty key (=val) → load fails with line number.
  • Quotes: matching " or ' around the value is stripped.
  • ${VAR} expansion: BASE=https://${HOST}/v1 resolves HOST from earlier lines, then OS env.
  • Spaces inside value are preserved.

Hot reload: edit .env while running — envstore.Watch polls mtime/size every 1.5s and reloads without restart (main.go:store.Watch). Removing a key really removes it.

.env.example — safe to commit

Same content as .env (0644), but values are placeholders. Commit this so teammates know which keys to fill. main.go:checkEnvExampleSync warns if names diverge, never values.

.env.example
MY_SECRET=change-me
HOST=127.0.0.1
PORT=8080
# MODE_PUBLIC=false
# CONFIG=envgo.routes.json

index.html — presence check, not the secret

Default (main.go:htmlContent):

index.html (generated)
<!DOCTYPE html>
<html>
<head><title>myapp — envGo</title></head>
<body>
<h1>Hello from envGo!</h1>
<div id="MY_SECRET"></div>
<script src="/__env.js"></script>
</body>
</html>
  • <div id="MY_SECRET"> is not the value. /__env.js fetches GET /__envgo_dashboard/data (names only) and replaces it with ✓ MY_SECRET — Success (value hidden) or ✗ ID not match Key + red banner (Levenshtein suggestion ≤3). Works for any <div id="YOUR_KEY">.
  • Skipped ids: out, chatBtn, btn, prompt, askBtn — avoid id on non-variable elements.
  • HOST/PORT/MODE_PUBLIC do not need an element — they are not secrets.

.gitignore — prevents accidental commit

.env
*.key
*.secret

.env is always ignored. Add envgo.routes.json there if it holds internal URLs you prefer not to commit (optional).

README.md — local quick-start

Generated README.md repeats envgo run dev, envgo -h, --tls, --config, and deploy. It is a stub — not a source of truth, the docs at envgo.dev are.

Complete .env reference (from Go source)

KeyTypeUsed byFlag that overridesSecrets?
MY_SECRET, OPENAI_API_KEY, …any secret{NAME} in proxy/gateway HOST check, dashboardyes — redacted in logs (logger.Redact)
HOST / hostinfraenvgo run only, when --host is default 127.0.0.1--hostno
PORT / portinfraenvgo run only, when --port is default 8080--port / -pno
MODE_PUBLIC / PUBLIC_MODE / ENVGO_MODE / MODEinfra booleanstartup mode switch when --config absent--config/-cno
CONFIG / ENVGO_CONFIG / ROUTES / ROUTES_PATH / CONFIG_PATHinfra pathexplicit gateway file when --config absent--config/-cno

Precedence: flag --config > CONFIG keys > MODE_PUBLIC keys > default local. HOST/PORT only apply to envgo run; plain envgo --port ignores them intentionally (common systemd pitfall).

Typical edits for a new dev

.env — after onboarding
MY_SECRET=real-value-from-teammate
OPENAI_API_KEY=sk-...
HOST=127.0.0.1
PORT=3000
# keep local for dev, flip to true when testing public gateway:
# MODE_PUBLIC=true
Terminal window
envgo run dev -a api.openai.com # local with allowlist
# or
MODE_PUBLIC=true envgo run dev # public via .env, no flag
envgo --config envgo.routes.json --allow api.openai.com # explicit

What to commit

Terminal window
git add .env.example index.html .gitignore README.md
# never: git add .env

See also: Environment Variables, CLI Commands, How It Works.