yaks.app

A custom domain

← Documentation

A space answers at <space>.yaks.fyi and each of its apps at <space>.yaks.fyi/<app>/. Either can also answer at a domain the person already owns — example.com, or www.example.com — with the .yaks.fyi address still working.

Two forms, and the difference is what you name:

  • The space. ourbookclub.com serves the space exactly as yourname.yaks.fyi does: the front page at /, every app at /<app>/. The apps are directories of their own domain.
  • One app. example.com serves that app at the root of it, and nothing else is there.

A space can have both at once, and an app of it can still have a domain of its own — at that domain the app is the root, at the space's domain it is a directory.

A domain is aimed at the space or the app itself, never at the .yaks.fyi address it happens to have. So space_set(slug) or app_set(app, slug) moves the .yaks.fyi address and the domain keeps serving exactly what it served before, with nothing to re-attach and no DNS to change (The front page, and routing the space).

This page is for you, the assistant. The person you are helping owns a domain and probably bought it years ago from GoDaddy or Namecheap or Squarespace. They do not know what a CNAME is and they should not have to. You already know what those control panels look like. So: attach the domain with domain_attach, then walk them through their registrar in their own words, or do it for them if you can reach it.

Back to the map: The guide

Before attaching a domain

Custom domains require Plus (or an explicitly exempt space). If the space is not eligible, domain_attach returns a message carrying the space's plan-settings link, and provisions no hostname. Give the person that settings link to compare paid plans; never link directly to checkout. It is their space's https://<space>.yaks.fyi/_yaks/billing page. If they need to sign in, the normal sign-in flow returns them to that page. The sign-in email already offers a single-use link; do not create or expose a login token in the conversation.

Wait for the plan change before continuing with DNS setup. Keep using the existing .yaks.fyi address in the meantime.

The three tools

  • domain_attach(app?, hostname) — provisions the hostname and returns the DNS record that has to be added, as data: records: [{type, name, value}]. Name an app and the domain is that app's; leave it out and the domain is the space's. Only the space owner may attach one.
  • domain_status(hostname?) — what each domain points at, and where provisioning has got to, split into the three things that can be outstanding. Leave hostname out to get every domain in the space.
  • domain_detach(hostname) — hands the hostname back. What it served is untouched: same data, same files, same <space>.yaks.fyi address.

A domain points at one place, and a hostname belongs to one place across the whole platform. www.example.com and example.com are two hostnames, so attach both if they want both.

The record

Always the same shape, whatever the registrar calls the fields:

CNAME   example.com   →   origin.saas.yaks.fyi

domain_attach returns exactly that, as data. Read it out of records rather than out of the message — the value is the one thing that must be typed character for character.

Nothing serves at the domain until that record resolves. Until then the hostname serves whatever it served before.

The panels

The trap that catches people at half of these registrars: the Name/Host field is a prefix, and the panel adds the domain itself. Typing www.example.com there produces www.example.com.example.com. When in doubt, type www, and check the record's full name after saving.

GoDaddy — Domain Portfolio → the domain → DNSAdd New Record. Fields are Type, Name, Value, TTL. Name is the prefix (www), not the whole hostname. GoDaddy has no apex option at all: see below.

Namecheap — Domain List → ManageAdvanced DNS → Host Records → Add New Record. Fields are Type, Host, Value, TTL. Host is a prefix and Namecheap appends the domain, so www, never www.example.com. Namecheap refuses a CNAME at @ — it offers a URL Redirect record instead, which is the www approach below.

