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
| field | default | description |
|---|---|---|
bind | 127.0.0.1:7070 | TCP address to listen on |
log_level | info | error | warn | info | debug | trace |
compact_threshold | 8 | Number of layers per lens before automatic compaction fires |
[disk]
| field | default | description |
|---|---|---|
backend | memory | Storage 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_level | 3 | zstd 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]
| field | default | description |
|---|---|---|
enabled | false | Enable 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_each | false | Skip 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]
| field | default | description |
|---|---|---|
enabled | false | Enable 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]
| field | default | description |
|---|---|---|
enabled | false | Enable 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]
| field | default | description |
|---|---|---|
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]
| field | default | description |
|---|---|---|
max_connections | 1024 | Maximum 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
| variable | description |
|---|---|
TAU_ENCRYPTION_KEY | 64 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
| metric | type | description |
|---|---|---|
tau_statements_total{type=...} | counter | Statements processed, by type |
tau_statement_duration_microseconds_bucket{type=...,le=...} | histogram | Per-type latency histogram |
tau_connections_total | counter | TCP connections accepted since startup |
tau_rejected_connections_total | counter | Connections refused at the max-connections cap |
tau_auth_attempts_total | counter | AUTH messages received |
tau_auth_failures_total | counter | Failed AUTH attempts |
tau_errors_total | counter | ERR responses sent |
tau_process_resident_bytes | gauge | Resident memory (Linux: VmRSS) |
tau_process_open_fds | gauge | Open file descriptors |
tau_process_uptime_seconds | gauge | Seconds since startup |
Permission model
When [auth] enabled = true, every statement is checked against the caller's CRUDA bitmap:
| bit | grants |
|---|---|
C | CREATE LENS, DERIVE LENS |
R | AT, RANGE, REDUCE, SHOW LENSES |
U | APPEND LENS, COPY LENS FROM |
D | DROP LENS |
A | Admin: 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.