| .githooks | ||
| .sqlx | ||
| migrations | ||
| src | ||
| tests | ||
| ui | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| justfile | ||
| README.md | ||
| todo.md | ||
Environment Variables
Loaded from the process environment, falling back to a .env file in the
working directory if one is present.
Required
The application exits at startup if any of these is unset.
| Variable | Type | Description |
|---|---|---|
S3_ENDPOINT |
URL | Address of the S3 instance. Must be reachable by anyone viewing the UI, or presigned URLs will not resolve. |
S3_BUCKET |
string | Bucket name. Checked with HeadBucket at startup, so an unreachable or missing bucket fails fast. |
S3_ACCESS_KEY |
string | S3 access key. |
S3_SECRET_KEY |
secret string | S3 secret key. |
SESSION_PRIVATE_KEY |
base64 string | Signing and encryption key for session cookies. Must decode to at least 64 bytes — generate with openssl rand -base64 64. |
STAG_CLIENT_ID |
string | OAuth client id issued by stag. Also the expected aud on identity tokens. |
STAG_CLIENT_SECRET |
secret string | OAuth client secret. Also the HS256 key identity tokens are verified against. |
Required only on first run
Read only when the database holds no root administrator, so they are needed on an empty database and ignored on every later start. Leaving them set is harmless.
| Variable | Type | Description |
|---|---|---|
AUTH_ROOT_ADMINISTRATOR_USER_STAG_ID |
i64 | The sub of the stag identity that becomes the root administrator. Startup fails if it will not parse. |
AUTH_ROOT_ADMINISTRATOR_USER_EMAIL |
string | Email for the root administrator record. |
AUTH_ROOT_ADMINISTRATOR_USER_DISPLAY |
string | Display name. Optional even on first run — defaults to the email. |
Optional
| Variable | Type | Default | Description |
|---|---|---|---|
DATABASE_URL |
SQLite URL | sqlite://<PATH TO FILE>. The file is created if missing; WAL and foreign keys are enabled on connect. Defaults to sqlite://deers-io.db |
|
BIND_ADDRESS |
socket address | 127.0.0.1:5000 |
Address the server listens on. Use 0.0.0.0:<port> to accept traffic from outside the host. |
SECURE_COOKIES |
true | false |
true |
Sets the Secure flag on session cookies. Only turn it off for local HTTP development — browsers drop secure cookies over plain HTTP. |
TRUST_PROXY_HEADERS |
true | false |
false |
Whether to believe X-Real-IP when rate limiting. Enable only behind a proxy that sets it; otherwise a client can forge a fresh rate-limit bucket per request. |
CORS_ALLOWED_ORIGINS |
comma-separated origins | unset | e.g. https://deers.io,https://www.deers.io. When unset the CORS layer is not installed at all, so browsers apply same-origin rules. |
RUST_LOG |
tracing filter | warn,deers_io=info,tower_http=info |
Standard EnvFilter syntax. Avoid trace in production — the HTTP client logs request bodies at that level, which would include the stag client secret. |
Notes on parsing
SECURE_COOKIESandTRUST_PROXY_HEADERSaccept only the exact stringstrueandfalse. Any other value is ignored and the default applies, so a typo such asTRUEor1fails silently and leaves the safer setting in place.CORS_ALLOWED_ORIGINSpanics at startup on a value that is not a valid header, rather than skipping it.SESSION_PRIVATE_KEYpanics at startup if it decodes to fewer than 64 bytes.- Changing
SESSION_PRIVATE_KEYinvalidates every existing session.
Accounts
Every account in this application is a full administrator. There are no roles: any authenticated user can create, edit, and delete every commission, person, and character, and can invite further users who are also full administrators. Invites cannot currently be revoked and users cannot be removed, so only invite people you would trust with direct access to the database.
The root administrator is the account seeded on first run. It is marked by
users.is_root_administrator, and the schema permits exactly one such row.
Development
Register the git hooks after cloning:
git config core.hooksPath .githooks
The pre-commit hook runs cargo check and cargo clippy, and verifies that the
.sqlx offline query cache matches the queries being committed. A stale cache
builds fine for anyone holding a migrated dev.db and fails for everyone else,
CI and fresh clones included. Installing sqlx-cli is required for the check:
cargo install sqlx-cli
Testing
Run everything with cargo test. No database setup is needed: #[sqlx::test]
applies migrations/ to a throwaway database (under target/sqlx/) for every
test, and hands it over as a SqlitePool argument. No environment variables are
needed either — integration tests build the router from fixtures rather than
from the environment, and never reach S3 or stag.
Unit tests live next to the code they cover, in a #[cfg(test)] mod tests at the
bottom of the file.
tests/ holds integration tests, which link the crate as an external caller and
therefore only see its public API. They are split by area — people.rs,
characters.rs, commissions.rs, authentication.rs — with public_api.rs
holding the cross-cutting router behaviour: authentication coverage across every
mutating route, fallbacks, body limits, and rate limiting. Shared fixtures,
including the HTTP test client and the fakes for S3 and stag, live in
src/util/test_support.rs.