Admin panel reference
The admin panel is at Admin in the top navigation (visible only to
users with isAdmin: true). It is a peer of Settings, not a section
of it: Settings is what belongs to your account, Admin is what belongs
to the instance.
This page is the reference that maps every field to its effect. Help text in the panel hangs on the field it explains; anything with consequences — a restore replaces everything, a deactivated account keeps its data, debug logging costs disk — is standing text rather than a tooltip.
Instance
Section titled “Instance”The basics. Most of these were set during the first-run wizard and rarely change afterwards.
| Field | What it does |
|---|---|
| Instance name | Browser title bar text, email subject prefix |
| Public URL | Used in email links (invitations, password reset) and as the address the Companion app is told. Crucial — if this is wrong, password-reset links go to the wrong host |
| Maximum users | Cap on the user count. New registrations / invitations fail when reached. 0 = unlimited |
| Registration mode | public — anyone can sign up · invite — only invited users · closed — admin-created users only. With registration off, the login page hides its Register link; invitation links still work |
| Default UI language | New users default to this |
| Beta features | The instance beta switch. Off by default. The panel lists every feature it turns on with a plain sentence, read from the same registry the application checks — so it cannot claim something the build does not do. Applies immediately, no reload. See Beta features |
| Country counting | The instance default for how strictly countries are counted: which evidence rung the headline starts from. Every user may override it. See Countries & passport |
| Anonymous usage statistics | Opt-in ping to the adoption-metrics service. See Anonymous usage statistics |
External APIs
Section titled “External APIs”Encrypted at rest with the auto-generated encryption key under
/app/data/secrets/encryption.key. A user may enter their own key for
most of these in their settings; the instance key is used when they
have none.
| Field | What it unlocks | Required? |
|---|---|---|
| AirLabs API key | Live flight enrichment (today’s flights primarily) | No |
| Aviationstack API key | Historical + future flight enrichment | No |
| AeroDataBox API key | The provider that answers for a past date — used for the one late check on a flight that landed without its actual times | No |
| OpenSky Client ID + Secret | Real-time tracking position fallback | No |
| OpenAI API key | OpenAI Vision for boarding-pass scanning, and as a text-parser tier | No |
| Anthropic API key | Claude Vision for boarding-pass scanning, and as a text-parser tier | No |
| Ollama URL | Local LLM endpoint | No — the built-in templates and the document auto-detection work without it |
| Ollama text model / vision model | Models for document parsing and boarding-pass vision (default text model gemma3:12b) | No |
| Google Places API key | The “what is here?” lookup in the map picker and place search | No |
| Logostream key | Premium airline logos ahead of the keyless sources | No |
| Geocoder URLs | Which geocoding services fill in addresses; instance-configurable | No — defaults exist |
| Immich | An instance-wide Immich connection users without their own fall back to | No |
| Dawarich (beta) | An instance-wide Dawarich connection, offered only with the beta switch on | No |
Each row has a Test button. Most hit the provider’s health endpoint and cost nothing. Two do not: the Google Places check sends one real, billed request and its result names the price (about 0.03 USD), and the logostream check spends one request of your quota. A test that costs money says so before you press it.
The pages Flight data APIs, Ollama, and Vision parsers walk through getting each key set up.
Exchange rates
Section titled “Exchange rates”Conversion asks the European Central Bank first. For the currencies the ECB does not publish, a keyless public dataset is consulted — and this panel carries the switch that turns that fallback off entirely, for an instance that would rather report “no rate” than consult a third party. Amounts with no rate stay in their currency and are left out of totals, which say how many they left out. See Money & currencies.
Same fields as the SMTP page — host, port, encryption, username, password, from-address, plus a Send test button. Empty fields mean SMTP is disabled — invitations and password-resets fall back to the “ask the admin” message. A stored password can be deleted, not only overwritten, behind a confirmation that names the consequence.
Backups
Section titled “Backups”| Field | Default |
|---|---|
| Enable automatic backup | off |
| Interval | weekly — daily, weekly or monthly, always at 02:00 UTC; there is no free cron field |
| Retention (days) | 30 — by age, not by count |
| WebDAV (under Settings) | (empty — disabled) |
Plus a Create backup now button and, per backup, Download,
Restore and Sync to the WebDAV share. A backup is one .tar.gz
holding the database dump, every upload directory and a metadata
file. Full deep-dive on the Backups & Restore
page.
A table of all user accounts with admin actions:
- Create user — manual user creation (no email sent)
- Send invitation — generates a single-use invite link, sends it via SMTP if configured
- Promote to admin / Demote — toggle the
isAdminflag - Force password change — sets
mustChangePassword; user must change at next login - Send reset email — generates a password-reset link, mails it via SMTP
- Reset two-factor — the way back in for a user who lost their phone and their recovery codes. Clears the TOTP secret; the user signs in with the password alone and can set 2FA up again. Passkeys are not touched — a user removes those from their own security section
- Deactivate — the account is refused a session on every path (login, registration, setup, two-factor verification, passkey assertion); its data stays
- Delete user — cascades to their travel, achievements, tokens and photographs. Confirms with a typed-in-username challenge before destruction
The table shows username, name, email (if any), admin flag, whether two-factor is on, registration date, last login.
Parser
Section titled “Parser”The instance-wide parser settings: the text and vision fallback chains (which provider is tried in which order), the airline template registry with its refresh button (an admin action — any account could once trigger it and spend the instance’s refresh quota), the user templates, and the parser statistics with their anonymised export.
Audit log
Section titled “Audit log”Read-only tabular view of every mutation:
| Column | Contains |
|---|---|
| Timestamp | UTC, ISO 8601 |
| User | Who triggered the action (cookie or PAT-bearing user) |
| Token | Which PAT, if PAT-authenticated |
| IP | Client IP (anonymised after 7 days) |
| Method + path | The HTTP request that was logged |
| Resource | What changed |
| Action | CREATE / UPDATE / DELETE |
| Diff | Per-field before-and-after values |
Filter by user, action, date range, resource. Export filtered results as CSV.
Logging
Section titled “Logging”| Field | Default | Description |
|---|---|---|
| Log level | info | One of error / warn / info / debug / trace. Persisted across restarts |
| Log retention (days) | 14 | Older log files are pruned by the daily cleanup job |
| Pino destination | /app/data/logs/ | app.log, error.log, http.log, parser*.log |
Raise to debug for one-off troubleshooting, drop back to info
afterwards — debug adds a lot of volume.
System
Section titled “System”Read-only “about” pane plus a few actions:
- TravStats version, build date, Node and Postgres versions, database size, backup count, health status
- Re-sync airports — pulls the current airport catalogue (this is how an existing install picks up new fields, such as the Antarctic ones)
- Refresh airline logos — re-syncs the stored logos; a nightly sweep does the same
- Diagnostic export — an anonymised bundle for filing issues; see Troubleshooting
Update banner
Section titled “Update banner”In the top-right corner of every page TravStats displays an Update available badge when a newer stable release exists on GitHub. The check runs at most once per six hours, considers only stable releases (RC and pre-release tags are filtered out), strips a pre-release suffix from your own version before comparing, and fails silently on an air-gapped instance.
What admins can’t change here
Section titled “What admins can’t change here”Some settings deliberately live in .env (deploy-time) rather than
the admin UI (runtime):
DATABASE_URL— changing the database in flight is not a thingJWT_SECRET— auto-generated, persisted to file, never editable in UI (rotation = delete file + restart, signs out everyone)COOKIE_SECURE,CORS_ORIGIN— proxy-related; almost always auto-detected correctlyTZ— the container’s clock. Always UTC. The UI renders in user-local timeTRAVSTATS_STATS_ENDPOINT— the usage-statistics kill-switch; empty disables all sending regardless of the consent toggle
If you need to change any of these, edit .env and restart the
stack. Everything else lives in the admin UI.