Skip to content

Fix: sign-in was impossible on plain-HTTP self-hosted installs - #6

Merged
BleakMidwinter90 merged 1 commit into
mainfrom
fix/lan-sessions
Aug 10, 2026
Merged

Fix: sign-in was impossible on plain-HTTP self-hosted installs#6
BleakMidwinter90 merged 1 commit into
mainfrom
fix/lan-sessions

Conversation

@BleakMidwinter90

Copy link
Copy Markdown
Owner

Two bugs, both found by running the app from a phone on a home network. Neither was caught by the test suite, and both broke the project's primary deployment path while leaving the app looking perfectly healthy.

1. The session cookie was Secure on plain HTTP

secure: process.env.NODE_ENV === 'production'.

Browsers silently discard a Secure cookie on an insecure origin. The primary way anyone runs chorely is a container on their home network, reached at something like http://192.168.1.20:3000 — so in production, over HTTP, the session cookie was set and immediately thrown away.

The symptom is genuinely confusing: pages render fine, then every single action reports Not signed in. Nothing points at cookie flags.

NODE_ENV was the wrong thing to key off. The flag now follows the request's actual protocol via x-forwarded-proto, so it's off on a home network and on behind a TLS-terminating proxy, with COOKIE_SECURE=1|0 to override for proxies that don't send the header.

2. output: 'standalone' silently broke next start

PR #4 set this unconditionally to keep the Docker image small. Next warns about the incompatibility, but the line is easy to lose in build output — and the failure mode is the same shape as bug 1: the server boots, serves pages, and then fails on Server Actions.

That meant npm run build && npm start handed everyone a broken app. Standalone is now opt-in behind BUILD_STANDALONE, which the Dockerfile sets and nobody else needs.

Why this matters more than the fix

Both bugs were invisible to CI. The tests exercise the domain and the service layer, and the Docker job checks that the container serves / — which it did, because rendering was never broken. What nothing covered was a real session doing a real mutation over a real network.

The cookie decision is now extracted into isSecureRequest() and tested directly: the bare LAN case, proxy chains like https,http, casing, and the override. It's a handful of trivial-looking assertions guarding a failure that makes every self-hosted install unusable.

Verified

Not just "tests pass". Against a real device over LAN HTTP:

  • joined a household and held the session across requests
  • loaded the agenda
  • posted the complete action → status done, credited to the right person, effort awarded, ledger updated
  • posted the skip action → status skipped, nobody credited, next occurrence rescheduled
  • server log clean, zero errors

Also adds scripts/demo-seed.mjs — a three-person household with a few weeks of deliberately uneven history. It's what made this reproducible, and it's what anyone evaluating the project needs, since an empty app doesn't demonstrate a balance score.

119 tests passing, lint clean, typecheck clean.

Two bugs, both reported from an actual phone on a home network. Neither
was caught by the test suite, and both broke the project's primary
deployment path while the app still looked fine.

1. The session cookie was marked Secure whenever NODE_ENV was production.
   Browsers silently discard a Secure cookie on an insecure origin, so
   every install reached over plain HTTP - http://192.168.1.20:3000, which
   is exactly how someone runs this on a NAS or a Pi - could render pages
   but never hold a session. The symptom is bizarre: pages load, then every
   action reports 'Not signed in'. The flag now follows the request's real
   protocol via x-forwarded-proto, with COOKIE_SECURE to override.

2. output: 'standalone' was set unconditionally for the Docker image, and
   it silently breaks 'next start'. So 'npm run build && npm start' gave
   everyone an app that booted, served pages, and failed on every Server
   Action. Next prints a warning about this that is easy to miss in build
   output. Standalone is now opt-in via BUILD_STANDALONE, which the
   Dockerfile sets and nobody else needs.

The cookie decision is extracted into isSecureRequest() so it can be
tested, covering the LAN case, proxy chains and the override. That is the
part worth guarding: getting it wrong makes every self-hosted install
unusable in a way that looks like a bug anywhere except here.

Also adds scripts/demo-seed.mjs, which is what made this reproducible.

Verified against a real device over LAN HTTP: joined a household, loaded
the agenda, and posted both the complete and skip actions. Completion
credited the right person and updated the ledger; skip rescheduled without
crediting anyone. Server log clean.

119 tests passing.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@BleakMidwinter90
BleakMidwinter90 merged commit d68df81 into main Aug 10, 2026
2 checks passed
@BleakMidwinter90
BleakMidwinter90 deleted the fix/lan-sessions branch August 10, 2026 00:53
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.

1 participant