feat(machine): record where a host can be reached, as the host reports it

The machine table said who owns a host, which team it belongs to and when it
was last seen, and nothing about how to reach it. Anything standing in front of
a host and authenticating browsers on its behalf could therefore authorise a
request perfectly and then have nowhere to send it.

Reported, never assigned. A host holds the secret half of this identity and is
the only thing that can know the public half first, so it rides on the beat it
already sends as itself. Omitting the field leaves the stored value alone --
an agent that does not mention where it is has not moved, and an absent field
must never read as "nowhere", which would take every host shipped before this
field off the map on its next beat.

Nullable, because "has never reported one" is a real state that every host
registered before today is in. Unique, because an endpoint id belongs to one
host: two rows claiming the same one would send a request addressed to one
machine to another machine's agent, which is the one mistake here that the
authorisation in front of it cannot catch.
This commit is contained in:
Wanjohi
2026-09-06 23:44:37 +03:00
parent 0e94620808
commit 6603383ad1
10 changed files with 3182 additions and 18 deletions
+47 -11
View File
@@ -22,6 +22,19 @@ export namespace Machine {
/** Length in bytes before base64url encoding. */
const SECRET_BYTES = 32;
/**
* A host's endpoint id: 32 bytes of public key, lowercase hex.
*
* Checked for shape and nothing else. What it addresses is meaningless
* here — this is a string the control plane stores and hands back — so the
* only thing worth refusing is a value that cannot possibly be one, which
* is what keeps a truncated or double-encoded id from being written and
* then failing far away, at whoever tries to dial it.
*/
export const EndpointId = z.string().regex(/^[0-9a-f]{64}$/, {
message: 'An endpoint id is 64 lowercase hex characters'
});
export const Info = z
.object({
id: z.string().meta({
@@ -44,6 +57,11 @@ export namespace Machine {
lastSeen: z.iso.datetime().optional().nullable().meta({
description: 'When this machine last authenticated',
example: Examples.Machine.lastSeen
}),
endpointId: EndpointId.optional().nullable().meta({
description:
'Where this host can be reached, as its own endpoint id. Null until the host has reported one — it holds the secret half of this identity, so it is the only thing that can say what the public half is',
example: Examples.Machine.endpointId
})
})
.meta({
@@ -195,17 +213,34 @@ export namespace Machine {
* Returns the stored timestamp rather than void so a caller can hand it
* straight back to the host — which is what lets a heartbeat be one round
* trip instead of a write followed by a read.
*
* `endpointId` rides along for the same reason. Where a host is reachable
* is a fact about the host, it changes when the agent's identity does, and
* a caller that has just proved it is that host is the only one who can
* report it — so it is written by the call that already says "still here",
* in the same statement, rather than by a second one that could succeed
* alone and leave the two facts disagreeing.
*/
export const touchLastSeen = fn(Info.shape.id, async (id) => {
return Database.use(async (tx) => {
return tx
.update(MachineTable)
.set({ lastSeen: sql`now()` })
.where(eq(MachineTable.id, id))
.returning({ lastSeen: MachineTable.lastSeen })
.then((rows) => rows.at(0)?.lastSeen ?? null);
});
});
export const touchLastSeen = fn(
Info.pick({ id: true }).extend({ endpointId: EndpointId.optional() }),
async (input) => {
return Database.use(async (tx) => {
return tx
.update(MachineTable)
.set({
lastSeen: sql`now()`,
// Omitted rather than nulled when it is absent: a caller
// that does not mention where it is has not moved, and
// clearing the column would deregister a working host
// from every route that reads it.
...(input.endpointId ? { endpointId: input.endpointId } : {})
})
.where(eq(MachineTable.id, input.id))
.returning({ lastSeen: MachineTable.lastSeen })
.then((rows) => rows.at(0)?.lastSeen ?? null);
});
}
);
/**
* Whether a host has beaten recently enough to place work on.
@@ -306,7 +341,8 @@ export namespace Machine {
ownerUserId: input.ownerUserId,
teamId: input.teamId,
label: input.label,
lastSeen: input.lastSeen?.toISOString() ?? null
lastSeen: input.lastSeen?.toISOString() ?? null,
endpointId: input.endpointId
};
}
}