# 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 + M0.2 implemented** β€” the auth & routing foundation, the web/PWA layer and the admin & account UI (the SPEC-002…010 slices) are built: routing Β· identity/sessions Β· the door Β· `/setup` Β· enroll/login (+ the passkey nudge) Β· roles/chain/recovery Β· the module registry Β· the web shell Β· the role-gated nav, the account page (add a passkey Β· opt-out), the admin Users page (create + invite) and the passkey banner. Next milestone: **M1 β€” org structure**. The design of record is [`PROJECT_PLAN.md`](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 ```bash # 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 β€” enable setup, then complete the wizard The **first user becomes the top admin** (the root of the chain). Setup is gated by the **`setup` config toggle** (D-045): enable it, start MyDay, complete the wizard β€” the app then writes **`setup: false`** and the instance becomes productive. ```yaml # config.yaml setup: true # enable the /setup wizard setup_code: "" # OPTIONAL second factor β€” set it if the domain is reachable by others ``` 1. **Start MyDay.** The startup banner states the mode and prints the setup URL. 2. **Open the wizard at `/setup`** (e.g. `https://myday.sechpoint.app/setup`). - `setup_code` **empty** β†’ the wizard is served directly; - `setup_code` **set** β†’ the code form comes first; enter the code printed in the startup banner. 3. **Complete the wizard** β€” domain β†’ top admin (TOTP) β†’ mail/locale. It creates the admin and writes **`setup: false`** to `config.yaml`. 4. **Restart** so the app adopts the persisted secrets: `systemctl restart myday`. While `setup: true` and setup is unfinished: - `GET /` points at the wizard, and `GET /setup` serves it (or the code form); - **every other route answers 404** β€” there is no app yet. While `setup: false` and **no admin exists**, the instance is **locked**: `GET /` and `GET /setup` show a page telling you to set `setup: true` and restart, and every other route 404. This state is **not publicly claimable** β€” a fresh instance never exposes its bootstrap by accident. > **Residual risk:** while `setup: true`, whoever reaches `/setup` first can claim the admin. > **Set `setup_code`** (or complete setup immediately) if the domain is open to others. The code is > **single-use** and the entry form is **rate-limited**. Read the code again (when one is set): ```bash journalctl -u myday # inside the container pct exec 11020 -- journalctl -u myday # from the Proxmox host ``` Only **after** the top admin exists should you put MyDay behind a reverse proxy and expose it. > **Note:** the setup wizard enrolls **TOTP**; the admin adds a **passkey** at first sign-in on the > real (https) origin, via the passkey nudge. --- ## Deployment β€” behind Zoraxy (TLS at the proxy) > **Full walkthrough:** [`docs/deploy-zoraxy.md`](docs/deploy-zoraxy.md) β€” topology Β· config Β· > the Zoraxy ProxyHost Β· the firewall rule Β· the **`setup: true` bootstrap** Β· systemd Β· > the misconfig warnings. This section is the summary. The reference deployment terminates **TLS at [Zoraxy](https://github.com/tobychui/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). - **Proxy** β€” Zoraxy terminates TLS and forwards to `http://: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 it keeps the bare, non-`Secure` cookie. 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. ```bash # on a fresh instance: enable setup, start MyDay, then open the domain at /setup # config.yaml: setup: true # (optional) setup_code: "…" # a second factor; the printed code comes first sudo systemctl restart myday # read the mode / setup code in the log: journalctl -u myday # inside the container pct exec 11020 -- journalctl -u myday # from the Proxmox host ``` --- ## 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`](PROJECT_PLAN.md) | the plan & decision log of record | | [`AGENT.md`](AGENT.md) | instructions for AI agents working here | | [`docs/specs/`](docs/specs/) | implementation specs | | [`docs/deploy-zoraxy.md`](docs/deploy-zoraxy.md) | the Zoraxy deployment guide (D-043) | | [`config.example.yaml`](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.