Ticket 0002 — reads 8 bytes from /dev/urandom (little-endian u64) and seeds xoshiro256**. Opt-in kernel entropy for stochastic runs; the default stays deterministic (k=0 at startup), so ticket 0001's portal-reproducibility contract is unchanged. Real-world flow now one call away: Machine A: (random-seed-from-os!) + run simulation + portal-save Machine B: portal-resume — same stream, bit-for-bit All three impls fail loud on /dev/urandom trouble (LispErr in Python and C, stderr + exit(1) in asm) — no silent fallback to a weak seed. Tests: - tests/functional.lsp: 2 new shared asserts (entropic + replay) - asm/test.sh: 2 new asm-local checks (149 total, was 147) - make test-all green across Python (205), C (205), asm (149) Whitepaper §7.5 gains one sentence noting the OS-seed path. unmoad: zero new findings in added code.
4.2 KiB
0002 — (random-seed-from-os!) pulls kernel entropy
Status: open Reporter: fox (response to 0001 follow-up) Implementer: blackops Opened: 2026-04-20
Problem
Ticket 0001 gave us xoshiro256** with deterministic startup seed k = 0
and explicit (random-seed! k). Two processes running the same program
produce the same stream. That is the point — it is what lets a portal
round-trip reproduce state across machines.
But determinism has a dual cost: a program that wants a different
stream per run must seed itself from outside. Without a kernel-entropy
hook, users have to hand-roll one by opening /dev/urandom and folding
bytes into an integer. That is defect-prone (wrong endianness, short
read not handled, FD left open) and duplicates in every caller.
fox's note: "a random seed from os on at least one side is very important" — because the common real-world flow is:
- Machine A seeds from kernel entropy (
random-seed-from-os!). - Machine A runs a stochastic simulation for a while.
- Machine A portal-saves — state captured, including xoshiro256** state.
- Machine B portal-resumes — continues the same stream bit-for-bit.
Step 1 is missing today. This ticket adds it.
Goals
- One builtin, all three impls:
(random-seed-from-os!). - Reads 8 bytes from
/dev/urandom, interprets as little-endianu64, calls existingrng_seed. No new PRNG, just a different seed source. - Returns void; user captures
(random-state)if they want to replay. - Fail-loud on I/O error (missing
/dev/urandom, short read) — we do not silently fall back to a weak seed.
Non-goals
- Blocking
/dev/random(entropy-exhaustion semantics).urandomis the right default;man 4 urandomconfirms it is cryptographically strong after boot. getrandom(2)syscall. Portable to older kernels matters more than saving one open/read/close.- Any silent use of OS entropy on startup. The existing
k = 0default stays; determinism remains the default, kernel entropy is opt-in. /dev/urandomemulation on non-Linux. Lumbda targets Linux x86_64; other platforms earn their own ticket.
API
(random-seed-from-os!) → void
Reads exactly 8 bytes from /dev/urandom, little-endian, passes the
resulting u64 to the same rng_seed path as (random-seed!). After
return, (random-state) reflects the new state, and the portal system
captures it unchanged.
Errors (fail-loud):
/dev/urandomcannot be opened → error + exit(1) in asm;LispErrin Python + C.- Short read (fewer than 8 bytes) → same treatment.
Implementation
Python (~3 lines):
def _rng_seed_from_os():
with open('/dev/urandom', 'rb') as f:
b = f.read(8)
if len(b) != 8: raise LispErr('random-seed-from-os!: short read')
_rng_seed(int.from_bytes(b, 'little', signed=False))
C (~10 lines): open / read / close on /dev/urandom, then call
existing rng_seed(uint64_t).
Asm (~30 lines): SYS_OPEN, SYS_READ, SYS_CLOSE with an 8-byte
stack slot, then call rng_seed. New data:
s_dev_urandom: .asciz "/dev/urandom". Error message follows the
existing err_rng_* pattern.
Test plan
- Unit —
(random-seed-from-os!)runs without error in each tier. - Entropic — two consecutive
(random-seed-from-os!)calls in the same process produce different streams (with overwhelming probability). Asserted via(not (equal? state-a state-b)). - Determinism preserved —
(random-seed-from-os!)+ capture via(random-state)+ reseed with(random-state! saved)produces the original stream. This proves the OS seed plugs cleanly into the portal path from 0001.
Deliverables
docs/tickets/0002-os-entropy-seed.md(this ticket)- Python:
lumbda.py— helper + builtin binding - C:
c/portal.c— helper + builtin +register_portal_builtins - Asm:
asm/lumbda.s— newBI_RANDOMSEEDFROMOS, dispatch,bi_random_seed_from_os, data strings tests/functional.lsp— 2 shared assertionsasm/test.sh— 2 asm-local checks- Whitepaper §7.5 — one sentence noting the OS-seed option
make test-allgreenunmoadclean on changed files