Skip to content

First-run bootstrap writes .env to dist/ instead of project root, causing AUTH_TOKEN crash #174

@tommygents

Description

@tommygents

Environment

  • freshell v0.6.0 (git tag)
  • Windows 11 (native, not WSL)
  • Node 22.12.0
  • Fresh clone + npm install + npm run build + npm run start

Summary

On a clean first run (no .env exists yet), the server crashes with:

Error: AUTH_TOKEN is required. Refusing to start without authentication.

...even though the bootstrap module did successfully generate the token and write a .env file. The problem is it wrote the file to the wrong directory.

Root Cause

server/bootstrap.ts lines 308-311 compute the project root relative to the current file:

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const projectRoot = path.resolve(__dirname, '..')
const envPath = path.join(projectRoot, '.env')

In the source tree, server/bootstrap.ts is one level below the project root, so path.resolve(__dirname, '..') correctly resolves to the project root.

After tsc compiles to dist/server/bootstrap.js, the file is two levels below the project root. So .. resolves to dist/, not the project root:

Context __dirname resolve(.., '..') Correct?
Source (server/bootstrap.ts) <root>/server <root> Yes
Built (dist/server/bootstrap.js) <root>/dist/server <root>/dist No

Meanwhile, dotenv/config (imported on the very next line in index.ts) loads .env from process.cwd(), which is the actual project root. So:

  1. Bootstrap writes .env to dist/.env
  2. dotenv/config looks for .env in CWD (project root) -- doesn't find it
  3. process.env.AUTH_TOKEN is undefined
  4. validateStartupSecurity() throws

On subsequent runs, dist/.env already has a token so bootstrap says "skipped", and the crash repeats unless the user manually copies the file.

Steps to Reproduce

git clone --branch v0.6.0 https://github.com/danshapiro/freshell.git
cd freshell
npm install
npm run build
npm run start   # crashes with AUTH_TOKEN error

Check: .env does NOT exist in the project root, but dist/.env does.

Suggested Fix

Change the projectRoot calculation in server/bootstrap.ts to go up two levels (matching the compiled output path), or better yet, use process.cwd() to match what dotenv/config uses:

// Option A: Use CWD (consistent with dotenv/config)
const projectRoot = process.cwd()

// Option B: Go up the correct number of levels from dist/server/
const projectRoot = path.resolve(__dirname, '..', '..')

Option A is more robust since it doesn't depend on the output directory structure, and it guarantees bootstrap and dotenv agree on where .env lives.

If Option B is preferred (to support being imported from unusual locations), consider detecting whether the file is running from a dist/ subdirectory and adjusting accordingly.

Workaround

After the first failed run, copy the generated file to the project root:

cp dist/.env .env
npm run start   # works now

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions