Skip to content

Releases: paimonsoror/telltaledns

edge (main @ a012bf2)

edge (main @ a012bf2) Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Oct 15:08

Built from main at a012bf2. Not a stable release: install with install.sh --edge or telltale self-update --channel edge.

TelltaleDNS 0.5.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 19:42

A release for seeing your network over time and from new angles, and the release that passes the v1.0 gate's performance, memory, and security checks.

Highlights

  • When things happen. Monitor → Heatmap shows the network's queries (or blocked queries) by day and hour, 15 minutes, or minute, or a typical week; open it for one device or one name too. Select a cell for the query log at that time.

  • Where queries go. Monitor → Flow draws every answer from group to decision (cached, forwarded, blocked, local) to upstream to response code. Every band is an exact count, never an estimate; SERVFAIL bands are red. For the whole network or one device, with Live refreshing it every 15 seconds.

  • Who spent the error budget? Under each service-level objective, the bad answers broken down by device, name, upstream, status, response code, and node, with the budget left drawn over time.

  • Answer times as a heatmap. The dashboard's Where time goes card (and each upstream's page) shows answers over time in latency bands, with the latency objective's threshold as a line. Its dots are real queries: select one to open it in the query log.

  • Every change on every chart. Time charts draw a thin line for each configuration change (who made it, from where, which sections), restart, list refresh, rollout, maintenance window, pause, cache flush, and upstream breaker change. Operators and agents can add notes.

  • The odd node out. Cluster → Compare puts every node side by side (queries per second, SERVFAIL, slow answers, cache, upstream failures, p50, p99) and outlines a node that really deviates, with a note when one only looks worse because it has little traffic.

  • A device's rhythm and day. On the Clients page, Rhythm shows how regularly a device asks for each domain (a time-sync loop every 2.5 s is one dark cell), and Day shows its domains as swimlanes over 6 hours to 7 days, with blocked bursts, slow answers, first contacts, findings, and changes of identity marked. A device asking one domain faster than every 30 seconds for an hour is reported as a chatty loop, for your information only.

  • A wall display. #/wall is a full-screen page for a monitor or tablet: the traffic map, the flow, the service level, answer times, and the week, rotating or all at once depending on the screen, with live queries along the bottom. An admin can make a wall display link for a screen nobody sits at: it stays signed in, can only show the wall, and can be limited to the screen's address.

  • A list that suddenly shrinks waits. When a list download arrives with less than half the lines it had ([filter] max_shrink_percent, default 50), the previous copy keeps blocking until the next refresh brings the same content; Refresh now accepts it at once.

  • Measured for v1.0. The shipped build idles at 13.7 MiB and holds a million blocked names with 100,000 cached answers in 60 MiB. On a Raspberry Pi 4, a recompiled 2-million-name filter answers 4.7 s after the change. With the load sent from another machine, 99.999 % of cached and blocked answers left the server within 250 µs.

  • Verifiable builds. Release binaries carry their dependency list (cargo auditable), each comes with a CycloneDX SBOM, container images are signed with cosign (keyless), and two builds of the same commit are identical.

  • Also new: the dashboard's controls (time range, Showing, Top lists, Changes) and every switch and drop-down across the site follow one style; view toggles are switches and view filters are pill drop-downs.

Fixes

  • Security review (v1.0 gate). Seventeen findings fixed, in sign-in, the cluster channel, and the parsers; see docs/security-review.md. The two rated high were both in the cluster: a resolver pod that left and came back with its still-valid certificate could be trusted with the cluster key, and an RPC answer from any member could answer a call made to another. Also: a query name with an escaped dot in a label got past the lists.
  • Zone transfers (AXFR, IXFR) are refused instead of forwarded upstream.
  • An expired SERVFAIL is never served stale.
  • Cluster page: a node doing under 1 query per second showed 0 q/s, and resolver pods showed a 0 % cache hit rate (refreshed answers counted as misses). Both now match the dashboard.
  • Helm alerts covered only the controller. With prometheusRule.enabled in scaled mode, the alerts and SLO rules now also watch the resolver pods, which answer most queries.
  • Quieter logs: resolver pods no longer log an error for OIDC and a warning for router integrations they deliberately don't have the secrets for; a replica no longer repeats its file's warnings, or the "shared settings ignored" warning, on every change from the primary.

