CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Failing after 9s
CI & Build / integration (push) Failing after 12s
CI & Build / Build & push image (push) Successful in 32s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m17s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m14s
Desktop (Tauri) / Update manifest (push) Successful in 5s
Operator: *"proxy hops defaults to 1 and should be in the settings UI not in the envs, we need the security values to be in the UI."* Overrules the call I made yesterday, and rule 25 is on your side — I argued deployment-topology, but the operator has to be able to SEE what protects them, and reading a container's environment is not seeing. Six new settings in a **Security** group: trusted proxy hops (default 1), the per-account and per-address sign-in limits with their shared window, and the sign-up limit with its own. `THOUGHTSYNC_TRUSTED_PROXY_HOPS` is gone; the rate limits are no longer hardcoded constants. **The hard part was keeping the throttle cheap.** It consults these BEFORE opening a database connection — deliberately, because a refused attempt is meant to cost nothing, and the hop count is needed to know who is even asking. A query per attempt would undo both. So there is a small cache seeded from the registry defaults (the app works with no database at all, which is what the DB-free unit lane relies on), loaded at boot, and refreshed on every settings save — the same live-update contract `session_ttl_days` already had. `SlidingWindow` now takes its limit and window as SUPPLIERS rather than values, so a saved number applies to the next attempt instead of the next deploy. **Bounds are rejected, not clamped.** A hop count of 99 would trust anything a caller sent; a sign-in limit of 0 would lock every account out permanently. Both now fail validation with a message naming the range, and the number input carries min/max so the browser objects first. Silently storing a different number than the one typed is how somebody ends up believing a protection is set to something it is not. `MAX_BUCKETS` stays a constant on purpose: it protects the limiter from itself rather than the app from a caller, and there is no operator judgment to apply. Two integration tests, because the whole point is the round trip: a dangerous value refused, a legitimate one reaching the cache the throttle reads and persisting; and every Security row reaching the admin payload with bounds and a description that explains itself.
65 lines
2.9 KiB
Bash
65 lines
2.9 KiB
Bash
# ThoughtSync production settings. Copy to `.env` and edit:
|
|
#
|
|
# cp .env.example .env
|
|
#
|
|
# Only POSTGRES_PASSWORD has no default — compose refuses to start without it.
|
|
# Everything else here is optional. Anything NOT in this file (site name, signups,
|
|
# attachment limits, trash retention, link previews) is configured in the admin
|
|
# Settings UI and stored in the database, not here.
|
|
|
|
# --- required ---------------------------------------------------------------
|
|
|
|
# Generate one and keep it: changing it later means also changing it inside the
|
|
# database, or Postgres will reject the app's connection.
|
|
#
|
|
# openssl rand -base64 24 | tr -d '/+=' | head -c 32
|
|
#
|
|
# Stick to letters and digits. This value goes into a connection URL, so a `@`,
|
|
# `/`, `:` or `#` in it will be misparsed as URL structure rather than password.
|
|
POSTGRES_PASSWORD=
|
|
|
|
# --- optional ---------------------------------------------------------------
|
|
|
|
# Which build to run.
|
|
#
|
|
# latest tracks the `main` branch — the production line (default)
|
|
# dev tracks the `dev` branch — newer, less settled
|
|
# <commit sha> pins one exact build; every push publishes one, and this is
|
|
# the rollback lever when an upgrade misbehaves
|
|
#
|
|
# NOTE: `main` can sit well behind `dev`. If a feature you expect is missing,
|
|
# check which branch it actually landed on before assuming a bug.
|
|
#THOUGHTSYNC_TAG=latest
|
|
|
|
# The host port the app is published on.
|
|
#THOUGHTSYNC_PORT=5000
|
|
|
|
# Which interface to bind. The default (all interfaces) is what lets desktop
|
|
# clients on your network reach the server. Behind a reverse proxy, set this to
|
|
# 127.0.0.1 so only the proxy can talk to it.
|
|
#THOUGHTSYNC_BIND=0.0.0.0
|
|
|
|
# NOTE: how many proxies sit in front of this app is a SETTING, not an env var —
|
|
# Settings → Security → "Trusted proxy hops" in the admin UI. It defaults to 1 (one
|
|
# reverse proxy terminating HTTPS) and belongs there because it is something you may
|
|
# need to change while the server is running, alongside the sign-in limits.
|
|
|
|
# How much the app says. Credential events (sign-ins, failures, throttles, new
|
|
# accounts, device tokens issued) are logged at INFO and read with
|
|
# `docker compose logs app`.
|
|
#THOUGHTSYNC_LOG_LEVEL=INFO
|
|
|
|
# Database identity. Changing these AFTER the first start does not rename anything
|
|
# that already exists — the volume keeps whatever the first run created.
|
|
#POSTGRES_USER=thoughtsync
|
|
#POSTGRES_DB=thoughtsync
|
|
|
|
# --- a note on HTTPS --------------------------------------------------------
|
|
#
|
|
# The app marks its session cookie Secure automatically when a request arrives over
|
|
# HTTPS, directly or via a proxy setting X-Forwarded-Proto — no setting needed.
|
|
#
|
|
# Worth knowing if you use the desktop app: typing a bare hostname there defaults to
|
|
# https://, deliberately, so a device token never crosses the wire in cleartext by
|
|
# accident. Serving over plain HTTP means typing the `http://` yourself.
|