# Deploying NoteTrace ## Quick start (Docker Compose) ```bash git clone https://github.com/traceapps/notetrace.git cd notetrace cp .env.example .env # Edit .env, at minimum set JWT_SECRET to a long random value docker compose up -d # Open http://localhost:3004 ``` Opening the app from another device at a plain `http://` address (a LAN IP, an Unraid WebUI link)? Signing in only sticks over HTTPS, or with `INSECURE_COOKIES=1` on a trusted LAN; the sign-in and setup screens say so. `http://localhost` on the server itself works in Chrome and Firefox either way. See [Plain-HTTP LAN](https://traceapps.github.io/docs/getting-started/lan-http/). ## Image tags Every release publishes a multi-arch (linux/amd64 + linux/arm64) image to **two registries** with an identical tag set. GHCR is primary; Docker Hub is a discoverability mirror. Both are first-class; pick whichever fits. - **`ghcr.io/traceapps/notetrace`** (primary) - **`traceapps/notetrace`** on [Docker Hub](https://hub.docker.com/r/traceapps/notetrace) (mirror) Two tags are published (examples below use GHCR; swap the prefix for Docker Hub if preferred): | Tag | Updates when | Use case | |-----|--------------|----------| | `ghcr.io/traceapps/notetrace:latest` | Every stable release | Running the app | | `ghcr.io/traceapps/notetrace:dev` | Every push to `dev` branch | Leading edge, not for production | There are no version-number tags (`:1`, `:1.4`, `:1.4.0`); the ones published for the first releases stopped updating and were retired. To hold a server on one exact build, pin it by digest (`ghcr.io/traceapps/notetrace@sha256:...`; `docker inspect --format '{{index .RepoDigests 0}}' ghcr.io/traceapps/notetrace:latest` prints the one you run). ## Testing pre-release builds Two mechanisms cover pre-release testing between stable releases. ### `dev-latest` (rolling, primary) Every dev-worthy build refreshes the [`dev-latest`](https://github.com/traceapps/notetrace/releases/tag/dev-latest) GitHub pre-release. The APK is signed with the same keystore as stable releases, so it upgrades in place. Same guarantees as the Docker `:dev` tag, which auto-publishes on every push to `dev`. This is the default channel for testers who want "always the newest thing." ### Milestone `v-devNN` (occasional, pinnable) When a specific feature or fix is worth its own tester milestone (a new AI capability, a wearable integration, a big backup change), a numbered pre-release gets cut: `v1.0.4-dev01`, `v1.1.0-dev01`, etc. These get their own permanent GH release, their own tester-facing notes, but no Docker tag of their own: the server side of a milestone is the `:dev` image. `dev-latest` is refreshed to point at the same commit. Iteration numbers are zero-padded two digits for 1 through 9 (`dev01`, `dev02`, …, `dev09`) and natural two digits from 10 onward (`dev10`, `dev11`, …). No dot between `dev` and the number. That keeps the identifier inside SemVer 2.0.0 §9 and gives correct lex ordering everywhere (GitHub Tags, `gh release list`). Use numbered dev builds when reporting bugs ("I saw this on `v1.1.0-dev02`") or if you want to install a specific milestone and stay on it. Everyone else, `dev-latest` covers you. Both channels use the shared TraceApps keystore, so upgrading between them (or from either back to stable) works in place. ## Pinning Android to a specific version Sideloaded APKs don't auto-update the way docker containers do. Every version has its own APK on the [Releases page](https://github.com/traceapps/notetrace/releases): - **[Latest stable](https://github.com/traceapps/notetrace/releases/latest)** — auto-redirects to the newest stable release - **`/releases/tag/v1.0.0`** (or any version) — exact-version pin, won't change - **`/releases/tag/dev-latest`** — rolling dev channel To "auto-patch" on Android, redownload from the latest URL whenever you want to upgrade. Because APKs are signed with the same shared key across every stable version and dev-latest, in-place upgrade to a newer version is always allowed. Downgrading requires an uninstall. ## Environment variables See [.env.example](.env.example) for the full list. Required for any multi-user deploy: - `JWT_SECRET` — long random string (64+ chars). Rotating this invalidates every existing session. Recommended: - `RECOVERY_TOKEN` — used by the login-page lockout-recovery flow. - `TOKEN_ENC_KEY` — at-rest key for OIDC client secrets. Defaults to a key derived from `JWT_SECRET`. Only for plain HTTP: - `INSECURE_COOKIES`: Set `1` to send the sign-in cookie over plain HTTP too. The cookie is HTTPS-only by default, so at a plain `http://` address other than `localhost` (a LAN IP, an Unraid WebUI link) the browser drops it and signing in loops back to the sign-in page; the sign-in and setup screens say so. Use `1` only on a trusted LAN, and leave it `0` behind HTTPS. See [Plain-HTTP LAN](https://traceapps.github.io/docs/getting-started/lan-http/). Optional integrations: - `SMTP_*` — outgoing email for password resets and household invites. - `OIDC_*` — Single Sign-On via any standard OIDC provider (Authentik, Keycloak, Auth0). Multi-provider supported. - `AI_*`: Trace assistant config. - `WEBHOOKS_ENABLED` / `ALLOW_PRIVATE_WEBHOOK_URLS`: outgoing webhooks. See `docs/webhooks.md`. ## Reverse proxy NoteTrace listens on port 3004 inside the container, exposed on host port 3004 by default (family host-port sequence is NutriTrace 3001, LiftTrace 3002, CookTrace 3003, NoteTrace 3004, all avoiding the common `:3000` default). Front with Caddy / Nginx / Traefik on 443. If hosting at a subpath (e.g. `https://example.com/notetrace/`), set `BASE_URL=/notetrace` in the environment. ## Updating ```bash docker compose pull docker compose up -d ``` Always back up `notetrace.db` before a major version bump. ## Backups The mounted `${DATA_DB_PATH}` (SQLite database) and `${DATA_UPLOADS_PATH}` (uploaded images) directories should be in your normal backup rotation.