# opentracker configuration. Every field can also be set as OT_. # Copy to opentracker.toml and edit. All values shown are the defaults. # HTTP API and web UI. Localhost by default: nginx or Caddy terminates TLS. http_addr = "127.0.0.1:7372" # OTP/1 UDP listener. # # THE TRAP: HTTP reverse proxies do not forward UDP. This port must be exposed # directly by a firewall or NAT rule. If it is not, every phone silently falls # back to TLS-over-TCP and you lose the whole point of the protocol. udp_addr = "0.0.0.0:7373" # TLS-over-TCP fallback for UDP-hostile networks. Also needs its own rule. # tls_addr = "0.0.0.0:7374" # What the login response tells phones to connect to. These are the *public* # names, which are usually not the same as the bind addresses above. public_udp_host = "localhost" public_udp_port = 7373 # public_tls_url = "tls://track.example.com:7374" base_url = "http://localhost:7372" # REQUIRED. The OSM tile usage policy demands a contactable User-Agent, and an # unidentified tile proxy is blocked without notice. The server refuses to start # while this is the placeholder. admin_email = "you@example.com" db_path = "opentracker.db" cache_dir = "cache" # Tile cache ceiling in bytes; eviction runs down to 90% of it. 1 GiB. max_cache_bytes = 1073741824 # Point at your own renderer here if you ever run one. tile_upstream_url = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" # Hard drop for points older than this. Points older than 24 h are also thinned # to roughly one per 5 minutes. The live marker lives in its own table and is # never affected. retention_days = 7 # Mark tokens with no activity for this long as revoked. A phone idle for a # month has to log in again — the same contract as an expiring browser session. # # The row and its wrapped key are kept, not deleted. The key is the only thing # that can seal a message the phone will believe, and a phone idle for a month is # exactly the one that needs telling. A token row is about a hundred bytes. token_stale_days = 30 # Accept client timestamps within ±this many days. # # This is the only server-side handling of a client timestamp anywhere. Nothing # corrects a ts, nothing measures clock skew, and no message in the protocol # carries the server's clock. This bound exists purely so a phone whose clock says # 2106 cannot write rows the retention sweep will never reach. ts_window_days = 30 # Answer a datagram naming an unknown token with a sealed REVOKED notice instead # of silence, so a phone whose token the server has forgotten lands on the login # screen instead of reporting into nothing. # # THE TRADE: this is the one reply the server sends without having verified the # request, which makes the UDP port a reflector — UDP source addresses are # forgeable, so the reply goes wherever the sender claimed to be. Three things # bound it: the notice is 38 bytes and is refused to any shorter request, so it # can never amplify; it is limited to one per destination address per minute # under a global ceiling of 10/s; and naming unknown tokens still earns strikes # and a ban either way. It cannot be forged, because it is sealed with a key # derived from the server master and that token_id. # # Turning it off costs little. A revoked token keeps its row and its key, so the # ordinary "you are logged out" answer is a fully authenticated NACK that never # takes this path. This switch only matters when the row is genuinely gone: a # restored backup predating the login, or a rotated OT_SECRET_KEY. With it off, # those devices get silence until someone opens the app. revocation_notices = true # Argon2id. 19 MiB / 2 passes / 1 lane is OWASP's second recommended profile. # Raising these later does not invalidate existing hashes: each hash carries its # own parameters and is transparently upgraded on the next successful login. argon2_memory_kib = 19456 argon2_iterations = 2 argon2_parallelism = 1 # Number of SO_REUSEPORT receive tasks. Defaults to min(cpus, 4). # udp_workers = 4