This is the canonical reference for the three signal-specific Rust data-plane
binaries shipped from the servers workspace. The binaries own HTTP parsing,
bounded queues, language evaluation, SQLite connections, and process
lifecycle. They do not implement a second storage engine: every durable write,
query, and maintenance operation uses the public surfaces of
libtimeless_ext documented in the
SQLite extension API reference.
The servers are independently usable. Phoenix is a control plane that may supervise them and issue policy files and tokens, but no request requires BEAM, Elixir, a NIF, or a fallback process. PromQL, MetricsQL, and LogsQL support is defined by the query feature maps. TraceQL is not implemented.
| Binary | Default TCP listener | Storage table | Required extension batch capability |
|---|---|---|---|
timeless-metrics-api |
127.0.0.1:19439 |
metric_samples for a fresh database; legacy public metrics vtab remains readable |
named-v0 |
timeless-logs-api |
127.0.0.1:19429 |
logs with epoch-microsecond timestamps |
rich-v1 |
timeless-traces-api |
127.0.0.1:19449 |
traces |
rich-span-v2 |
All three use the same positional command line:
timeless-<signal>-api <libtimeless_ext.so> <database> [listen-address]
The extension and database paths are required. The listener is the optional
third positional argument. An extra or malformed argument exits with status
2. --version, supplied by itself, prints a JSON object containing the binary
name, Cargo version, source commit, target triple, and build profile.
TCP is the implemented transport. Loopback is the safe default. An operator
who has separately secured a container or host network may set
TIMELESS_ALLOW_NON_LOOPBACK=1 and pass a non-loopback address. There is no
Unix-socket listener in this release.
Authentication is opt-in at every level, and the library and binaries agree:
Config::default() and an unconfigured binary both start open. Set
TIMELESS_AUTH_MODE=required with TIMELESS_AUTH_POLICY_FILE to enable
token verification; a bad policy still fails closed before the listener
binds.
The following marked inventory is checked against every production Axum
.route(...) registration, including its exact method set.
The "Required scope" column applies only when auth is enabled
(TIMELESS_AUTH_MODE=required); an open server enforces no scopes.
| Signal | Methods | Path | Required scope | Contract |
|---|---|---|---|---|
metrics |
GET |
/live |
none | Process liveness only; does not touch SQLite. |
metrics |
GET |
/ready |
metrics:stats |
Readiness plus build, storage, queue, rollup, and file accounting. |
metrics |
GET |
/health |
metrics:stats |
Alias of /ready. |
metrics |
GET |
/metrics |
none | Prometheus text exposition of the plane's own operational stats plus timeless_build_info; unauthenticated like the probe endpoints. |
metrics |
GET |
/select/metrics/stats |
metrics:stats |
Complete serialized StorageStats. |
metrics |
POST |
/api/v1/flush |
metrics:maintenance |
Ordered writer completion, extension flush, and durability barrier. |
metrics |
POST |
/api/v1/backup |
metrics:maintenance |
Flush, compact/roll up, checkpoint, and verified SQLite backup. |
metrics |
POST |
/api/v1/import |
metrics:write |
VictoriaMetrics JSON-line import. |
metrics |
POST |
/api/v1/import/prometheus |
metrics:write |
Prometheus text exposition import through the public extension parser. |
metrics |
GET, PUT |
/api/v1/scrape/targets |
metrics:read for GET; metrics:write for PUT |
Read or atomically replace the process-local scrape target set. |
metrics |
GET, POST |
/api/v1/query |
metrics:read |
Native exact-latest query with metric=, or PromQL instant query with query=. |
metrics |
GET |
/api/v1/export |
metrics:read |
VictoriaMetrics JSON-line raw export. |
metrics |
GET, POST |
/api/v1/query_range |
metrics:read |
Native range query with metric=, or PromQL range query with query=. |
metrics |
GET |
/api/v1/labels |
metrics:read |
Native label-name discovery. |
metrics |
GET |
/api/v1/label/{name}/values |
metrics:read |
Native label-value discovery. |
metrics |
GET |
/api/v1/series |
metrics:read |
Native series discovery. |
metrics |
GET |
/prometheus/api/v1/labels |
metrics:read |
Prometheus-compatible label-name alias. |
metrics |
GET |
/prometheus/api/v1/label/{name}/values |
metrics:read |
Prometheus-compatible label-value alias. |
metrics |
GET |
/prometheus/api/v1/series |
metrics:read |
Prometheus-compatible series discovery; requires match[]. |
metrics |
GET, POST |
/prometheus/api/v1/query |
metrics:read |
Stable PromQL instant endpoint. |
metrics |
GET, POST |
/prometheus/api/v1/query_range |
metrics:read |
Stable PromQL range endpoint. |
metrics |
GET, POST |
/metricsql/api/v1/query |
metrics:read |
Explicit MetricsQL instant compatibility tier. |
metrics |
GET, POST |
/metricsql/api/v1/query_range |
metrics:read |
Explicit MetricsQL range compatibility tier. |
logs |
GET |
/live |
none | Process liveness only; does not touch SQLite. |
logs |
GET |
/ready |
logs:stats |
Readiness plus build, storage, and queue accounting. |
logs |
GET |
/health |
logs:stats |
Alias of /ready. |
logs |
GET |
/metrics |
none | Prometheus text exposition of the plane's own operational stats plus timeless_build_info; unauthenticated like the probe endpoints. |
logs |
POST |
/insert/jsonline |
logs:write |
NDJSON ingestion into one public rich-log batch per request. |
logs |
GET, POST |
/select/logsql/query |
logs:read |
Native parameter query on GET; LogsQL compatibility grammar on POST. |
logs |
GET |
/select/logsql/field_values |
logs:read |
Bounded discovery for service, host, path, or status. |
logs |
GET |
/select/logsql/stats |
logs:stats |
Complete serialized StorageStats. |
logs |
GET, POST |
/select/logsql/tail |
logs:read |
Live tail: streams admitted entries matching one LogsQL filter expression as NDJSON; pipelines are rejected; slow consumers drop (counted in stats). |
logs |
POST |
/api/v1/flush |
logs:maintenance |
Ordered writer completion and extension durability barrier. |
logs |
POST |
/api/v1/backup |
logs:maintenance |
Flush, optimize, checkpoint, and verified SQLite backup. |
traces |
GET |
/live |
none | Process liveness only; does not touch SQLite. |
traces |
GET |
/ready |
traces:stats |
Readiness plus build, negotiated rich-span capability, and queue watermarks. |
traces |
GET |
/health |
traces:stats |
Alias of /ready. |
traces |
GET |
/metrics |
none | Prometheus text exposition of the plane's own operational stats plus timeless_build_info; unauthenticated like the probe endpoints. |
traces |
GET |
/select/traces/stats |
traces:stats |
Complete serialized StorageStats. |
traces |
GET |
/select/jaeger/api/services |
traces:read |
Sorted Jaeger service discovery. |
traces |
GET |
/select/jaeger/api/services/{service}/operations |
traces:read |
Sorted operations for one service. |
traces |
GET |
/select/jaeger/api/traces |
traces:read |
Jaeger trace search. |
traces |
GET |
/select/jaeger/api/traces/{trace_id} |
traces:read |
One Jaeger trace with all matching spans. |
traces |
GET |
/select/timeless/api/spans |
traces:read |
Native rich-span search for the Timeless dashboard contract. |
traces |
GET, POST |
/select/timeless/api/spans/tail |
traces:read |
Live tail: streams admitted spans as NDJSON, filtered by the search surface's live-matchable parameters (service, name, kind, status) plus an attributes JSON object of exact scalar matches; invalid values are rejected; slow consumers drop spans (counted in stats). |
traces |
GET |
/select/timeless/api/traces/{trace_id} |
traces:read |
One native trace with complete rich-span fields. |
traces |
POST |
/api/v1/flush |
traces:maintenance |
Ordered writer completion, extension flush, and durability barrier. |
traces |
POST |
/api/v1/backup |
traces:maintenance |
Flush, optimize, checkpoint, and verified SQLite backup. |
A backup occupies the signal's single writer for its whole duration — flush,
the entire optimize backlog, a WAL checkpoint, then the copy — so queued
ingest waits behind it. At most one runs per process at a time: an
overlapping POST /api/v1/backup is answered 409 Conflict with
{"error":"conflict","reason":"a backup is already running"} rather than
queued, so repeating the call costs the writer nothing. Retry after the
in-flight backup completes.
| traces | POST | /insert/opentelemetry/v1/traces | traces:write | OTLP JSON, protobuf, or gzip-compressed protobuf ingestion. |
An unregistered route always returns HTTP 422 with
{"error":"unsupported_capability","reason":"unsupported_route"}. The
servers never forward it to Rocket, Phoenix, another process, or another
storage owner.
The three serialized StorageStats responses obtain extension-owned tier,
block, index-byte, and optimizer-source accounting only from the public
timeless_stats TVF. Servers never query implementation-owned shadow tables.
Whole-database page/freelist and file/WAL/SHM sizes still come from public
SQLite PRAGMAs and filesystem metadata. If dbstat is unavailable,
sqlite_index_bytes is zero while all logical counts remain available.
Trace responses additionally pass through the cumulative public
attribute_index_fields, attribute_bloom_rows, and
attribute_bloom_bytes storage values, plus the cumulative public
query_decoded_columns, query_decoded_column_bytes,
query_materialized_values, and query_materialized_rich_values values as
extension_query_* fields; they remain aggregate diagnostics rather than
request-local attribution when readers overlap.
Every plane's /metrics exposition publishes the honest storage split as
separate families: timeless_<plane>_storage_bytes (the engine's
bytes_on_disk — data-block payload only), timeless_<plane>_index_bytes,
timeless_<plane>_wal_bytes, timeless_<plane>_freelist_bytes, and
timeless_<plane>_database_file_bytes. A compression ratio is
raw-vs-storage; index, WAL, freelist, and whole-file bytes are operational
series and are never part of one.
The raw side per plane: timeless_metrics_raw_ingested_bytes is
16 × total_points (8-byte timestamp + 8-byte value per sample — the
standard raw comparator; series identity is the amortized catalog), and is
lifetime-accurate because it derives from durable point counts.
timeless_logs_raw_ingested_bytes_total and
timeless_traces_raw_ingested_bytes_total surface the extension's
persisted ingest_raw_bytes_total counter: logical row bytes (log
timestamp + level + message + metadata; span ids + kind/status + timings +
string fields) counted once when rows become durable, monotonic under
optimize and prune, restart-safe. Rows still buffered in memory are not
yet counted, so the ratio briefly understates after bursts and
self-corrects on flush; pre-upgrade databases start the counter at their
next flush. The paired
timeless_logs|traces_compression_input|output_bytes_total counters are
exported alongside. The same values appear in the serialized StorageStats
responses; each plane README documents its exact series list.
GET and POST query endpoints consume URL-encoded parameters. On POST, form body values follow URL values and therefore win for a duplicated singleton parameter. Unknown Prometheus/MetricsQL parameters fail explicitly.
- Native latest requires
metric; other non-reserved parameters are exact label matchers. - Native export requires
metric;start/fromandend/toare epoch seconds and default to the previous hour and now. - Native range requires
metric; accepts the same bounds, a positive whole-secondstep(default 60), andaggregate=avg|min|max|sum|count|last|first|rate. Label values beginning with=~are regular expressions. Unsupported dashboard composition parameters fail rather than being ignored. - PromQL instant accepts
query, optionaltime, and optionallookback_delta. Range acceptsquery,start,end,step, and optionallookback_delta. - MetricsQL instant additionally accepts
stepandmax_lookback; range additionally acceptsmax_lookback. - Discovery accepts repeated
match[]ormatch. Native label values and series may also usemetric; the Prometheus series alias requires at least onematch[].
The exact expression compatibility and response behavior are in the
PromQL matrix and
query cookbook.
Prometheus-family success and error envelopes follow the documented
status, data, errorType, error, warnings, and infos contract.
POST /api/v1/import accepts VictoriaMetrics JSON lines. Valid rows from a
partially malformed request are admitted and parse failures are counted.
POST /api/v1/import/prometheus accepts Prometheus text exposition and gives
the complete body directly to the extension's public parser. A successful
204 means bounded writer-queue admission, not an implicit extension flush.
Scrape target replacement accepts:
{
"version": 1,
"targets": [{
"id": 1,
"job_name": "self",
"scheme": "http",
"address": "127.0.0.1:4000",
"metrics_path": "/metrics",
"scrape_interval_secs": 15,
"scrape_timeout_secs": 5,
"labels": {"instance": "local"},
"auth": null,
"enabled": true
}]
}The set is process-local control-plane state and must be supplied again after restart. Versions cannot move backward. A set has at most 10,000 targets; IDs are unique and positive; job and address are nonempty; intervals are 1–86,400 seconds; timeouts are 1–300 seconds and no longer than the interval; label names contain only ASCII letters, digits, and underscore. HTTP and HTTPS are supported. Each response is capped at 10 MiB. Bearer auth takes precedence over optional basic-auth username/password. Configured labels do not overwrite labels already present in a sample.
POST /insert/jsonline accepts one JSON object per line. _msg_field and
_time_field query parameters select alternate source keys (defaults _msg
and _time). The release table uses epoch microseconds. Every other retained
field remains typed/nested metadata, and all eight severities remain exact.
A fully valid request returns 204; a partially valid request returns 200 with
accepted/error counts. That response means admission to the bounded writer
queue, not durability; call the flush route for the ordered barrier.
Native GET /select/logsql/query rejects unknown parameters and accepts
level, message, service, host, path, status, start, end,
limit, offset, and order. order is asc or desc; bounds accept the
documented native time forms. Field discovery requires
field=service|host|path|status, accepts the same filters except offset/order,
and defaults to 1,000 values.
POST /select/logsql/query uses an URL-encoded form with required query and
optional allow_partial_response. false is the complete fail-closed mode.
true fails explicitly because one authoritative SQLite owner cannot produce
an honest distributed partial result. The complete shipped grammar, strict
errors, rich values, ordering, and intentional VictoriaLogs differences are
in the LogsQL matrix and
query cookbook.
POST /insert/opentelemetry/v1/traces accepts OTLP JSON by default and OTLP
protobuf when Content-Type contains application/x-protobuf. Protobuf may
also use Content-Encoding: gzip. The handler validates the complete request,
encodes one public rich-span-v2 batch, and waits for its SQLite insertion
statement before returning {"partialSuccess":{}}. That statement may leave
spans in the extension's authoritative 8,192-span buffer; call flush when a
durability barrier is required. There is no server-owned span buffer.
Jaeger search accepts service, operation, microsecond start and end,
non-negative limit (default 100), and minDuration/maxDuration duration
strings. Its compatibility limit counts selected spans before trace grouping,
so a limited trace may be incomplete. Unknown parameters return explicit 422.
Native dashboard span search accepts name, service,
kind=internal|server|client|producer|consumer,
status=unset|ok|error, nanosecond since/until, limit from 1 through
100 (default 100), non-negative offset, and order=asc|desc (default desc).
It returns the stored status description, typed attributes, events, resource,
and instrumentation scope rather than blanking fields for a lower-fidelity
wire format.
The marked inventory below is checked against production source before its test modules. Unless a row says otherwise, numeric values are positive base-10 integers and an invalid value stops startup with status 2.
| Variable | Applies to | Default | Meaning and constraints |
|---|---|---|---|
TIMELESS_ALLOW_NON_LOOPBACK |
all | unset | Only 1, true, or TRUE permits an explicitly supplied non-loopback TCP listener. |
TIMELESS_AUTH_MODE |
all | disabled |
Set required to enable token verification; auth is opt-in. |
TIMELESS_AUTH_POLICY_FILE |
all | none | Readable policy-v1 JSON path; required when TIMELESS_AUTH_MODE=required. |
TIMELESS_TENANT |
all | default |
Exact tenant required in both policy and token. |
TIMELESS_BACKUP_DIR |
all | backups/ beside the database file |
Directory backups are confined to; destinations that canonicalize outside it are rejected. Relative destinations resolve inside it. |
TIMELESS_ADMIN_KEY |
all | unset | When set, /api/v1/scrape/targets, /api/v1/backup, /api/v1/flush, and /api/v1/optimize additionally require it (header x-timeless-admin-key), independent of TIMELESS_AUTH_MODE. Unset means open. Header-only by design: a query-string key leaks into access/proxy logs and browser history. Keys must be visible ASCII (an HTTP header-value constraint). |
TIMELESS_METRICS_READER_CONNECTIONS |
metrics | 2 |
Independent bounded SQLite readers. |
TIMELESS_METRICS_COMMAND_QUEUE_BATCHES |
metrics | 256 |
Writer-command queue capacity in admitted request batches. |
TIMELESS_METRICS_QUEUE_BYTES |
metrics | 134217728 |
Queued-payload admission gate in bytes; admissions wait while in-flight ingest bytes exceed it (a batch larger than the gate is admitted alone). |
TIMELESS_METRICS_FLUSH_INTERVAL_SECS |
metrics | 10 |
Ordered public extension flush cadence. |
TIMELESS_METRICS_COMPACT_INTERVAL_SECS |
metrics | 300 |
Public compact/rollup cadence. |
TIMELESS_METRICS_RETENTION_INTERVAL_SECS |
metrics | 3600 |
Seven-day raw-retention prune check cadence. |
TIMELESS_METRICS_PROMQL_MAX_POINTS_PER_SERIES |
metrics | 11000 |
Evaluation-grid points per series; valid range 1–11,000. |
TIMELESS_METRICS_PROMQL_MAX_RESULT_POINTS |
metrics | 100000 |
Final serialized result points. |
TIMELESS_METRICS_PROMQL_MAX_WORK_POINTS |
metrics | 100000 |
Cumulative storage and intermediate evaluation points. |
TIMELESS_METRICS_PROMQL_MAX_RESPONSE_BYTES |
metrics | 16777216 |
Serialized response bytes. |
TIMELESS_METRICS_PROMQL_DEFAULT_SUBQUERY_STEP_MS |
metrics | 15000 |
Omitted PromQL subquery resolution in milliseconds. |
TIMELESS_METRICS_PROMQL_DEADLINE_MS |
metrics | 30000 |
Hard PromQL/MetricsQL execution deadline. |
TIMELESS_LOGS_READER_CONNECTIONS |
logs | 2 |
Independent bounded SQLite readers. |
TIMELESS_LOGS_COMMAND_QUEUE_BATCHES |
logs | 256 |
Writer-command queue capacity in admitted request batches. |
TIMELESS_LOGS_QUEUE_BYTES |
logs | 134217728 |
Queued-payload admission gate in bytes; admissions wait while in-flight ingest bytes exceed it (a batch larger than the gate is admitted alone). |
TIMELESS_LOGS_FLUSH_INTERVAL_SECS |
logs | 1 |
Ordered public extension flush cadence. |
TIMELESS_LOGS_OPTIMIZE_INTERVAL_SECS |
logs | 30 |
Bounded public optimize cadence. |
TIMELESS_LOGS_LOGSQL_MAX_RESULT_ROWS |
logs | 100000 |
Final rows; valid range 1–100,000. |
TIMELESS_LOGS_LOGSQL_MAX_WORK_ROWS |
logs | 100000 |
Cumulative decoded/examined rows and bounded state items. |
TIMELESS_LOGS_LOGSQL_MAX_RESPONSE_BYTES |
logs | 16777216 |
Response and bounded pipeline-state bytes. |
TIMELESS_LOGS_LOGSQL_DEADLINE_MS |
logs | 30000 |
Hard LogsQL/native-query deadline in milliseconds. |
TIMELESS_LOGS_INDEX_KEYS |
logs | absent/inherit | Comma-separated indexed-metadata allowlist. New stores are created with it; an existing store whose persisted allowlist differs is reindexed once at startup (postings rewritten for every block). Absent preserves the store's current allowlist. |
TIMELESS_LOGS_RETENTION |
logs | absent/inherit | Retention window as <n> plus a required unit suffix s, m, h, or d. Applied to new and existing stores at startup; enforcement remains at flush/optimize boundaries. Absent preserves the store's current window. |
TIMELESS_TRACES_READER_CONNECTIONS |
traces | 2 |
Independent bounded SQLite readers. |
TIMELESS_TRACES_COMMAND_QUEUE_BATCHES |
traces | 256 |
Writer-command queue capacity in admitted requests. |
TIMELESS_TRACES_QUEUE_BYTES |
traces | 134217728 |
Queued-payload admission gate in bytes; admissions wait while in-flight ingest bytes exceed it (a batch larger than the gate is admitted alone). |
TIMELESS_TRACES_RETENTION_SECS |
traces | absent/inherit | If absent, preserve the vtab's stored retention (a fresh table has none); positive sets and enforces it; 0 explicitly disables it. |
TIMELESS_TRACES_FLUSH_INTERVAL_SECS |
traces | 1 |
Ordered public extension flush cadence. |
TIMELESS_TRACES_OPTIMIZE_INTERVAL_SECS |
traces | 30 |
Bounded public optimize cadence. |
TIMELESS_BUILD_COMMIT is a build-time override, not a runtime setting. If
unset, the build script records the current Git commit when available. Cargo
supplies target and profile. These values are returned by --version and
health/readiness build identity.
Authentication is opt-in: servers start open, and verification engages
only under TIMELESS_AUTH_MODE=required with a policy file. For running the
binaries standalone — including non-loopback binding and sharing a database
with an embedding application — see
Running standalone.
Enabling it takes four commands — no Elixir control plane required:
timeless-authctl keygen --out ./auth
timeless-authctl policy init --signal metrics --key "$(cat ./auth/timeless-auth.pub)" \
--out ./auth/policy.json
TIMELESS_AUTH_MODE=required TIMELESS_AUTH_POLICY_FILE=./auth/policy.json ./timeless-metrics-api &
curl -H "Authorization: Bearer $(timeless-authctl token mint --key ./auth/timeless-auth.key \
--policy ./auth/policy.json --subject default --signal metrics --ttl 1h)" \
http://127.0.0.1:19439/api/v1/statustimeless-authctl policy add-subject narrows scopes per caller, and
timeless-authctl token inspect decodes a token without verifying it. For
operators who want ingest and query open but administration closed without
any token machinery, TIMELESS_ADMIN_KEY independently gates the
administrative routes (see the environment table). When enabled,
every route except the probe endpoints /live, /ready, and /health,
and the /metrics self-metrics endpoint (exact-match exemptions, so
container probes, load balancers, and Prometheus scrapers need no
credentials) requires an exact Authorization: Bearer <token>. Tokens are compact JWS/JWT values signed with
Ed25519 (alg=EdDSA); typ, if present, must be JWT. The policy contains
base64url-no-padding 32-byte public keys. Rust validates tokens but never
issues them.
Policy version 1 contains issuer, audience, tenant,
minimum_auth_version, positive max_token_seconds, maximum_limits, a
nonempty subjects object, a nonempty keys array, and optional
revoked_jtis. A subject contains exact allowed scopes, auth_version,
maximum_limits, and optional enabled (default true). A key contains
kid, public_key, not_before, expires_at, and optional revoked.
Audience convention: timeless-<signal> — timeless-metrics,
timeless-logs, timeless-traces. authctl policy init writes this, and
authctl mint copies the policy's audience into the token's aud, so an
authctl-managed deployment satisfies it automatically. Hand-written policies
and third-party minters should follow it.
The verifier does not enforce the shape: it requires only that the token's
aud equals the policy's audience (any matching pair is accepted), and it
separately rejects a token whose signal does not match the server's. A
per-signal audience is therefore defence in depth rather than the primary
control — it keeps a policy minted for one signal from being repurposed for
another by editing the server it points at.
Token claims are iss, aud, sub, jti, tenant, signal, scopes,
auth_version, iat, nbf, exp, and limits. The signal is exactly
metrics, logs, or traces. The seven positive limit fields are:
| Claim limit | Enforcement |
|---|---|
max_request_bytes |
Auth middleware buffers and caps the request before route execution. The hard server body cap remains 10 MiB. |
max_decompressed_bytes |
Caps gzip OTLP protobuf after decompression; other current request formats are not compressed. |
max_response_bytes |
Auth middleware caps the final body; signal query limits may be tighter. |
max_query_rows |
Prechecks limit, max_rows, max_points, and the LogsQL limit pipeline stage; then verifies the handler's internal exact result-row header. |
max_request_ms |
Deadline for read and stats routes. Writes are not response-cancelled because queued durability would make timeout ambiguous. |
max_concurrent_requests |
Per-subject active request cap. |
max_queue_ms |
Per-subject admission wait before HTTP 429. |
Claim limits cannot exceed either policy-wide or subject-wide maxima, and auth cannot raise the signal server's hard query/body limits. A token may not claim a scope absent from its subject even if the current route needs only one of the claimed scopes. Policy files are at most 1 MiB; bearer tokens are at most 32 KiB; not-before and issued-at checks allow 30 seconds of positive clock skew. Policy replacements are noticed by size, modification time, and file identity so atomic same-size rewrites reload correctly.
Scope derivation is deterministic: readiness, health, and paths ending in
/stats use <signal>:stats; paths ending in /flush, /optimize, or
/backup use maintenance; remaining GET/HEAD and query/discovery routes use
read; other routes use write. The exact result is listed beside every route
above.
Backpressure rejections carry Retry-After, in whole seconds, derived from
the subject's own max_queue_ms — exactly how long admission was willing to
hold the request — rounded up to a 1-second floor (RFC 9110 has no sub-second
form). It is sent only for queue_wait_exceeded (429) and queue_unavailable
(503): rejections a retry cannot fix, such as bad credentials, a wrong tenant,
or an oversized payload, deliberately carry no hint.
RateLimit-* headers are deliberately not sent. Those describe a request
quota over a time window; the limit enforced here is max_concurrent_requests,
a concurrency cap with no window, so populating them would misreport what the
server actually enforces.
Authorization errors use
{"error":"data_plane_authorization","reason":"<code>"}. Stable reason
codes are:
- credential/token/policy:
missing_credentials,invalid_credentials,invalid_token,invalid_key,invalid_signature,unknown_key,unknown_subject,authorization_policy_unavailable,authorization_policy_invalid,clock_unavailable,expired_token,token_lifetime_exceeded,token_not_yet_valid,revoked_key,revoked_token, andstale_auth_version; - identity/authority:
wrong_issuer,wrong_audience,wrong_tenant,wrong_signal,insufficient_scope,scope_policy_exceeded, andclaim_limits_exceeded; - limits/admission:
request_bytes_exceeded,response_bytes_exceeded,invalid_query_limit,query_rows_exceeded,request_time_exceeded,queue_unavailable, andqueue_wait_exceeded.
Credential failures are HTTP 401, authority failures HTTP 403, malformed limit parameters HTTP 400, byte caps HTTP 413, row caps HTTP 422, admission exhaustion HTTP 429/503, and read deadlines HTTP 504.
Startup fails closed before binding the listener if configuration, auth
policy, extension loading, capability negotiation, data ABI, minimum version,
signal batch support, public vtab schema, stored retention, or database schema
is incompatible. The current server data-schema ledger version and extension
data ABI are both 1. A database written by a newer schema version is not
mutated. A pre-ledger database receives the additive, idempotent
_timeless_schema_migrations v1 row on the writer connection.
Each server holds an advisory exclusive
<database>.timeless-<signal>-api.lock lease. This fences another conforming
server owner. It cannot protect against an unrelated SQLite process that
ignores the lease; an operator must not give another writer the database while
the server owns it.
The writer is a bounded FIFO. Scheduled flush/compact/optimize/retention work
uses the same ordered public command path as explicit maintenance. On SIGINT
or SIGTERM, Axum stops new admission and drains accepted HTTP requests. The
server stops periodic maintenance, closes readers, places a final flush behind
all admitted writer commands, checkpoints the WAL with TRUNCATE, joins its
workers, and releases the owner lease. Metrics also stop all scrape tasks.
Shutdown returns failure if the final flush or complete checkpoint fails.
SIGKILL cannot run this sequence. Already flushed SQLite transactions remain recoverable; an extension-buffer tail is intentionally not claimed durable.
Explicit flush is the durability boundary for every signal. Metrics and
traces return exact admitted/completed/failed/queued/in-flight watermarks;
logs returns {"status":"ok"} after the same ordered writer barrier.
Every backup route accepts exactly:
{"destination":"/absolute/path/to/new-backup.db"}The parent directory must already exist. The destination must be absolute,
must name a file, and must not exist. Backup runs on the established sole
writer connection: metrics flushes and compacts/rolls up; logs and traces
flush and drain actionable optimize backlog. The writer then requires a
complete WAL TRUNCATE checkpoint, copies pages with SQLite's online-backup
API, runs PRAGMA quick_check, validates the signal schema ledger, fsyncs the
staging file, and publishes without overwrite in the destination directory.
An error removes the staging name and does not label a partial copy complete.
The success report contains signal, final destination, bytes, copied pages, schema version, WAL/checkpoint frame counts, completion Unix seconds, and elapsed nanoseconds. Restore is deliberately offline: stop the owner, retain the current database and WAL/SHM as rollback material, copy or rename the verified backup into the configured database path, and start the same or a compatible newer server. Startup performs the capability/schema check before serving. Do not replace a live database file behind an owner process.
The auth envelope is documented above. Signal handlers use these additional stable families:
| Family | Status | Shape |
|---|---|---|
| Unsupported route/parameter/capability | 422 | {"error":"unsupported_capability","reason":"..."}; LogsQL may add message. |
| Malformed LogsQL | 400 | {"error":"invalid_query","reason":"malformed_logsql","message":"..."} |
| LogsQL execution conflict | 422 | {"error":"query_execution","reason":"field_conflict","message":"..."} |
| Logs query limit | 422 | {"error":"query_limit",...} with reason max_result_rows, max_work_rows, or max_response_bytes and numeric limit. |
| Logs query timeout | 504 | {"error":"timeout","reason":"query_deadline","deadline_ms":N} |
| Prometheus/MetricsQL parse | 400 | {"status":"error","errorType":"bad_data","error":"..."} |
| Prometheus/MetricsQL execution/limit | 422 | Same envelope with errorType="execution". |
| Prometheus/MetricsQL timeout | 504 | Same envelope with errorType="timeout". |
| Native metrics server/client | 500/400 | {"status":"error","error":"..."} |
| Native logs server | 500 | {"error":"..."} |
| Native traces server/client | 500 or route-specific 400/413 | {"status":"error","error":"..."} or {"error":"..."} |
Unknown parameters and unsupported language constructs fail explicitly; no server silently delegates to Elixir or another query implementation. Detailed language diagnostics and intentional compatibility differences are versioned with their matrix rows.
The intended release matrix covers x86-64 and AArch64 Linux GNU and macOS.
Linux loadable extensions use .so; macOS uses .dylib. A complete archive
contains the telemetry extension and all three signal-specific binaries.
dbhealth is a separate extension artifact and is not registered by these
servers.
v0.4.1 passed all four intended native package jobs and the complete outer
checksum gate. The archives are retained as authenticated GitHub Actions
artifacts until 2026-11-06; the workflow did not attach them to a permanent
GitHub Release. Other targets may compile from source but are not claimed as
release artifacts. Exact archive paths, current channel status, checksums,
manifest/SBOM content, install layout, and removal behavior are in the
artifact inventory.
The binaries use TCP, a local SQLite/libSQL-compatible database file, WAL
mode, and the loadable extension API. A direct Rust embedding that does not
need HTTP should use the mutually exclusive embedded crate feature,
timeless_ext::register_telemetry, and the
embedded guide instead of launching a signal server.
Before replacing either side of the binary/extension pair, follow the compatibility statement and upgrade and rollback guide.