Appearance
Configuration Reference
Introduction
- The
main.config.jsonfile is located in theconfig/directory of the project (or/opt/safecall/config/in production). Legacy installations with the file in the root directory are automatically migrated on first run. - Only options that differ from defaults need to be in the config file. Missing options are filled from
config/defaults.tsat startup and written back to the file. - The
web(frontend) service does not have any configuration options of its own but consumes the config of other services in its build process. The entire configuration file is never included in the build process to avoid leaking sensitive information. - This file is generated automatically by the deployment installer or setup wizard.
- After deploying the app, change the password of the default admin user to something secure using the web interface.
- Admins can change a subset of options from Settings → Configuration and Settings → User interface. Those keys apply immediately without restarting. All other keys are shown on Settings → System as read-only; changing them still requires editing this file and restarting the service.
Features
The server_features array controls which subsystems are started. Available features:
| Feature | Description |
|---|---|
"bridge" | Zabbix MQTT bridge (can run standalone or alongside others) |
"navigation" | Indoor navigation (maps, zones, tracking canvas) |
"alerting" | Emergency alerting / SOS / KISS integration |
"rtls" | Real-time location system (zone prediction, tracking algorithms) |
"sensors" | Sensor data collection, storage, and alerts dashboard |
"safecall" | Legacy alias — expands to all non-bridge features |
Common configurations:
- Full server + bridge:
["bridge", "safecall"] - Bridge only (e.g. on Zabbix machine):
["bridge"] - Navigation only:
["navigation"] - Custom subset:
["navigation", "alerting"]
Minimal Config Example
Only server_id and server_features are truly required. Everything else has defaults:
json
{
"server_id": "PRD001",
"server_features": ["navigation", "alerting"],
"server_http_port": 8088,
"server_mqtt_url": "mqtt://10.0.0.5:1883",
"server_mqtt_user": "myuser",
"server_mqtt_pass": "mypass"
}Bridge-only installations need even less:
json
{
"server_id": "BRG001",
"server_features": ["bridge"],
"server_bridge_server_url": "http://10.0.0.10:8088/",
"server_bridge_zabbix_host": "localhost"
}All Configurable Options
Core
| Key | Default | Description |
|---|---|---|
server_id | "TST000" | Unique server identifier. UI-editable; applies immediately. |
server_features | ["bridge", "safecall"] | Features to enable (see above). Restart required. |
server_mode | "prod" | Server mode: "prod" or "debug". UI-editable; applies immediately (starts/stops diagnostic snapshots). |
server_http_port | 8088 | Port for the HTTP server. Restart required. |
server_db_path | "db/safecall.db" | Path to the SQLite database file. Restart required. |
server_url_prod | "/api/v1/" | Root-absolute base path for application JSON APIs |
server_url_ws | "/ws" | WebSocket URL |
Auth / Credentials
| Key | Default | Description |
|---|---|---|
server_credentials_default_email | (from brand) | Default admin email |
server_credentials_default_password | "changeme" | Default admin password |
server_auth_jwt_issuer | (from brand) | JWT issuer |
server_auth_jwt_audience | (from brand) | JWT audience |
server_auth_jwt_expiry | "8h" | JWT token expiry duration |
server_mobile_api_key | "change-me-with-safecall-config-regen-mobile-key" | Shared secret used only for mobile navigation endpoint authentication; regenerate with safecall config regen-mobile-key |
Brand-dependent defaults
The JWT issuer, audience, default admin email, and features are derived from the active brand at runtime (see Whitelabel / Brands). This ensures the server defaults always match the values baked into the frontend during a release build. You only need to override these if your deployment uses different values.
MQTT
| Key | Default | Description |
|---|---|---|
server_mqtt_url | "mqtt://localhost:1883" | MQTT broker URL |
server_mqtt_user | "safecall" | MQTT broker username |
server_mqtt_pass | "safecall" | MQTT broker password |
Bridge (only used when "bridge" feature is enabled)
| Key | Default | Description |
|---|---|---|
server_bridge_server_url | "http://localhost:8088/" | URL of the SafeCall server |
server_bridge_zabbix_host | "localhost" | Hostname of the Zabbix server |
server_bridge_zabbix_port | 10051 | Port of the Zabbix server |
server_bridge_endpoint_beacon | "lld/beacons" | REST endpoint for beacons |
server_bridge_endpoint_gateway | "lld/gateways" | REST endpoint for gateways |
server_bridge_delay_beacon | 300 | Delay between beacon updates (seconds) |
server_bridge_delay_gateway | 300 | Delay between gateway updates (seconds) |
server_bridge_cache_update_interval | 3600 | Interval for refreshing cache (seconds) |
KISS / Alerting (only used when "alerting" feature is enabled)
| Key | Default | Description |
|---|---|---|
server_kiss_url_base | "" | Base URL for KISS API. UI-editable; applies immediately. Empty disables KISS. |
server_kiss_url_path_login | "/api/Mobile/LoginUser" | Path for KISS login endpoint. Restart required. |
server_kiss_url_path_convene | "/api/Mobile/ExecuteConveneByRule" | Path for KISS convene endpoint. Restart required. |
server_kiss_credentials_username | "" | Username for KISS API. UI-editable; applies immediately. |
server_kiss_credentials_password | "" | Password for KISS API. UI-editable; applies immediately. |
Performance / Devices
| Key | Default | Description |
|---|---|---|
server_devices_cache_sync_interval | 300 | Interval for devices cache sync (seconds). UI-editable; applies immediately. |
server_perf_memory_threshold | 512 | RSS warning threshold in MB for diagnostic snapshots. UI-editable; applies immediately. Snapshots flag when process RSS exceeds this value; the process is not killed. |
Diagnostics (Debug Mode)
When server_mode is set to "debug" (or the binary is started with --debug), the server writes periodic JSON snapshot files containing system metrics, MQTT throughput, tracking algorithm performance, WebSocket stats, and memory estimates. See Server Diagnostics for full details.
| Key | Default | Description |
|---|---|---|
server_debug_snapshot_interval | 300 | Seconds between diagnostic snapshots. UI-editable; applies immediately when debug is on. |
server_debug_log_dir | "logs/diagnostics" | Directory for snapshot files (relative to binary location). UI-editable; applies immediately. |
server_debug_max_files | 288 | Maximum snapshot files to retain (288 = 24h at 5min intervals). UI-editable; applies immediately. |
server_log_verbosity | "normal" | Application audit log verbosity: "none", "normal", "verbose". UI-editable; applies immediately. |
Gateway LuCI (admin UI gateway configuration tool)
| Key | Default | Description |
|---|---|---|
server_gateway_luci_username | "admin" | Default LuCI username for apply / fetch-preset when the UI does not override |
server_gateway_luci_password | "admin" | Default LuCI password (set in deployment config) |
server_gateway_luci_http_transport | "fetch" | Outbound HTTP client: "fetch" (Bun) or "curl" (requires curl on server PATH). Use "curl" if gateways fail with fetch but work with manual curl |
server_gateway_luci_timeout_sec | 30 | Timeout per LuCI request (login and admin endpoints) |
server_gateway_scan_default_subnet | "" | Default CIDR for subnet scan when the UI leaves subnet empty; empty string derives a /24 from the host’s first non-internal IPv4 |
Notifications
| Key | Default | Description |
|---|---|---|
server_ntfy_enabled | true | Master switch for SOS, watch, pipeline-test, and daily-status ntfy. UI-editable; applies immediately. |
server_ntfy_cooldown_seconds | 600 | Deprecated compatibility value. Hidden in the UI. |
server_ntfy_sensors_cooldown_seconds | 21600 | Unused by the daily status plane. Kept for older binaries. |
server_ntfy_button_dedup_seconds | 40 | Shared SOS/watch window used by event logging, KISS, and ntfy to combine one press reported by several gateways. UI-editable. |
server_ntfy_critical_cooldown_seconds | 21600 | Unused by the daily status plane. Kept for older binaries. |
server_ntfy_digest_timezone | "Europe/Prague" | IANA timezone for the daily Overview reminder. UI-editable. |
server_ntfy_digest_time | "09:00" | Local wall-clock time for the once-a-day {server_id}-status wake. UI-editable. |
server_ntfy_batteries_mode | "bulk" | Legacy key. Delivery is daily status vs mute, not bulk/individual. |
server_ntfy_batteries_interval_seconds | 86400 | Legacy key. |
server_ntfy_sensor_notseen_mode | "bulk" | Legacy key. |
server_ntfy_sensor_notseen_interval_seconds | 86400 | Legacy key. |
server_ntfy_calibration_mode | "bulk" | Legacy key. |
server_ntfy_calibration_interval_seconds | 86400 | Legacy key. |
server_ntfy_fleet_health_mode | "bulk" | Legacy key. |
server_ntfy_fleet_health_interval_seconds | 86400 | Legacy key. |
server_ntfy_sensors_muted | false | Omit sensor threshold items from the status snapshot and daily count. UI-editable; applies immediately. |
server_ntfy_batteries_muted | false | Omit battery items from the snapshot and daily count. UI-editable. |
server_ntfy_notseen_muted | false | Omit not-seen items from the snapshot and daily count. UI-editable. |
server_ntfy_calibration_muted | false | Omit calibration items from the snapshot and daily count. UI-editable. |
server_ntfy_health_muted | false | Omit install-health items from the snapshot and daily count. UI-editable. Vendor ops is independent. |
server_keryx_status_url | "" | Optional keryx-status base URL. Empty derives {ntfy origin}/keryx-status. Restricted; not on the settings form. |
server_keryx_status_read_secret | "" | Optional HMAC secret shared with keryx-status for phone GET tokens. Restricted; never exposed via the HTTP API. Empty uses the ntfy subscribe token. |
server_ntfy_server_url | https://notify.${active_brand.company_domain} | Base ntfy server URL used when publishing push notifications |
server_ntfy_ops_enabled | true | Post install-health events to the vendor ops topic. UI-editable; applies immediately. Independent of server_ntfy_enabled. Turn off on test installs. |
server_ntfy_ops_server_url | https://notify.${active_brand.company_domain} | Vendor diagnostics ntfy host (independent of server_ntfy_server_url) |
server_ntfy_ops_topic | {binary_name}-ops | Vendor diagnostics topic shared across installs. Empty disables outbound vendor ops even when server_ntfy_ops_enabled is true. Not a staff subscription. |
server_ntfy_publish_token | "" | Bearer token for authenticated publish to the client ntfy host. Restricted; never exposed via the HTTP API. Empty means anonymous publish (visitor rate limits). Operator how-to: ntfy auth dual-run. |
server_ntfy_token_manager_user | "" | ntfy user for POST /v1/account/token when minting Keryx subscribe tokens. Empty (or a 404 from ntfy) falls back to a local tk_local_… QR credential. |
server_ntfy_token_manager_password | "" | Password for server_ntfy_token_manager_user. Restricted; never exposed via the HTTP API. |
Staff ntfy topic names are prefixed with server_id. Full trigger, tag, and cadence tables: SafeCall ntfy / Keryx alert topics.
${server_id}-sos— panic button alerts (immediate)${server_id}-watches— watch button push alerts (immediate)${server_id}-test— pipeline tests (Watch/Panic Test Send and dashboard Recheck; no KISS)${server_id}-status— at most one daily Overview reminder (counts only)${server_id}-sensors/-batteries/-notseen/-calibration/-health— not phone wakes; items live on keryx-status
Mute vs daily: each condition category is included in the snapshot and the daily count unless the matching server_ntfy_*_muted flag is on. SOS, watch, and pipeline tests ignore those flags. server_ntfy_enabled off stops all staff ntfy, including the daily wake. Existing Keryx phones need a new QR to subscribe to -status and GET the snapshot.
SafeCall persists current alert conditions in SQLite and PUTs a replace-in-place snapshot to keryx-status (debounced). Test messages bypass cooldown. See the topic reference for Keryx Overview vs Active.
Grafana datasource API notes:
- SafeCall exposes
/grafana/search,/grafana/metrics, and/grafana/queryfor Grafana JSON datasource integrations. - Loopback requests (
127.0.0.1/::1) can use these endpoints without JWT. - Non-loopback requests must still provide a valid SafeCall JWT.
- See Grafana Setup for step-by-step configuration.
User interface
Site-wide chrome for simple and maintenance users. Editable from Settings → User interface. Changes apply immediately (config_runtime_apply); those users pick them up on the next GET /api/v1/user/session (or page load). The admin navbar is unchanged.
| Key | Default | Description |
|---|---|---|
server_ui_nav_orientation | "horizontal" | "horizontal" top bar or "vertical" sidebar. UI-editable; applies immediately. |
server_ui_show_brand | true | Show the product name in the user shell. UI-editable; applies immediately. |
server_ui_show_profile | true | Show the signed-in email on the user menu. UI-editable; applies immediately. |
server_ui_show_account | true | Show Account and personal settings entries. UI-editable; applies immediately. |
server_ui_show_logout | true | Show logout in the chrome. UI-editable; applies immediately. |
server_ui_logout_style | "menu" | "menu" (inside the user menu) or "button" (header button on the right). UI-editable; applies immediately. |
server_ui_show_system_status | false | Quiet status dot from GET /api/v1/status/dashboard. Off by default. UI-editable; applies immediately. |
server_ui_show_footer | true | Compact product footer. UI-editable; applies immediately. |
server_ui_footer_show_version | true | Include the running version on the footer line. UI-editable; applies immediately. |
server_ui_footer_text | "" | Optional extra footer line (max 240). Empty keeps the default credit. UI-editable; applies immediately. |
server_bug_tracker_enabled | false | Enable in-app Bug Tracker FAB and admin reports. Edited on Settings → User interface. UI-editable; applies immediately. |
Log Verbosity Policy
The server_log_verbosity key controls writes to the SQL logs table.
none: disable app-level log writes.normal: record operational/audit changes.verbose: include everything fromnormalplus high-frequency/operator activity.
Current classification rules:
- Normal
- Entity create/update/delete (users, beacons, gateways, locations, maps, floors, nodes, edges, navigation tokens).
- Settings changes via
PATCH /api/v1/settings.
- Verbose
- User login events.
- Tool usage events (
Debug Device,Gateway ID,Placement Plannerinteractions).
Guidance for future features:
- If an action mutates persisted state or system behavior, classify as Normal.
- If an action is diagnostic, exploratory, or high-frequency UI/tool usage, classify as Verbose.
- Never include secret values (passwords, tokens, API keys) in log messages.
Whitelabel / Brands
The build system supports multiple brand profiles. Set the SAFECALL_BRAND environment variable or use --brand=<name> when building:
bash
# Build for SafeCall (default)
bash scripts/build/server.sh --brand=safecall
# Build for NavBeacon (navigation only, Bleuvista branding)
bash scripts/build/server.sh --brand=navbeaconBrand definitions live in config/brands/. Each brand specifies product name, company, default features, update server URL, and other branding details.
Several configuration defaults are derived from the active brand so that a release build's baked-in values (JWT issuer/audience, default features, admin email) always match the server's runtime defaults:
| Config key | Brand field |
|---|---|
server_features | default_features |
server_credentials_default_email | default_admin_email |
server_auth_jwt_issuer | auth_issuer |
server_auth_jwt_audience | auth_audience |
This means a fresh deployment with default config will work out of the box — no need to manually align JWT or feature settings with the brand.