Upgrading from 0.4

Most installs need no change. Check these if they apply to you:

  • Upgrading a cluster: check the registry first. Nodes now trust only members the cluster's registry lists. Before upgrading, check (read-only) that telltale cluster status on the primary, or the Cluster page, lists every node; one missing would come back as a plain ephemeral member (not eligible to be primary). Then upgrade replicas first, then the primary: a 0.4 node refuses a configuration that uses a new setting, keeps the one it had, and logs upgrade this node.
  • Scripts that manage an account with a token: changing a password or two-factor sign-in, and creating or deleting API tokens, now need a signed-in session (POST /api/v1/auth/login, then the cookie and X-CSRF-Token). An API token, an agent token, or HTTP Basic gets 403. Revoking a wall display still works with an admin token.
  • Resolver pods no longer receive users and token hashes; they serve no API, so nothing users do changes.
  • API and metrics: qps on cluster nodes (GET /api/v1/cluster) is now a decimal (two places) instead of a whole number, and so is telltale_cluster_peer_queries_per_second. telltale_cluster_peer_cache_hit_ratio now counts refreshed and stale answers as hits.
  • Helm chart, alerts: if you use prometheusRule.enabled in scaled mode, alerts can now fire for resolver pods too. With kube-prometheus-stack, give the rules its release label (prometheusRule.labels: {release: <its release name>}) or Prometheus won't load them.
  • Lists: the new shrink guard is on by default; set [filter] max_shrink_percent = 100 to turn it off.
  • Flow history starts at the upgrade: minutes counted by 0.4 have no flow counts, and the card says so.

Known issues

  • DNSSEC validate mode: right after a start, an upstream that closes fresh DoT connections can make the first validations fail, so a signed name may get SERVFAIL until its keys are cached. Keep permissive or off for now; see ADR-098.
  • _dns.resolver.arpa (encrypted-DNS discovery) is forwarded upstream instead of being answered locally.

For developers

  • New ADRs: 126–147 (all Proposed); ADR-120 amended (resolver pods log missing router and OIDC secrets as info).
  • New MCP tools: activity_heatmap, query_flow, slo_attribution, compare_nodes, device_timeline, annotate, list_annotations.
  • New API paths: /stats/heatmap, /stats/flow, /stats/latency/heatmap, /stats/latency/exemplars, /stats/slo/attribution, /cluster/compare, /clients/{id}/rhythm, /clients/{id}/timeline, /annotations, /displays, /auth/display.
  • The cluster heartbeat carries queries_last_minute (optional; older nodes read as qps × 60).

Full Changelog: v0.4.1...v0.5.0

TelltaleDNS 0.4.1

Choose a tag to compare

@github-actions github-actions released this 12 Oct 21:29

A small fix release for clusters: every node now names a device the same way.

Fixes

  • Device names are the same on every node. In a cluster, usually only one node reads your router's client list (the one with its API key), and pods can't hear mDNS. So the query log named a device ("living-room-tv") in that node's rows and showed only its address in the other nodes' rows, and resolver pods' anomaly findings and device identities missed the router's names too. Now every node asks the others once a minute for the names their routers and mDNS learned, and uses them after its own. Within a minute of starting, a resolver pod names devices exactly as the primary does, without the router's credentials. The cluster query log also names any row that reaches it without a name.

