Configuration

Tau is configured via a TOML file. The server looks for config.toml in the current working directory unless --config <path> is passed. All fields are optional; an absent config file starts an in-memory server on 127.0.0.1:7070 with defaults.


Quick start

# Copy the sample (used by the Docker stack) and edit
curl -fsSLO https://raw.githubusercontent.com/bxrne/tau/master/container/tau-config.toml

# Start with the default config.toml in the current directory
tau

# Or point at a specific path
tau --config /etc/tau/tau-config.toml

Full example

bind = "127.0.0.1:7070"
log_level = "info"         # error | warn | info | debug | trace
compact_threshold = 8      # layers per lens before auto-compaction fires

[disk]
backend = "memory"         # "memory" (default) or "disk" (persist to <path>/<db>.dat)
# path = "/var/lib/tau/data"  # required when backend = "disk"
compression_level = 3      # zstd level 1–22; higher = better ratio, slower writes

[wal]
enabled = true
path = "/var/lib/tau/tau.wal"
no_fsync_each = false       # true: 50 ms group-commit; risk: up to one interval of data loss
max_size_mb = 512           # rotate WAL when it reaches 512 MiB; omit to disable

[tls]
enabled = true
cert = "/etc/tau/cert.pem"  # omit both cert+key for ephemeral self-signed (dev only)
key  = "/etc/tau/key.pem"

[auth]
enabled = true
username = "admin"          # bootstrap admin on first run
password = "changeme"       # hashed with argon2id at startup; plaintext not retained
users_file = "/var/lib/tau/users.db"

[metrics]
port = 9100                 # serves GET /metrics and GET /healthz

[limits]
max_connections = 1024
idle_timeout_secs = 300     # omit to disable (default: no timeout)

Top-level fields

fielddefaultdescription
bind127.0.0.1:7070TCP address to listen on
log_levelinfoerror | warn | info | debug | trace
compact_threshold8Number of layers per lens before automatic compaction fires

[disk]

fielddefaultdescription
backendmemoryStorage backend. memory keeps layers in RAM (pair with [wal] for durability). disk gives every database its own compressed <path>/<name>.dat file.
path(none)Directory for the per-database .dat files — required when backend = "disk".
compression_level3zstd compression level applied when the disk store flushes. Range 1–22: 1 is fastest with least compression, 22 is best ratio but slowest.

For the throughput impact of compression_level and compact_threshold (a checkpoint on the disk backend flushes and recompresses the file; checkpoints fire at most every 8 compactions, or sooner if [wal].max_size_mb is reached), see the storage grid results in Benchmarks.

With backend = "disk", each database gets a <name>.dat file plus a <name>.wal file in [disk].path. APPEND writes go to the WAL first (fsynced by default) and update the in-memory layer stack; the .dat file — which holds both layer data and schema DDL (CREATE LENS, DERIVE LENS, SET TTL, DROP LENS) — is rewritten atomically only on a checkpoint (every 8 compactions, or [wal].max_size_mb, whichever comes first). Issuing CREATE DATABASE <name> re-opens an existing <name>.dat, replays <name>.wal on top of it, and replays the schema, so lenses, policies, and any appends since the last checkpoint survive a restart without re-issuing DDL. [wal].no_fsync_each and [wal].max_size_mb apply to these per-database WAL files; [wal].enabled and [wal].path are not used by the disk backend — a WAL is always attached per database, colocated with its .dat file.


[wal]

fielddefaultdescription
enabledfalseEnable write-ahead logging for durability across restarts. Only applies to the memory backend — disk always has a per-database WAL.
path(none)Path for the WAL file — required when enabled = true. Only applies to the memory backend.
no_fsync_eachfalseSkip per-record WAL flush+sync; a background thread flushes every 50 ms. Applies to both backends.
max_size_mb(none)Soft size cap in MiB. Once the WAL file reaches this size, the next write triggers a checkpoint rewrite to keep the file bounded. Omit to disable the cap. Applies to both backends.

With the memory backend and enabled = true, every write is fsynced to the WAL before being applied to the in-memory store. On startup, the WAL is replayed to reconstruct state. Without the WAL, data is in-memory only and lost on process exit.

With the disk backend, the WAL is always enabled per database regardless of enabled; no_fsync_each and max_size_mb still configure it.

no_fsync_each trades durability for throughput. Use only on trusted workloads or when an external durability boundary (replication, backup) exists.


[tls]

fielddefaultdescription
enabledfalseEnable TLS
cert(none)Path to PEM-encoded certificate file
key(none)Path to PEM-encoded private key file

With enabled = true and no cert/key paths, an ephemeral self-signed certificate is generated at startup — convenient for development but not verifiable by clients. For production, provide a real cert and key.


[auth]

fielddefaultdescription
enabledfalseEnable per-connection authentication
username(none)Bootstrap admin username
password(none)Bootstrap admin password, hashed with Argon2id at startup
users_file(none)Persistent multi-user store; created on first run

Two user-store modes:

In-memory single user — set username/password, no users_file. Bootstraps one global-admin user with no persistence. Every restart requires the same values.

Persistent multi-user — set users_file. On first run with username/password, the file is seeded with that user as global admin. Subsequent CREATE USER, DROP USER, GRANT, and REVOKE statements are atomically written back.


[metrics]

fielddefaultdescription
port(none)Expose Prometheus /metrics and /healthz on this HTTP port

When port is set:

GET http://0.0.0.0:<port>/metrics    Prometheus text-format
GET http://0.0.0.0:<port>/healthz    Liveness probe

[limits]

fielddefaultdescription
max_connections1024Maximum concurrent client connections; new connections beyond the cap receive ERR server at connection limit
idle_timeout_secs(none)Per-connection idle timeout in seconds; omit to disable

Environment variables

variabledescription
TAU_ENCRYPTION_KEY64 hex characters (32 bytes). When set, WAL entries are encrypted per-entry with AES-256-GCM. Without this key, an encrypted WAL file cannot be replayed.
export TAU_ENCRYPTION_KEY=$(openssl rand -hex 32)
tau --config /etc/tau/tau-config.toml

Metrics reference

metrictypedescription
tau_statements_total{type=...}counterStatements processed, by type
tau_statement_duration_microseconds_bucket{type=...,le=...}histogramPer-type latency histogram
tau_connections_totalcounterTCP connections accepted since startup
tau_rejected_connections_totalcounterConnections refused at the max-connections cap
tau_auth_attempts_totalcounterAUTH messages received
tau_auth_failures_totalcounterFailed AUTH attempts
tau_errors_totalcounterERR responses sent
tau_process_resident_bytesgaugeResident memory (Linux: VmRSS)
tau_process_open_fdsgaugeOpen file descriptors
tau_process_uptime_secondsgaugeSeconds since startup

Permission model

When [auth] enabled = true, every statement is checked against the caller's CRUDA bitmap:

bitgrants
CCREATE LENS, DERIVE LENS
RAT, RANGE, REDUCE, SHOW LENSES
UAPPEND LENS, COPY LENS FROM
DDROP LENS
AAdmin: manage users, GRANT/REVOKE, CREATE DATABASE, DROP DATABASE

Effective permissions for a user on database db = grants[db] | grants["*"]. A user with A on "*" is a global admin.

SHOW DATABASES is post-filtered for non-admins: only databases the caller holds any grant on are returned.


Client (tauctl)

tauctl has no configuration file. It accepts only --version and --help; all connection and session settings are entered interactively in the TUI.