feedback.acme.com, that points at your app. Pylon Cloud issues the
certificate, and your app decides what to render from the host of the request.
This page covers three parts:
- Attach the domain with
ctx.domains. - Pick the customer from
props.hostin a page. - Sign users in on the customer’s domain.
ctx.domains works on Pylon Cloud only. On a self-hosted server every method
throws DOMAINS_NOT_CONFIGURED.
Attach a domain
Callctx.domains.add from an action when your customer enters their domain.
The result has the DNS records the customer must add.
ctx.domains.status(hostname). When status is "ready", the
certificate is active and your app accepts requests for the host. Pylon
refreshes the list of ready hosts every 30 seconds, so there is no deploy per
domain. ctx.domains.remove(hostname) detaches a domain.
Keep your own record of which customer owns which hostname. The control plane
only knows that the hostname belongs to your app.
Pick the customer from the host
Every page getsprops.host. It is the request’s host, lowercased, when the
server trusts it, and "" otherwise. A ready customer domain is trusted.
props.host:
- A forged
Hostheader gives"". A page cannot be made to render another customer’s content by a header the server does not trust. hostis part of the SSR cache key. Each host gets its own cache entry, so a page withexport const revalidatestays cacheable.- The browser receives the same value in the hydration payload.
/roadmap), not paths with your app’s own prefix.
Sign users in on the customer’s domain
A session cookie belongs to one host. Pylon sets it on the host that asked for it:- Email and password, magic codes, passkeys. These run on the customer’s
domain directly. The session cookie is host-only on that domain, also when
PYLON_COOKIE_DOMAINis set for your own domain, because a browser drops a cookie whoseDomain=does not cover the host. - OAuth (Google, GitHub, and others). The provider only knows your app’s one registered callback URL, on your own host. Pylon moves the sign-in to the customer’s domain with a one-time code.
- The start request sets a short-lived, host-only cookie on the customer’s domain. It binds the sign-in to this browser.
- The provider returns the browser to your app’s callback. The callback does
not set a session cookie on your host. It redirects to
https://<customer domain>/api/auth/handoff?code=…. - The handoff request, on the customer’s domain, checks the code and the binding cookie, creates the session, sets the cookie, and redirects to the callback URL.
- is 32 random bytes, and only its SHA-256 is stored,
- works once, for 120 seconds,
- works only on the domain it was made for,
- works only in the browser that started the sign-in, so a code sent to another person cannot sign them in to the sender’s account.
400 HANDOFF_WRONG_START_HOST.
The handoff errors are:
The handoff codes are stored with the other auth state (SQLite or Postgres), so
a sign-in that starts on one machine can finish on another.