Upgrading from 0.4.0

  • Nothing to change. Once every node runs 0.4.1, the names line up. A node still on 0.4.0 keeps naming its own rows as before, and the others skip it when they ask for names.
  • If your resolver pods carry a [[router]] block they can't read (no credentials mounted), it still logs one warning at start. You can drop it from their configuration: they now get the names from the node that reads the router.
  • Order of names, unchanged for a single node: the name you gave a device, then this node's routers, then this node's mDNS, then the other nodes' routers and mDNS (the primary's first).

Known issues

  • DNSSEC validate mode: right after a start, an upstream that closes fresh DoT connections can make the first validations fail, so a signed name may get SERVFAIL until its keys are cached. Keep permissive or off for now; see ADR-098.
  • _dns.resolver.arpa (encrypted-DNS discovery) is forwarded upstream instead of being answered locally.

For developers

  • New ADR: 125 (device names are shared across the cluster). New cluster call devices.names.

Full Changelog: v0.4.0...v0.4.1

TelltaleDNS 0.4.0

Choose a tag to compare

@github-actions github-actions released this 12 Oct 03:37

An observability and operations release: see what your network is reaching, hold DNS to targets you set, and try a change against your real traffic before you make it.

Highlights

  • Traffic map. A new page under Monitor answers "what is the network reaching right now?" in one picture: a tile per service, site, or name, sized by how often it was looked up this hour, blue for answered and red for blocked. It refreshes every 5 seconds, rings the tiles growing fastest, and opens any tile's names in the query log. It reads the same top lists as the dashboard, so it adds no work to answering queries.

  • What would this have done? Before you save a quick rule, a list, a group change, a device move, or an upstream change, replay the query log with it: the last day's queries are decided again with and without the change, and you see what would have been newly blocked, newly allowed, sent to another upstream group, or answered differently, by name, device, and group, with the rule or list that decided. Only the changed lists are compiled, and DNS never waits for it. It's a button in the UI, simulate=24h on any dry run, POST /api/v1/simulate and telltale config simulate for a whole configuration (test a Git commit before pushing it), and simulate_change for agents. In a cluster every node replays its own log.

  • What is this device? The Clients page says what each address looks like ("Looks like a Roku player"), from its maker (the IEEE registry), the name it announces, and the domains it talks to, scored against a catalog of 45 common home devices that ships with TelltaleDNS. The guess fills in the naming form, and a group that lists the kinds of device it's for (device_classes = ["camera", "plug"]) is suggested for them. Nothing is named or moved on its own; set "What it is" on a device to correct it. Agents get identify_device.

  • Service-level objectives. Targets for answers that work (not SERVFAIL, 99.9% by default) and answers that are quick (99% within 250 ms), each with an error budget over 30 days and burn rates from 5 minutes to 3 days. A fast burn turns the health icon amber and can alert (slo_burn), while one failed lookup on a quiet night doesn't. In the dashboard's Advanced view, GET /api/v1/stats/slo, slo_status for agents, and Prometheus recording rules and alerts in the Helm chart with the same definitions.

  • Asked like a device would. Every node asks each of its listeners a question over UDP, TCP, DoT, DoH, or DoQ every 30 seconds, plus any address you add (a load balancer in front), and watches each TLS certificate's expiry. A listener that stops answering, or a certificate within 14 days of lapsing, shows on the health icon, in metrics, and can alert. Settings → System → Listener checks.

  • Try a list before it blocks. A list with mode = "shadow" is compiled but never blocks: the Lists page shows what it would have blocked (names, devices, how often), so you can judge it first. Blocked names that devices keep retrying, or that someone allowed right after a block, are listed as likely over-blocking, with the list that decided.

  • A second opinion on upstreams. Per upstream: DNSSEC verdicts and the error codes it attaches to answers (Blocked, Censored, Filtered mean it filters). Optionally ([upstream_check] sample_every, off by default), a sample of forwarded questions is asked again of another upstream and the answers compared, both shown side by side.

  • From a spike to the query. The latency histograms on /metrics carry OpenMetrics exemplars: click a dot in Grafana and its trace ID opens that query in the query log. Sampled and slow queries can go to your tracing backend as OpenTelemetry traces with the same ID ([telemetry.otlp] traces_sample_every, traces_slow_ms).

  • Is the cache the right size? Each node estimates what a cache 25%, 50%, or 100% larger would have answered, from a small sample of names asked again after they were evicted, and says whether to grow it, shrink it, or leave it (Cache page, cache_advice, metrics).

  • Expired answers as fast as fresh ones. With [cache] stale_answer_client_timeout_ms = 0, a name whose cached answer just expired is answered at once and refreshed after the reply has gone out. It has its own status, refreshed, and at low load its p99 dropped from about 250 µs to 50 µs. The dashboard's cache tile counts fresh, refreshed, and stale answers as one number; the Advanced view splits them.

  • Only what matters. [exclusions] keeps names (with their subdomains) and devices out of the query log, live view, top lists, anomalies, and exports while they're still answered and counted in Prometheus: uptime monitors, connectivity checks, time servers. Settings → Query handling, or your Git configuration. A URL list whose content hasn't changed in 30 days (stale_after_days) is flagged as stale.

  • Staged rollouts and pins. In a cluster, a configuration change can reach canary nodes first (node IDs, a site, or the Kubernetes resolver pods) and every other node after a bake time, if their SERVFAIL share stays normal. A failed bake pins the cluster to the version before. You can pin to an earlier version by hand and unpin. Off until you name canaries: Settings → Cluster → Staged rollouts, [cluster.rollout], or the Helm chart's rollout values.

  • Maintenance mode. Before swapping a Pi's SD card or moving a box, put the node in maintenance (Cluster page, telltale ctl maintenance start, the API, or an agent with ops:maintenance): for a set time it reports not ready, so load balancers move away, keeps answering whatever still arrives, and is left out of alerts, the health level, and elections. Under automatic failover, the primary hands its role to another node first.

  • Also new: NSID, so dig +nsid names the node or pod that answered ([dns] nsid); no IPv6 addresses for a group (filter_aaaa); tabs on the long Cluster, Settings, and Clients pages (a tab with a problem in it says so); router integrations verify a self-signed console by the name in its certificate (tls_server_name); and task guides on the project site, from building a cluster to changing things safely.