Squarespace (which is also where Google Domains ended up — the migration is finished, and the panel is Squarespace's) — account.squarespace.com/domains → the domain → DNSCustom RecordsAdd Record. Fields are Type, Name, Priority, TTL, Data — the target goes in Data. Name is a prefix and Squarespace appends the domain. Squarespace has no apex record at all.

Wix — Domains → the domain's Domain ActionsManage DNS records. Wix's own instruction: where another vendor tells you to put @ in Host Name, leave Host Name blank instead.

Hover — the domain's OverviewDNSAdd a record. Fields are Type, Hostname, Target Name. Hover says plainly that it cannot hold a CNAME on the root domain. It also only manages DNS while the domain still uses Hover's nameservers.

Shopify — admin → Settings → Domains → the domain → DNS settings → ManageAdd custom record. Host is www. No apex option for an outside target.

Porkbun — Domain Management → DNSAdd Record. Fields are Type, Host, Answer, TTL. Porkbun is the one consumer registrar in this list with a working apex option: pick the record type ALIAS – CNAME flattening, leave Host blank, and put origin.saas.yaks.fyi in Answer.

Cloudflare — the zone → DNSAdd record. Type CNAME, Name www or @, Target origin.saas.yaks.fyi. Set Proxy status to DNS only (the grey cloud). Proxied, Cloudflare serves its own address in the person's own zone and the request never reaches us.

The apex

DNS does not allow a CNAME at a domain's apex — the bare example.com, with nothing in front of it. This is where a non-technical person gives up, so have the answer ready before they hit it. Three ways through:

Move their DNS to Cloudflare. The best option, and the one to lead with. Cloudflare's DNS flattens a CNAME at the apex — it resolves the target and serves the addresses — so example.com simply works. It is free, it does not move the domain's registration, and it takes about ten minutes:

  1. dash.cloudflare.com → Add a domain → type example.com.
  2. Choose the Free plan. Cloudflare scans the existing records; check that their mail records came across before continuing, because a missing MX is how this goes wrong.
  3. Cloudflare gives two nameservers. Copy both.
  4. At the registrar, replace the current nameservers with those two. Every panel above has this under a "Nameservers" or "DNS" heading.
  5. Wait. Usually an hour or two, sometimes a day.
  6. Then add the CNAME in Cloudflare's own DNS tab, as above.

If the domain has DNSSEC turned on at the old registrar, turn it off there first and give it a day before changing nameservers; otherwise resolvers see a broken chain and the domain goes dark. Cloudflare can turn it back on afterwards.

Use the registrar's ALIAS or ANAME. Same value, a record type that is allowed at the apex. Porkbun has one. GoDaddy, Namecheap, Squarespace, Hover and Shopify do not.

Attach www instead. domain_attach(app, hostname: 'www.example.com'), a CNAME at www, and a forwarding rule at the apex sending example.com to www.example.com. Every registrar above has domain forwarding under some name. It works and people accept it, but the address they give out is still the bare one, so offer Cloudflare first.

While they are moving DNS, it is worth saying that Cloudflare's registrar charges what the registry charges, with no markup added — so transferring the domain there at its next renewal usually costs less than they pay now. A transfer needs the domain to be at least 60 days old, unlocked at the current registrar, and an authorization code from them, and it takes about five days. None of that is required to point the domain at yaks.app; it is just the thing they will ask about once their DNS is on Cloudflare.

Reading the status

domain_status reports three steps, each with done, waiting or error, because each one waits on somebody different:

  • dns — whether the record resolves to yaks.app. Waiting means the person has not added it yet, or it has not propagated. This is the only step they can do anything about.
  • validation — whether Cloudflare has accepted the hostname as one we may serve. It follows dns.
  • certificate — whether the HTTPS certificate is issued. This is the step that is still running after the record has arrived, and it needs nothing from anybody.

So "your CNAME hasn't propagated yet" and "the certificate is still issuing" are different answers, and the tool tells you which one is true. Tell them the one it reports. A step in error carries the reason as it was given, and those words are Cloudflare's, not a paraphrase.

Timing: DNS is usually minutes and can be a day, depending on what the old record's TTL was. The certificate is usually minutes after that. Nothing needs doing in between — check again in five minutes rather than changing anything.

When it stays stuck

  • A CAA record on their domain. If they have one, it lists which certificate authorities may issue for the domain, and ours has to be on the list. The certificate step reports that.
  • Another record at the same name. DNS forbids a CNAME sitting beside an A, AAAA or another CNAME at one name. An old A record at www pointing at a site they replaced years ago has to be deleted, not left alongside.
  • The domain is already on Cloudflare in somebody else's account, on hold. The error names it. Only that account's owner can lift it.
  • An old custom hostname from a previous platform. If they moved from another site builder, the hostname may still be registered there and will keep winning. They have to remove it at the old platform.
  • The record went in with the domain doubledwww.example.com.example.com. See the panels above. This is the most common one by a distance.

Detaching

domain_detach(hostname) gives the hostname back and leaves the app exactly as it was, still serving at <space>.yaks.fyi. It does not touch the person's DNS — that record is theirs, in their registrar, and after detaching it points at nothing, so tell them to delete it.

Detach before re-attaching a domain somewhere else. A hostname belongs to one app on the whole platform, and domain_attach refuses a name that is taken rather than moving it out from under whoever has it.