Skip to content

fix: start the backend without a preconfigured environment - #305

Merged
dusanstanojeviccs merged 1 commit into
mainfrom
fix/backend-startup-defaults
Aug 26, 2026
Merged

fix: start the backend without a preconfigured environment#305
dusanstanojeviccs merged 1 commit into
mainfrom
fix/backend-startup-defaults

Conversation

@FrameAutomata

Copy link
Copy Markdown
Collaborator

The problem

go run ./cmd/traceway panics twice on a fresh clone:

  1. DB_TYPE has no default, so it falls through to initPostgres() — in a build whose migration runner applies SQLite-dialect migrations unconditionally
  2. PORTS falls back to 80,8082, and :80 cannot bind as a non-root user

Every entry point hits this — fresh clone, IDE run, CI, Docker — so the workaround kept getting re-invented per environment. Most recently as env exports in the Nix dev shell (#302), which only helped Nix users and silently shadowed backend/.env, because godotenv.Load calls loadFile(name, false) and never overwrites an already-set var.

Fixing it here means #302 can delete those exports entirely rather than patch them.

DB_TYPE

Defaults in config.defaultDBType, guarded by build tag: "sqlite" without transactional_pg, "" with it so that build stays byte-for-byte unchanged.

One definition, not one per call site. db_transactional_sqlite.go, db_telemetry_sqlite.go and db_telemetry_duckdb.go all test DBType != "sqlite" — my first attempt patched only the first and left a nil telemetry DB (database/sql.(*DB).conn(0x0, ...)), which is exactly the duplication this avoids. runMainDBMigrations ignores the value in the no-tags build, so migrations were never at risk.

PORTS

Ports are now bound up front rather than inside the serving goroutines, so a bind failure is known before the process reports itself started. Individual failures warn + CaptureException; only losing every port is fatal.

The old behavior was worse than "panics on :80" — it was inconsistent:

Config, as non-root Before
PORTS=80,8082 :80 fails → fatal panic
PORTS=8082,80 :80 fails → silently swallowed, runs on :8082 with no output

Only portsList[0] bound on the main goroutine; the rest panicked inside goroutines guarded by traceway.Recover(), which calls recover() and — when collectionFrameStore == nil, i.e. monitoring unconfigured — returns without reporting anything. The default 80,8082 is unchanged, since it's a documented interface and all four Dockerfiles EXPOSE 80 8082.

JWT_SECRET

Still no default, deliberately: a key committed to the repo is identical in every clone, and persisting a generated one has no clean home in the transactional_pg telemetry_ch build. It now exits 1 with the existing message plus openssl rand -hex 32 instead of burying it under a panic stack trace.

Verification

  • Only JWT_SECRET set, non-root: warns about :80, serves :8082, /version → HTTP 200, 0 panics
  • PORTS=80 alone → exit 2, could not listen on any port
  • Missing JWT_SECRET → exit 1, clean message, 0 stack frames
  • All three supported build-tag combinations compile (no-tags, transactional_pg telemetry_ch, telemetry_duckdb)
  • Full backend test suite passes

Follow-up

Once this and #301 both land, the env-var paragraph #301 adds to CLAUDE.md should shrink to just JWT_SECRETDB_TYPE and PORTS stop being things you have to set.

🤖 Generated with Claude Code

`go run ./cmd/traceway` panicked twice on a fresh clone: DB_TYPE had no
default even though the no-build-tag build is dual-SQLite, and PORTS fell
back to "80,8082" whose first entry cannot bind as a non-root user. Every
entry point hit this -- a fresh clone, an IDE run, CI, Docker -- so the
workaround kept being re-invented per environment (most recently as env
exports in the Nix dev shell, which only helped Nix users and silently
shadowed backend/.env, since godotenv.Load never overwrites a set var).

DB_TYPE now defaults in config.defaultDBType, guarded by build tag:
"sqlite" without transactional_pg, "" with it so that build stays
byte-for-byte unchanged. One definition rather than one per call site --
db_transactional_sqlite.go, db_telemetry_sqlite.go and
db_telemetry_duckdb.go all test `DBType != "sqlite"`, and patching each
was how the first attempt at this left a nil telemetry DB.

Ports are now bound up front rather than inside the serving goroutines,
so a bind failure is known before the process reports itself started.
Individual failures are tolerated with a warning and a CaptureException;
only losing every port is fatal. Previously just the first entry was
bound on the main goroutine and panicked, while the rest panicked inside
goroutines guarded by traceway.Recover(), which swallows the panic and
reports nothing at all when monitoring is unconfigured -- so the same
failure was either fatal or invisible depending on the order of PORTS.

serveHTTP (added by #300) now takes an already-bound net.Listener and
calls srv.Serve instead of srv.ListenAndServe. Its ReadHeaderTimeout /
ReadTimeout / IdleTimeout are the point of that helper and are kept
verbatim: gin's RunListener sets none of them, so binding the listener
separately had to go through the same http.Server rather than around it.

JWT_SECRET keeps having no default, deliberately: a key committed to the
repo would be identical in every clone, and persisting a generated one
has no clean home in the transactional_pg telemetry_ch build. It now
exits 1 with the existing message plus a generation hint instead of
burying it under a panic stack trace.

Verified after rebasing onto the merged #296/#297/#300: with only
JWT_SECRET set and running as non-root, the server warns about :80,
serves :8082, and reaches /version with 0 panics; a partial request is
still cut off at exactly 15s, confirming #300's slowloris timeouts
survived the change; all three build-tag combinations compile; the full
backend test suite passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FrameAutomata
FrameAutomata force-pushed the fix/backend-startup-defaults branch from 2c16c55 to 1c61827 Compare August 25, 2026 20:12
@dusanstanojeviccs
dusanstanojeviccs merged commit 6d502b1 into main Aug 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants