How to bake an identity
A daemon that gets recycled needs to say which build it is running, so a supervisor can tell “the daemon I shipped” from “an older one still holding the gate.” That identity is baked in at build time by Nix and read back at runtime by one shared recipe.
1. Bake the pair with Nix
Use mkDaemonIdentity (in nix/daemon-identity.nix) to --set two environment
variables on the daemon binary, under a prefix of your choosing:
<PREFIX>_BUILD_ID— a hash over the daemon’s behavioural source closure. It flips exactly when a restart would load different wire or behaviour code.<PREFIX>_COMMIT_HASH— the git ref the daemon was built from.
Select the behavioural fileset deliberately — it is the slice whose change means “a restart behaves differently.” Everything outside it (docs, unrelated code) can change without flipping the build id.
2. Read it back at runtime
readBakedIdentity(prefix) (from @kolu/surface-daemon) is the one recipe both
the reads share. It returns { staleKey, navigableCommit }:
export function currentIdentity() {
return readBakedIdentity("KAVAL"); // reads KAVAL_BUILD_ID / KAVAL_COMMIT_HASH
}
export const currentBuildId = () => currentIdentity().staleKey;
export const currentCommit = () => currentIdentity().navigableCommit;
staleKey is the build hash a supervisor compares; navigableCommit is the git
ref you surface as a clickable identity in the UI.
3. Decide what the build id hashes
There is one policy choice: whether the daemon-spine code is inside the hashed closure.
- If a contract-compatible spine change is behaviourally interchangeable — because the wire contract version is itself hashed — you can exclude the spine from the build id (kaval does this; the contract version carries the compatibility signal).
- If you prefer a cheap auto-drain where over-firing is harmless, include the whole closure (padi does this).
Either way, the identity a client reads is now stable and meaningful across restarts, which is what recycling and upgrading depends on.