flybrain/docs/design/session-framework/seed-derivation-v1.md
acamilo f043cd79c0 docs(session-framework): seed derivation v1 and the FLYSESS1 checkpoint envelope
Two specifications CONTRACT-01 owes the slices that cannot be built without them.

seed-derivation-v1: the versioned derivation of independent per-agent seeds from a recorded
master seed and stable agent ids, the exact hashed material, the lane rule that keeps a seed
away from zero (the pinned kernel RNG is an xorshift, whose state must not be zero), the
properties a composition may rely on, and the test vectors both languages reproduce.

checkpoint-envelope-v1: the exact byte layout of the new envelope per state-media-v1 section
4, with the header, canonical-JSON manifest, 112-byte payload table entries, eight-byte
aligned payloads and the digest footer; the manifest field set; what a reader enforces and in
what order; and the durable commit sequence of section 6, whose commit point is the store
manifest rename. FLYSIM01 is unchanged, refused at the magic, and stays separately readable.

Both are specifications with fixtures, not the store: generations, rotation, the capture
queue and the restore flow remain STATE-01.
2026-09-22 12:47:48 +00:00

5.2 KiB

Seed derivation v1

Status: draft 1, 2026-09-22. Specified by CONTRACT-01 of the implementation guide, required by worker interfaces section 2 before the real-agent slice. Reference implementations: services/flysim/crates/fly-session-types/src/seed.rs and packages/session-types/src/seed.ts; test vectors: services/flysim/crates/fly-session-types/fixtures/seed-vectors.json.

1. What this is for

Agent.Initialize takes seed, a signed 32-bit integer, matching the current RNG input. Workers-v1 section 2 requires that the coordinator derive independent per-agent seeds from its recorded master seed and stable agent IDs under a versioned algorithm, and that the algorithm be specified and tested before the real agent slice. This is that algorithm.

It is a reproducibility rule, not a secret: a run manifest records the master seed in the clear, and anyone with the manifest can recompute every agent's seed. It is not a key derivation function and must not be used as one.

seed-derivation-v1 is part of composition identity. Changing any byte of it requires a new identifier (seed-derivation-v2), because two runs that agree on every other identity but disagree here are not the same experiment.

2. Inputs

Input Type Source
masterSeed U64 decimal string Recorded once per run by the application/supervisor
agentId Id The configured agent identity, stable across restarts and epochs

Both are the ipc-v1 section 2 scalars. An agentId that is not an Id is an error, not something to normalize. The master seed is the whole 64-bit range: a 32-bit master seed would be no wider than the seed it derives.

3. Derivation

material = "flybrain/seed-derivation-v1" LF masterSeed LF agentId LF
digest   = SHA-256(material)
lanes    = digest read as eight big-endian uint32 values, in order
seed     = the first nonzero lane, reinterpreted as a two's-complement int32

LF is one 0x0a byte. masterSeed is its canonical decimal form: "0", or no leading zero. The prefix is a domain separator, so a digest from this algorithm can never collide with one taken over some other pair of strings.

Zero lanes are skipped because the pinned kernel's RNG is an xorshift generator, whose state must not be zero: a derivation that could hand out 0 would silently produce a stalled generator. If every one of the eight lanes were zero, the material is rehashed with a counter suffix (material || "1" LF, then "2" LF, then "3" LF) and the search repeats; no input has ever needed it, and four rounds exhausted is an error rather than a fallback seed.

The seed is the negative number when the lane's high bit is set. That is deliberate: the existing RNG input is a signed 32-bit integer, and half the range is negative.

4. Properties

  • Deterministic. The seed is a function of the two recorded inputs and nothing else: not of wall time, agent order, port assignment, worker process or thread count.
  • Independent per agent. Distinct agent IDs give unrelated seeds; there is no arithmetic relationship between fly-a and fly-b for a caller to exploit or accidentally rely on.
  • Stable across recovery. Restore, episode reset and a new epoch do not re-derive a different seed for the same agent ID under the same master seed. The seed is persisted as run configuration and state, and the capture compatibility digest covers the resolved seed (workers-v1 section 2), so a checkpoint cannot be installed into a differently seeded instance.
  • Equal IDs give equal seeds. That is the only way to get identical seeds, and workers-v1 allows identical seeds only when an experiment declares them. A composition therefore refuses a repeated agent ID rather than quietly sharing a seed between two agents.

Non-properties, stated so nobody assumes them: this is not uniform over the int32 range beyond what SHA-256 gives, it is not a stream (one seed per agent per run, not per step), and it says nothing about how a model consumes its seed.

5. Test vectors

fixtures/seed-vectors.json carries the full table: five master seeds (0, 1, 42, 2^63 and the U64 maximum) across four agent IDs, each with the exact material string, its SHA-256 and the derived seed, plus one four-agent composition and the inputs that must be refused. Both implementations reproduce every row, and each records the material as well as the seed so a third implementation can find where it diverges.

The first two rows:

masterSeed agentId material seed
0 fly-a flybrain/seed-derivation-v1\n0\nfly-a\n 1828176714
0 fly-b flybrain/seed-derivation-v1\n0\nfly-b\n 1218785088

Refused: an agent ID that is not an Id (uppercase, empty, over 64 characters), a master seed that is not a canonical U64, and a composition with a repeated agent ID.

6. Out of scope

Choosing the master seed, recording it in the run manifest, and the hand-selected explicit seeds that workers-v1 allows for the first synthetic composition. This document defines only the derivation. A profile that needs several independent streams inside one agent derives them from the agent's own seed under its own documented rule; that is a profile concern, not a session one.