Guide

Users and sign-in

There is no default password. You create the first admin with a one-time setup token, then add people with a role each, and give scripts and AI agents tokens of their own.

DNS never waits for sign-in. Users, sessions, and tokens live in <data_dir>/state.db. If it can't be opened, the API stays off and DNS keeps answering.

Create the first admin

On first start the server creates a one-time setup token and saves it in <data_dir>/setup-token, readable by its owner only. The log says where the file is, never the token itself. Print it with:

telltale auth setup-token

On a native install, run it as the service user: sudo -u telltale telltale auth setup-token -c /etc/telltale/telltale.toml.

Web UI

  1. Open http://<server>:8053/. On first start it shows Welcome to TelltaleDNS.
  2. Paste the Setup token, choose an Admin username, and enter a password twice (at least 10 characters).
  3. Choose Create admin and sign in. The token file is then deleted.

API / CLI

curl -s -c jar -H 'content-type: application/json' \
  -d '{"setupToken":"<token>","username":"admin","password":"a long passphrase"}' \
  http://dns.example.com:8053/api/v1/auth/setup

That creates the first admin and signs them in; the token file is then deleted. Passwords need at least 10 characters.

From a secret (Kubernetes, Ansible)

For a deployment that must come up configured, set these environment variables instead. They're read only while no user exists:

TELLTALE_BOOTSTRAP_ADMIN_USER=admin
TELLTALE_BOOTSTRAP_ADMIN_PASSWORD_HASH='$argon2id$v=19$...'   # from: echo 'a long passphrase' | telltale auth hash-password
# or TELLTALE_BOOTSTRAP_ADMIN_PASSWORD=...

With the Helm chart, create a Secret with the keys username and password or password-hash, and name it in the values:

auth:
  bootstrapAdmin:
    existingSecret: telltale-admin

The Kubernetes guide and Helm values cover the rest of the chart.

The Linux installer's guided setup asks about this too: a setup token for the web UI (the default), or a username and password now. In a cluster, only the primary has a setup token; the other nodes take their users from it.

Add people and pick roles

RoleWhat it can do
viewerSees dashboards and the query log.
operatorEverything a viewer can, plus pause blocking, flush the cache, and manage lists, clients, and groups.
adminEverything, including users, the audit log, and security settings.

Web UI

  1. As an admin, open Settings → Users.
  2. Under Add a user, enter a username, a password (10 or more characters), and a role, then choose Add. Leave HTTP Basic unticked unless a tool needs it (below).
  3. In the table, change someone's role from the drop-down, turn an account off with Active · disable, reset their two-factor sign-in with Reset, or Delete it.

API / CLI

Admins manage users at /api/v1/users (here with an admin-scope token). role is viewer, operator, or admin:

curl -s -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"username":"ana","password":"a long passphrase","role":"operator"}' \
  http://dns.example.com:8053/api/v1/users

Deleting a user also deletes their sessions and tokens.

The last admin can't be removed or demoted. Keep at least two admins, or set up break-glass access, so you can't lock yourself out.

Each person changes their own password under Settings → Account. Changing it signs out their other sessions.

Turn on two-factor sign-in

After the password, TelltaleDNS asks for a 6-digit code from an authenticator app. Anyone who can change settings should turn it on.

Web UI

  1. Open Settings → Account and choose Set up two-factor sign-in.
  2. Add the account to your authenticator app: on a phone, open the link in the app, or type in the key shown.
  3. Enter the 6-digit code the app shows and choose Turn on.
  4. Save the ten recovery codes now. Each works once if you lose your phone, and they aren't shown again.

To turn it off, enter your password and choose Turn off two-factor. When signing in without your phone, choose Use a recovery code instead.

API / CLI

  1. POST /api/v1/auth/totp/setup returns a secret and an otpauth:// URL for an authenticator app.
  2. POST /api/v1/auth/totp/enable with a current code turns it on and returns ten single-use recovery codes.

Config file

Require two-factor sign-in for a role:

[auth]
totp_required_roles = ["admin"]

Lost both the phone and the recovery codes? An admin resets it for you: Settings → Users, then Reset in the Two-factor column.

Single sign-on with OpenID Connect

