No description
Find a file
Claus Lohmar c7229e23ff feat(setup): gate setup with the config toggle and an optional code
D-045 — the setup gate is now `config.setup`, not the peer/SSH tunnel:

- setup: true      -> /setup serves the wizard (the code form first when
                      `setup_code` is set, via the existing D-044 gate);
                      GET / points at the wizard; every other route -> 404.
- setup: false + no admin -> LOCKED: GET / and /setup render an "enable
                      setup" boilerplate; every other route -> 404; the
                      wizard is not claimable.
- completion       -> writes `setup: false` and creates the admin.

`setup_code` is now OPTIONAL: blank => Gate.Required() false => the toggle
alone admits; set => required (reusing the unlock cookie/fingerprint/
redaction/rate-limit). The peer/tunnel gate is retired: `httpx.
SetupRequestAllowed` and `httpx.IsLoopbackIP` are deleted; the XFF/XFP
handling is kept for the client IP and the Secure/__Host- cookies + HSTS
(A6/A11/D-043). The !bootstrapped() fail-safe becomes the locked predicate,
latched at startup.

New door seams: Deps.LockedMode and SetupGate.{Required,CodeForm,Landing,
Locked}; the Gate renders three first-run pages. The startup banner states
the mode and prints the code only when required.

Tests cover the three states, the boilerplate, the code optionality and the
unlock mechanics; live-smoked end to end (locked / wizard / code form /
full bootstrap -> setup: false).
2026-10-15 06:47:33 +00:00
.opencode chore: bootstrap MyDay repository 2026-10-13 11:52:30 +00:00
cmd/myday feat(setup): gate setup with the config toggle and an optional code 2026-10-15 06:47:33 +00:00
deploy docs: add the Zoraxy deployment guide and systemd unit 2026-10-15 06:15:41 +00:00
docs docs: document the setup-code first run for the LXC/Zoraxy topology 2026-10-15 06:33:16 +00:00
internal feat(setup): gate setup with the config toggle and an optional code 2026-10-15 06:47:33 +00:00
.gitignore chore(gitignore): ignore only the root build binary, not the cmd/myday source 2026-10-14 10:53:46 +00:00
AGENT.md docs: add the project plan, M0/M0.1 specs, agent guide and README 2026-10-14 10:53:44 +00:00
config.example.yaml feat(setup): gate setup with the config toggle and an optional code 2026-10-15 06:47:33 +00:00
go.mod chore(go): add the Go module manifest 2026-10-14 10:53:46 +00:00
go.sum chore(go): add the Go module manifest 2026-10-14 10:53:46 +00:00
PROJECT_PLAN.md docs: document the setup-code first run for the LXC/Zoraxy topology 2026-10-15 06:33:16 +00:00
README.md docs: document the setup-code first run for the LXC/Zoraxy topology 2026-10-15 06:33:16 +00:00

MyDay

A self-hosted employee time, absence & reporting app by Sechpoint. Delivered as a PWA for mobile and desktop. Single-tenant — one company per instance.

Status: 🚧 M0 + M0.1 implemented — the auth & routing foundation and the web/PWA layer (the SPEC-002…009 slices) are built: routing · identity/sessions · the door · /setup · enroll/login (+ the passkey nudge) · roles/chain/recovery · the module registry · the web shell. Next milestone: M1 — org structure. The design of record is PROJECT_PLAN.md.


What it will do

Area
Org structure users, the manager hierarchy, departments, positions
Time presence and timesheets (a per-user mode)
Absence leave requests, approvals, balances
Reporting rollups and aggregations

Built as one Go binary: a small core (MyDay.auth — identity, sessions, the door) plus drop-in modules.


Running it

# 1. configure
cp config.example.yaml config.yaml     # then edit; config.yaml is gitignored
# 2. build
go build -o myday ./cmd/myday
# 3. run
./myday

⚠️ First run — the setup code

The first user becomes the top admin (the root of the chain). MyDay bootstraps through a /setup wizard that is gated — never first-visitor-wins (D-025/D-044). On first start it prints a one-time 128-bit SETUP CODE in its startup log:

======================================================================
  MyDay is NOT set up yet — no admin exists.
======================================================================
  Open the setup wizard at:
      https://myday.sechpoint.app/setup
      SETUP CODE:  9f3a-1b2c-…

Open the domain (or http://localhost:8080) and enter that code at /setup. Until then every page is a friendly first-run page — never a bare 404 — and every other route answers 404. Read the code again with:

journalctl -u myday                          # inside the container
pct exec 11020 -- journalctl -u myday        # from the Proxmox host

Why a code, not a tunnel? MyDay's reference deployment is an LXC container behind Proxmox + Zoraxy. An SSH tunnel is impractical there: pct enter/SSH lands on the container gateway, not loopback, so a tunnel to 127.0.0.1:8080 cannot be formed. The setup code closes first-visitor-wins instead. A direct SSH tunnel to the app's loopback port still works as a second gate and needs no code (D-025 gate a).

Note: the code is single-use and the entry form is rate-limited. Complete the wizard, then restart so the app adopts the config.yaml the wizard wrote.

Bonus: to use the tunnel gate, reach the app over localhost / 127.0.0.1 — only a loopback origin counts as a secure context for the browser's WebAuthn dialogs. Over plain http the cookies are sent without Secure (required by the tunnel bootstrap); behind the proxy (https) they are hardened — see Deployment.

Only after the top admin exists should you put MyDay behind a reverse proxy and expose it.


Deployment — behind Zoraxy (TLS at the proxy)

Full walkthrough: docs/deploy-zoraxy.md — topology · config · the Zoraxy ProxyHost · the firewall rule · the first-run setup code (no tunnel needed) · systemd · the misconfig warnings. This section is the summary.

The reference deployment terminates TLS at Zoraxy at 10.1.0.10; the app itself speaks plain HTTP. Config (config.example.yaml → config.yaml, D-043):

  • App — server.listen: "0.0.0.0:8080"; the app binds any address (there is no setup-mode bind refusal, D-025 revised).
  • Proxy — Zoraxy terminates TLS and forwards to http://<app-host>:8080, setting X-Forwarded-For and X-Forwarded-Proto.
  • trusted_proxies — ["10.1.0.10"] (Zoraxy). The app honours X-Forwarded-Proto from a trusted proxy to decide a request is https, and then sets the session cookie Secure + __Host- and sends HSTS. Over plain http (the tunnel) it keeps the bare, non-Secure cookie so the bootstrap works. A wrong value fails silently — no Secure cookie, no HSTS.
  • base_url — https://myday.sechpoint.app: the public origin, used in invite/recovery links.
  • Firewall — :8080 MUST be reachable only from 10.1.0.10. Anything else that can reach the port bypasses the proxy and its TLS; the app would then serve plain HTTP to that client.
# read the one-time setup code, then open the domain and enter it at /setup
journalctl -u myday                          # inside the container
pct exec 11020 -- journalctl -u myday        # from the Proxmox host
# (or, for the no-code tunnel gate: ssh -L 8080:127.0.0.1:8080 user@server)

What /setup writes — and the restart

The wizard persists config.yaml (setup: false, the base URL, mail/locale, and the secrets) next to the loaded file — never over the committed config.example.yaml. If the file is not writable it prints the values for you to save by hand, and setup still completes.

The secrets are generated at startup when a value is still CHANGE_ME…, so the wizard's ceremony is never signed with a known key. The persisted file then holds those same values — so a restart adopts them and the admin's TOTP secret (sealed with the generated key) keeps working. Boot a productive instance from the written config.yaml, not from the example: config.example.yaml's placeholders fail closed in productive mode by design.


Documentation

PROJECT_PLAN.md the plan & decision log of record
AGENT.md instructions for AI agents working here
docs/specs/ implementation specs
docs/deploy-zoraxy.md the Zoraxy deployment guide (D-043)
config.example.yaml configuration template

Security

  • Secrets live in config.yaml (gitignored) — never commit them.
  • Auth is Passkey (primary) + TOTP (fallback).
  • See PROJECT_PLAN.md §10 for the deferred-security register and its beta gate.

License

TBD.