feat(machine): mint a public name, and stop routing on the id

A host's hostname was its primary key. That worked and disclosed three
things it should not have: ids here are monotonic, so an id in a hostname
tells anyone who reads a URL roughly when that machine was registered and
where it falls among its owner's others; an id is the primary key, so a
name that had to change could only change by re-registering the machine,
which is changing its identity to fix its name; and the hostname is also
the OAuth audience and the cookie scope, so the id travelled into redirect
URLs and browser history.

Machines now carry a minted name -- two words and four digits, unique
across the fleet, DNS-safe by construction, which an id was not. The words
are a curated list rather than a dictionary, because every pair is shown to
strangers. Names the fleet's own infrastructure answers on are refused at
mint time: one minted onto the edge's own label would take the published
key path away from every host at once.

Minting retries on the unique index rather than checking first, because two
registrations in the same instant both read "free" and both write. Only a
name collision retries; a duplicate id or secret means something a new name
cannot fix.

The migration adds the column in three steps. Generated as a NOT NULL
column it fails outright against a populated table, and a default would be
worse: every row would share one value on a routing key.
This commit is contained in:
Wanjohi
2026-09-18 00:36:13 +03:00
parent aecae0c69e
commit cfb8ec26a0
8 changed files with 3371 additions and 12 deletions

View File

@@ -37,6 +37,11 @@ export namespace MachineApi {
schema: Result(
z.object({
machineId: z.string().meta({ example: Examples.Machine.id }),
slug: z.string().meta({
description:
'The name this host is reached at. Its hostname is this label under the box zone, and the id never appears in one.',
example: Examples.Machine.slug
}),
secret: z.string().meta({
description: 'Shown once. Store it on the box; it cannot be retrieved.'
})
@@ -111,7 +116,13 @@ export namespace MachineApi {
label
});
return c.json({ data: { machineId: registered.id, secret: registered.secret } });
return c.json({
data: {
machineId: registered.id,
slug: registered.slug,
secret: registered.secret
}
});
}
)
.patch(