Sign in with Keycloak, Authentik, or another OpenID Connect provider. Each provider gets a "Sign in with ..." button. People are created in TelltaleDNS the first time they sign in, and their role comes from their groups at every sign-in.

  1. Add the provider to the config file.
    [auth.oidc]
    public_url = "https://dns.example.com"     # where people open the UI
    
    [[auth.oidc.provider]]
    id = "authentik"                           # used in URLs
    name = "Authentik"                         # the button label
    issuer = "https://auth.example.com/application/o/telltale/"
    client_id = "telltale"
    client_secret_file = "/run/secrets/oidc"   # or client_secret = "..."
    groups_claim = "groups"                    # Keycloak realm roles: "realm_access.roles"
    
    [[auth.oidc.provider.role]]
    group = "dns-admins"
    role = "admin"
    
    [[auth.oidc.provider.role]]
    group = "family"
    role = "viewer"
  2. Create a confidential client at the provider with the redirect URI <public_url>/api/v1/auth/oidc/<id>/callback (here https://dns.example.com/api/v1/auth/oidc/authentik/callback), and allow <public_url>/ after sign-out.
  3. Make sure the ID token carries the groups (see the tabs below).
  4. Restart TelltaleDNS. Changes to [auth.oidc] need a restart.

Authentik

Authentik's profile scope includes groups, so groups_claim = "groups" works as shown. After sign-out, Authentik shows its own "logged out" page.

Keycloak

Add a "Group Membership" mapper (full group path off) to the client. To map realm roles instead, set groups_claim = "realm_access.roles". On sign-out, Keycloak asks "Do you want to log out?".

  • Roles: someone in several mapped groups gets the highest role. Someone in none can't sign in unless you set default_role. require_verified_email = true refuses addresses the provider hasn't verified.
  • Usernames come from preferred_username (or username_claim). A provider account is never attached to an existing local user: if the name is taken, the new account is called name@provider.
  • Signing out also signs out at the provider. Provider accounts have no TelltaleDNS password, but they can still create API tokens.
  • Sessions are local: the provider is only contacted while signing in. If a provider can't be set up (for example, its secret file is missing), it's logged and password sign-in keeps working.
  • On Kubernetes, mount the client secret with secretMounts (at /etc/telltale-secrets/<name>) and point client_secret_file at it. Put [auth.oidc] under config: in the values.

Break-glass admin. disable_local_login = true in [auth.oidc] turns off password sign-in and HTTP Basic, except for admins signing in from allowed_admin_networks (private networks by default). Keep a local admin account: it's your way in when the provider is down.

Every [auth.oidc] key is in the configuration reference.

Tokens for scripts, tools, and agents

Give each tool its own token with the smallest scope instead of your password. A token is shown once, only its hash is stored, and it never has more rights than its owner's role.

ScopeActs as
readviewer: dashboards, Grafana, a Homepage widget
writeoperator: a script that names devices
adminadmin. It can create users, so keep these rare and short-lived.

Web UI

  1. Open Settings → API tokens.
  2. Give the token a name (for example grafana), pick for my scripts, a scope, and when it expires.
  3. Choose Create token and copy it now; it won't be shown again. Revoke it from the same table at any time.

API / CLI

From a signed-in session (cookie jar jar), sending the CSRF token that sign-in returned:

curl -s -b jar -H "X-CSRF-Token: $CSRF" -H 'content-type: application/json' \
  -d '{"name": "grafana", "scope": "read"}' \
  http://dns.example.com:8053/api/v1/tokens

It returns tt_<id>_<secret> once. Send it as Authorization: Bearer ....

Creating and revoking tokens, and changing a password or two-factor sign-in, need a signed-in session: a token can use the account, but not change it.

Use it with telltale ctl

The same API from a terminal:

export TELLTALE_TOKEN=tt_...                 # or --token-file /path/to/token
telltale ctl status                          # version, uptime, the last day's numbers
telltale ctl top blocked --limit 20
telltale ctl queries --status blocked --from -1h

It talks to the node in [api] listen from your config, or the one you give with --url http://dns.example.com:8053. Every ctl command in the docs.

Agent tokens for AI assistants

Give an AI assistant an agent token instead of your own account: Settings → API tokens, pick for an AI agent, and tick what it may do. The default is read-only, without the query log. Agents can't touch users, tokens, passwords, backups, or the audit log, must give a reason for every change, and all stop at once with [agents] enabled = false. See AI agents.

HTTP Basic for scrapers

For scrapers and widgets that can't send a token. It's off for each user until an admin ticks HTTP Basic for them in Settings → Users (allowBasicApi in the API). It's refused over plain HTTP unless you set:

[auth]
allow_insecure_basic = true   # default false; on plain HTTP anyone on the path can read the password

Verified credentials are cached for a minute. The Prometheus listener on :9153 needs no sign-in (it's limited to allowed_networks); /metrics on the API port needs a viewer token or Basic.

A leaked token? Revoke it without disabling its owner: an admin opens Settings → Users → Tokens for that user and chooses Revoke (DELETE /api/v1/users/{id}/tokens/{tokenId}, audited as token.revoke).

Encrypt sign-ins with HTTPS

Out of the box the web UI on port 8053 is plain HTTP, so a password typed into the sign-in form crosses your network unencrypted. One line turns on HTTPS on the same port:

[api]
tls = { self_signed = true }      # or: tls = { cert = "/etc/telltale/api.crt", key = "/etc/telltale/api.key" }
  • Self-signed: TelltaleDNS makes a certificate on first start, for this host's name and addresses, and keeps it in <data_dir>/api-tls/. Your browser warns the first time: accept it, or import cert.pem into your trusted certificates to make the warning go away for good.
  • Your own certificate (your CA, or Let's Encrypt with a DNS challenge): no warning, renewals are picked up within 10 s, and browsers are told to always use HTTPS for the name (HSTS).
  • Check before you trust it: Settings → System → Listener checks shows the certificate's SHA-256 fingerprint and names. Compare the fingerprint with the one your browser's certificate viewer shows. Your own certificate's expiry is watched there too, with the same warnings as the DNS listeners'.
  • Old http:// links and bookmarks are redirected to https://. Health checks on /livez, /readyz, and /healthz keep working over plain HTTP.
  • telltale ctl and telltale mcp --stdio on the node switch to HTTPS by themselves; from another machine, pass the node's cert.pem with --ca-file.

Restart after changing [api] tls. In Kubernetes, terminate TLS at the ingress instead (below).

Sessions, lockouts, and proxies

  • Sessions end after 7 days, or after a day unused. Signing in to the web UI sets an HttpOnly; SameSite=Strict cookie (Secure over HTTPS).
  • Lockout: after 5 failed sign-ins for a username or from an address, through the sign-in form or HTTP Basic alike, each further attempt waits 30 s, doubling up to 15 minutes (HTTP 429). A successful sign-in clears the count.
  • Busy: at most four password checks run at once. Beyond that a sign-in is answered 503 with Retry-After: 1, which doesn't count as a failed sign-in, and DNS keeps answering.
  • Audit log: every sign-in, lockout, and change to users, passwords, two-factor sign-in, and tokens is recorded. Admins see it under Settings → Audit log.
[auth]
totp_required_roles = ["admin"]
session_ttl_hours = 168
session_idle_hours = 24
allow_insecure_basic = false

Behind a reverse proxy or ingress, list the proxies, or everyone looks like the proxy and one person's failed sign-ins lock out everyone. The same list lets HTTP Basic work and marks the cookie Secure behind a TLS-terminating proxy. A proxy on the same host (127.0.0.1) always counts.

[api]
trusted_proxies = ["10.0.0.0/8"]   # e.g. the Kubernetes pod network your ingress runs in

Users in a cluster

Users and tokens are the primary's, everywhere. Users (passwords, roles, two-factor settings, recovery codes) and API and agent tokens are made and changed on the primary, and every node takes them within seconds. You sign in to any node with the same account, and a token works against any node. Only hashes travel, over the cluster's encrypted channel.

  • On a replica, changing a user, a password, two-factor settings, or a token is refused with a pointer to the primary.
  • Sessions stay per node: signing in to a second node is a second sign-in (with SSO, a silent redirect).
  • Users that exist only on a replica (an admin made before it joined) keep working there. If the primary has a user of the same name, the primary's account and password take over on every node.
  • A promoted replica becomes the place to manage users, with everything it had synced.
  • [auth] and [api] are per node: set them in each node's own file. Shared or per node.

More in Build a cluster and the cluster docs.

Troubleshooting

What you seeWhat to do
You can't find the setup tokenRun telltale auth setup-token as the service user. The log only says which file holds it. It's gone once the first admin exists, and replicas never have one.
Sign-in answers 429Too many failed sign-ins for that username or address. Wait (up to 15 minutes); a successful sign-in clears the count. Behind a proxy, set [api] trusted_proxies so one person can't lock out everyone.
Sign-in answers 503Four password checks are already running. Try again in a second; it isn't counted as a failure.
Lost your authenticator appChoose Use a recovery code instead. Without recovery codes, an admin resets two-factor sign-in in Settings → Users.
Changing a user or token is refused on a nodeIt's a replica: make the change on the primary.
Someone can't sign in with SSOTheir groups match no rule: add a [[auth.oidc.provider.role]] or set default_role. Check that the ID token carries the groups and that the provider has the redirect URI <public_url>/api/v1/auth/oidc/<id>/callback. Restart after any [auth.oidc] change.
The "Sign in with ..." button is missingThe provider couldn't be set up (for example, its secret file is missing). The log says why, and password sign-in keeps working.
HTTP Basic is refusedTick HTTP Basic for that user, and use HTTPS (or a trusted proxy that sets X-Forwarded-Proto: https).
Every change answers 503The audit log can't be written (a full disk, a broken database). Fix that; DNS keeps answering meanwhile.

Full detail: Users and sign-in in the docs.