Skip to content

Signing in and hosted names

Gardener needs names. The garden’s API server, the dashboard, the seed’s ingress wildcard and every shoot’s API server all need a DNS name, and Let’s Encrypt needs a TXT record before it will issue a certificate for them. So DNS is the one precondition of a landscape besides the Hetzner token, and the one decision to make before you run anything.

dns.modewhat it costs youwhat you get
zonea domain (roughly €10 a year) and a Hetzner DNS zone; its token in $DNS_TOKENyour own names, shared with nobody. The right choice for anything that outlives a trial
my-paasboxa paasbox account and one sign-in in your browser<team>.paasbox.app, free, with the TXT records Let’s Encrypt needs
sslipnothingnames derived from the node’s IP through sslip.io, no zone and no records. The garden, the dashboard and the seed ingress work, but shoots get no external DNS because there is nowhere to write it

sslip is designed but not implemented yet (as of 2026-09-07): the bring-up refuses the mode and tells you to pick one of the other two. The file paasbox garden init writes starts with dns.mode: sslip, so change it before your first paasbox garden up.

Switching later is two lines in values.yaml and another paasbox garden up. zone and sslip never talk to us at all: no account, no sign-in, nothing to agree to. The sign-in below appears only when you ask for a free hosted name. There, the account is what limits abuse.

dns.mode: my-paasbox needs a token for your team’s names. Copying one out of a web page is the kind of step the tool exists to remove, so the CLI fetches it itself. Run paasbox auth login, or start a bring-up and let it notice:

$ paasbox garden up my-garden
dns.mode my-paasbox and no $DNS_TOKEN — signing in to console.paasbox.com
open https://console.paasbox.com/device (opening your browser…)
code FTQK-2X7M (expires in 10 minutes)
waiting… ✓ signed in — team Acme GmbH → acme.paasbox.app
### check — preflight …

It is the OAuth 2.0 Device Authorization Grant (RFC 8628), the flow gh auth login and docker login use. There is no local callback server and no port to keep free, so it works over SSH and on a machine with no browser at all: --no-browser prints the URL and you approve it on your phone.

On the page, at console.paasbox.com/device, you sign in to the console as usual, then see who is asking (the CLI names its own version), what it will be able to do, in words, and which team the token will belong to. Then:

  • Several teams: you pick one. A team you cannot use is shown with the reason instead of being left out: its slug is not a DNS label, it is a reserved name, or you are not an admin of that team. Approving here is as privileged as issuing a token on the team’s own page, so it asks for the same role.
  • No team yet: create one on the spot. The slug is checked as you type by the same code that decides the button.
  • No account yet: sign up as normal and you land back on the approval page.

The code is eight characters from an alphabet with no 0/O and no 1/I/l, shown as XXXX-XXXX and accepted in any case, with or without the dash. It is valid for ten minutes and can be approved exactly once. The token itself is created when the CLI collects it, not when you press approve, so it is stored nowhere in between.

The CLI knows the portal’s address as a compiled-in constant and never takes it from a reply: an answer pointing anywhere else aborts the sign-in and stores nothing. $PAASBOX_PORTAL or --portal overrides it if you run your own instance.

What you get is a team credential, not a personal one. A running landscape must not lose its DNS because the person who created the token left, so the token belongs to the team, carries the scope dns and nothing else, and is shown exactly once. A team can hold up to five active tokens; revoking one is done in the console, on the team’s DNS page.

On your machine it is stored in ~/.config/paasbox/credentials.json, mode 0600 in a 0700 directory, keyed by the portal’s host, so a self-hosted instance and console.paasbox.com can both be signed in without overwriting each other. That file is the only copy. It is never written into a landscape directory; when a stage runs, the token is handed to that one child process as $DNS_TOKEN.

What you already have always wins: an exported $DNS_TOKEN, or one in the landscape’s .env, is used as it is and the stored credential is left alone. paasbox garden up --no-login refuses to sign in at all and tells you what is missing.

Terminal window
paasbox auth login # --team acme pins it, --no-browser prints the URL
paasbox auth status # who you are, checked against the portal
paasbox auth token # just the token, for a script
paasbox auth logout # forget it here; the token stays valid until you revoke it

Team acme owns acme.paasbox.app and everything under it. A landscape brought up in that mode gets its names there without you owning a domain:

the garden APIapi.acme.paasbox.app
the Gardener Dashboarddashboard.ingress.acme.paasbox.app
the garden’s ingress*.ingress.acme.paasbox.app
the seed’s ingress*.ingress.seed.acme.paasbox.app
your shoots’ API serversunder shoots.acme.paasbox.app

They are real names in a real zone, so Let’s Encrypt issues real certificates for them over the DNS-01 challenge, and Gardener’s Hetzner DNS extension writes them itself as shoots come and go. The records live in Hetzner’s DNS and Hetzner serves them; paasbox holds the tokens, the quotas and an audit line per write.

The token is scoped to one label, and the API enforces that on every call:

  • One team, one name. Writes are allowed to <team>.paasbox.app and anything below it. The zone apex is refused, and so is every other team’s name and every name of ours.
  • Reads are filtered. You see your own records and nothing else. A read of any other name is a 404, not a 403, so the zone cannot be enumerated this way.
  • Four record types: A, AAAA, CNAME, TXT. NS is refused everywhere, because delegating a name away would take it out of reach.
  • Reserved labels cannot be claimed: www, api, dns, admin, mail, ns, _acme-challenge and the API host’s own label.
  • Your label is your team slug, lower-cased with underscores turned into hyphens, at most 32 characters. A slug that does not survive that is refused rather than changed into a neighbour’s name; the page tells you to rename the slug. The label is frozen onto the token, so renaming the team later does not move a live landscape’s records. That takes a new token.
  • Quotas (as of 2026-09-07): 200 records and five active tokens per team.

Every write is recorded, including the refused ones: team, token, name, type, action, time and what the upstream answered.

It is a free courtesy for landscapes you run yourself, not a general-purpose DNS service. If the landscape matters, a domain of your own at roughly €10 a year and dns.mode: zone is the better choice.

The same names are reachable through a small Hetzner-Cloud-compatible API, so hcloud zone rrset and any Hetzner DNS client work against it unchanged. See the API page for the endpoint, the scoping rules and what is refused.

Your own Gardener has the values file, the sizes, the bring-up and the day-two commands.