Fixes

  • Cluster charts in Kubernetes lost history. Once a cluster had sent a few MB of minutes to the primary, its charts dropped the history of nodes that weren't online, and the primary logged rollups: state database: disk I/O error every few seconds. The database needed a temporary file for a sort, and the image's read-only filesystem has none. Temporary data now stays in memory, and the query uses an index instead of a sort.
  • Settings editors on cluster nodes said "None yet" for upstreams, lists, and other settings that came from the primary.
  • The phone menu now closes on any link, a tap beside it, or Esc.

Upgrading from 0.3.0

Most installs need no change. Check these if they apply to you:

  • Upgrading a cluster: upgrade replicas first, then the primary, and upgrade every node before you use a new shared setting (exclusions, staged rollouts, simulation, device identification, listener checks, SLOs). A node still on 0.3.0 refuses a configuration with settings it doesn't know, keeps the one it had, and logs upgrade this node. A 0.3.0 node also doesn't take part in simulations (it's listed under missingNodes).
  • New checks are on by default. Listener checks ([probe]), SLOs ([slo]), and stale-list warnings ([filter] stale_after_days = 30) can turn the health icon amber and can alert. Turn any off with enabled = false (or stale_after_days = 0). Probe queries never reach the query log or the statistics. Device identification is on too ([identify] enabled); it only suggests.
  • Prometheus: /metrics answers in OpenMetrics, with exemplars, to scrapers that ask for it (Prometheus does). Set [telemetry.metrics] exemplars = false to always serve the plain text format.
  • Dashboards and alerts on cache status: with stale_answer_client_timeout_ms = 0, expired answers given at once are now refreshed, not stale, in telltale_queries_total and the query log. stale now means only that upstreams failed or were slow.
  • Helm chart: with prometheusRule.enabled, the chart now adds SLO recording rules and burn-rate alerts (prometheusRule.slo.enabled: false to skip). Keep their targets equal to the nodes' [slo] settings.
  • Agent tokens: simulating needs the querylog:read scope as well as the change's own. New scopes: config:write:exclusions, config:write:simulate, and ops:maintenance.
  • Scripts reading /readyz: the status codes are unchanged (200 ready, 503 not); the body is now JSON with the reason ({"ready":false,"reason":"maintenance"}) instead of ready/not ready. The Helm chart's startup probe asks /readyz?startup=1.
  • Dashboard: the Service level, Where time goes, Upstreams, Upstream exchanges, and traffic-by-group cards are in the Advanced view (the switch at the top). Problems still reach everyone through the health icon and alerts.

Known issues

  • DNSSEC validate mode: right after a start, an upstream that closes fresh DoT connections can make the first validations fail, so a signed name may get SERVFAIL until its keys are cached. Keep permissive or off for now; see ADR-098.
  • _dns.resolver.arpa (encrypted-DNS discovery) is forwarded upstream instead of being answered locally.

For developers

  • New ADRs: 105–124. ADR-115 amended (the comparison re-decides the configuration in effect; the result is simulation; refused rows are replayed; no new heartbeat field).
  • New MCP tools: simulate_change, identify_device, slo_status, probe_status, cache_advice, upstream_checks, shadow_lists, overblocking_suspects, rollout_status, plan_set_exclusions, plan_pin_version, plan_unpin, start_maintenance, end_maintenance.
  • The site's traffic map and dashboard screenshots come fr...
Read more

TelltaleDNS 0.3.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 22:02

A hardening and visibility release: a full review of the observability, clustering, and API layers, a dashboard for every node, acknowledgeable anomalies across the cluster, and one health icon for the whole deployment.

Highlights

  • Health at a glance. An icon at the bottom of the menu says whether everything is working: healthy, degraded (an upstream or node down, a device rate-limited, a list failing, a disk filling up), or severe (no upstream in a group answering, DNS not served). Click it for each reason, its node, and where to look. The same answer is at GET /api/v1/system/health and in the MCP tool health.
  • Anomalies for the whole cluster, and a way to clear them. The Anomalies page now shows every node's findings, including those of Kubernetes resolver pods, and anomaly alerts see them too. Acknowledge a finding once you've looked: it stops counting as new on every node, including one that was down at the time. Operators can do it from the UI, the API, or an agent with the new ops:anomalies scope.
  • The dashboard, node by node. Showing switches between every node together, one node, or one site. Top clients show their groups.
  • Upstream failures by kind. Each upstream's failures are split into timeouts, connection errors, bad replies, SERVFAIL, REFUSED, and more, in the API and the Upstreams page. A run of SERVFAIL or REFUSED answers alone no longer marks an upstream as dead.
  • Tunable limits. The per-client rate limit can be changed from the UI, the API, or an agent. TCP, DoT, DoH, and DoQ listeners cap connections per client address. The advertised EDNS size is configurable ([dns] edns_payload).
  • Tighter clusters. A join token now decides whether the node it brings may be promoted (--eligible) or is a witness (--witness), instead of the joining node. Join tokens can be listed and revoked. Only voters can fence a primary. Election leases use a clock that never steps. A Git-managed node takes over from an emergency primary by itself.
  • Tighter API. Password checks are bounded, a list check doesn't echo what it fetched, configuration writes run one at a time, admins can revoke other users' API tokens, and promotion from a signed-in session asks for the password and code.
  • Privacy levels cover everything. Levels 1 and up now also govern the live analytics, the stored top lists, and the anomaly engine. Level 1's name hashes are keyed with one key per cluster.

Upgrading from 0.2.0

Most installs need no change. Check these if they apply to you:

  • Joining eligible nodes and witnesses: create the token with telltale cluster token create --eligible (or --witness). A join with --eligible on a plain token is now refused. Nodes that already joined keep their roles.
  • Behind a reverse proxy or ingress: X-Forwarded-Proto is trusted only from [api] trusted_proxies (or loopback). If the UI is served over HTTPS through a proxy, set trusted_proxies to its network so sign-in cookies stay Secure.
  • One client address with many TCP, DoT, DoH, or DoQ connections: each listener allows 32 per address by default (max_connections_per_address; 0 for no limit). Raise it behind a load balancer that hides client addresses.
  • Lists with more than 1,000 regex rules: the extra rules are left out of the snapshot and reported on the Lists page. Raise [filter] max_regexes if you need them.
  • List URLs on link-local addresses (169.254.0.0/16, fe80::/10, cloud metadata) are refused unless [filter] allow_link_local_urls = true.
  • Audit log: entries older than [auth] audit_retention_days (365 by default; 0 keeps everything) are trimmed hourly, with a checkpoint so the chain still verifies. If the audit log can't record, changes are refused with 503 instead of going unrecorded.
  • Privacy level 1 and up: the anomaly engine is off at these levels. At level 1, names hashed before the upgrade don't group with names hashed after it, because the hash is now keyed.
  • The setup token is no longer written to the log. Print it with telltale auth setup-token. Cluster members other than the primary don't create one.
  • Upgrading a cluster: mixed versions work together. Upgrade replicas first, then the primary.

Known issues

  • DNSSEC validate mode: right after a start, an upstream that closes fresh DoT connections can make the first validations fail, so a signed name may get SERVFAIL until its keys are cached. Keep permissive or off for now; see ADR-098.
  • _dns.resolver.arpa (encrypted-DNS discovery) is forwarded upstream instead of being answered locally.

For developers

  • The README and the project site show the dashboard on a made-up demo network; ui/tests/demo/run.sh regenerates those screenshots.
  • New Proposed ADRs: 099–104 (connection limits, EDNS size, the regex cap, privacy levels, acknowledged anomalies, the health level).

What's Changed

New Contributors

Full Changelog: v0.2.0...v0.3.0

TelltaleDNS 0.2.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 20:44

A footprint and DNSSEC release: TelltaleDNS uses much less memory, and DNSSEC validation gets the corrections a live trial called for.

Highlights

  • Less memory. A Raspberry Pi 4 with 2.7 million blocked names went from about 83 MiB to about 50 MiB.
    • The domain lists are memory-mapped and their pages released once the lookup index answers queries.
    • Each cached answer is one allocation instead of three.
    • The response buffer for a query sent upstream is allocated when the answer arrives, sized to it. Before, it was a 64 KiB buffer held for the whole wait.
  • DNSSEC you can trust in permissive mode. Names in unsigned zones reached through CNAMEs (Netflix, Amazon, BBC) were judged bogus by the validation library. TelltaleDNS now proves for itself that such a zone is unsigned (the parent's signed denial of its DS) and answers it as insecure. Proven zones are remembered for 15 minutes, which also ends a slow lookup loop on some of those names.
    • In permissive mode every bogus answer is logged with its name and reason, so a trial shows what validate would refuse.
    • A validation that hits the library's depth limit is treated as indeterminate (answered without AD), not bogus.
  • Fewer failed lookups. When an upstream closes a DoT or TCP connection after a query was sent but before answering, the query is retried once on a fresh connection.
  • Quieter logs. The validation library's own warnings are off (TELLTALE_LOG_DNSSEC_LIBRARY=1 turns them back on), and cluster replicas no longer repeat configuration warnings on every replicated change.

Upgrading from 0.1.0

  • [cache] max_bytes now counts real memory. It used to undercount each entry by about a third, so the same setting now holds fewer entries. The cache uses about the memory you asked for. Raise the limit if you sized it by entry count.
  • The query log starts a new segment part after 16,384 distinct names in an hour (was 65,536), which keeps its memory near 1 MiB. Busy hours write more, smaller parts; searches are unchanged.
  • Nothing else to change in your configuration.

Known issues

  • DNSSEC validate mode: right after a start, an upstream that closes fresh DoT connections can make the first validations fail, so a signed name may get SERVFAIL until its keys are cached. Keep permissive for now and read the log; see ADR-098 and the roadmap (T10.9).
  • _dns.resolver.arpa (encrypted-DNS discovery) is forwarded upstream instead of being answered locally.

For developers

  • bench/gate.py measures the v1.0 gate on one machine; docs/v1-gate.md tracks every gate.
  • bench/memprobe.py shows memory by mapping. bench/heapprof.py with the dhat-heap cargo feature (development only) names the code holding the heap.
  • The release workflow's version bump now also updates the OpenAPI document.

Full Changelog: v0.1.0...v0.2.0

TelltaleDNS 0.1.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 03:35

The first stable release of TelltaleDNS: a filtering, encrypted, observable DNS resolver that runs on a Raspberry Pi, in Kubernetes, or on both as one cluster.

Highlights

  • Fast and small. Every CPU core answers on its own, with no garbage-collection pauses; cache hits take about 150 ns. About 60 MB of memory on a Pi 4 with 2.7 million blocked names.
  • Blocking you can explain. Hosts files, AdBlock-style lists, and regexes are compiled in the background into one compact snapshot. Every blocked answer says which list and line decided it, in the query log, the API, and telltale explain.
  • Groups for your networks. Each VLAN or set of devices gets its own lists, blocked services, safe search, schedules, rewrites, DNS64, and upstream servers. Every chart and log line shows which group a question came from.
  • Encrypted both ways. DoT, DoH (HTTP/2 and HTTP/3), and DoQ for your devices; DoT, DoH, DoQ, DNSCrypt, and DNS stamps to upstreams; DNSSEC validation, aggressive NSEC (RFC 8198), or full recursion from the root.
  • Observable. Per-question timing and percentiles, a searchable columnar query log, a live view across the cluster, Prometheus metrics with a Grafana dashboard, device anomaly detection, alerts (email, ntfy, Gotify, Slack, webhooks), and exports to syslog, OpenTelemetry, and dnstap.
  • One cluster, one place to manage it. A Pi and Kubernetes nodes form one cluster with replicated configuration, write forwarding, automatic failover, and configuration from Git.
  • Ready for AI agents. Scoped agent tokens or SSO, an MCP server whose changes come as plans you approve, and vqlog for analytics in one line.
  • Easy to try. Brings over a Pi-hole or Technitium setup; installs as a container, a Helm chart, or a native systemd service with a guided installer (and --uninstall).

Install

  • Container: ghcr.io/paimonsoror/telltale:0.1.0 (also :0.1, :0, :latest) for amd64, arm64, and arm/v7.
  • Kubernetes: helm install telltale oci://ghcr.io/paimonsoror/charts/telltale --version 0.1.0
  • Raspberry Pi or Linux: curl -fsSLO https://raw.githubusercontent.com/paimonsoror/telltaledns/v0.1.0/deploy/systemd/install.sh && sudo sh install.sh

Binaries are signed: SHA256SUMS.minisig verifies with the key in deploy/release/telltale-release.pub, and install.sh and telltale self-update check it for you.

Upgrading from edge builds

Nothing to change in your configuration. Native installs on the edge channel can stay there (telltale self-update --channel edge) or move to stable (telltale self-update). Container and Helm users can switch from :edge to a version tag.

Documentation: https://paimonsoror.github.io/telltaledns/ · Every design decision: https://paimonsoror.github.io/telltaledns/decisions.html

Full Changelog: https://github.com/paimonsoror/telltaledns/commits/v0.1.0