REVIEW-001 triage updated (H1, H2, M1c, M1s, L3s, L1c/L2s, L8c, M2s, M3s, M2c, L1s, L5s, L4s, L7c marked FIXED; L3c, L4c, L5c, L6c stay open with the reason), and each fix is tied to its code and test. - SPEC-003: the door pipeline (XFF right→left, the per-request setup gate, the isolated hardening headers, the pinned setup module), §6 the `__Host-` cookie and the startup-generated signing keys, §7 loopback enforcement per request, new AC-10…AC-12, D-2 amended (session_key removed), and a new §11 that maps every REVIEW-001 finding to its fix. - SPEC-005 / SPEC-006 / SPEC-007: the same three points where they belong (the wizard's loopback + module pin, the nudge's configured-origin signal, the async /access notification ruled in). - PROJECT_PLAN §10 register: A13 re-stated as enforced (per request + pin); A2/A5/A8 re-stated as enforced where the review verified them; A3/A6/A9/A15 now enforced by this pass; A11 left open (L4c). §14 change log entries. - README: the status said "planning / pre-alpha · nothing is built" — it is stale. State M0+M0.1, note that /setup PERSISTS config.yaml and a restart adopts the generated secrets, and state the per-request loopback rule.
3.8 KiB
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 — do NOT expose it publicly
The first user becomes the top admin (the root of the chain). So the instance bootstraps
loopback-only, running a /setup wizard until that admin exists.
# on the server it listens on 127.0.0.1:8080 only (config.example.yaml)
ssh -L 8080:127.0.0.1:8080 user@server # from your workstation
# then open http://127.0.0.1:8080/setup and complete the wizard
Loopback is enforced per request, not just at bind. While in setup mode the app answers 404 to any request whose client is not loopback — so a reverse proxy must not sit in front of a fresh instance, even one bound to
127.0.0.1. Bootstrap over the SSH tunnel (above) first.
Reach the app over
localhost/127.0.0.1— not a hostname. With anhttps://base_urlthe CSRF and session cookies are markedSecure, so they are not sent over plainhttp://; and only a loopback origin counts as a trustworthy/secure context for the browser's WebAuthn dialogs. So for first run and any human testing, open the app athttp://localhost:8080orhttp://127.0.0.1:8080(via the tunnel above) — never at a bare server hostname.
Only after the top admin exists should you put MyDay behind a reverse proxy and expose it.
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 |
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.