From 0bd4985e07b788e20e7408ff055e5e44e60a80b8 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 6 Sep 2026 16:08:44 +0200 Subject: [PATCH] Record the CultLib store divergence as a blocker for TypeScript targets Enrolling Heimdall's signer surfaced that cultcache-ts cannot read a single-file store written by cultcache-rs at all: pullAll() throws invalid_type, expected object received array. Verified against the real idunn-provision enrolment on yggdrasil. Idunn writes every store a target must read -- presence identity, process write lease, expected incarnation -- with the Rust client. Rust targets consume them natively; TypeScript targets cannot, and must decode the layout by hand. That makes this a per-runtime blocker rather than a Heimdall quirk, so it belongs in the migration route. Worth separating what broke from what held: the record schemas are identical across runtimes, including the identity's protector binding. The contracts are fine. The store framing drifted. Which framing is canonical is a CultLib decision and is not taken here; the C# implementation is the stated reference and is the tiebreaker. Co-Authored-By: Claude Opus 5 --- docs/migration.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/migration.md b/docs/migration.md index 7ea8602..cda5eb7 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -349,6 +349,38 @@ targets. `/run/idunn/route-preflight`; the unit now declares `RuntimeDirectory=idunn`, which creates and removes `/run/idunn` with the service. +## Blocker for every TypeScript target: the CultLib store formats diverged + +Found 2026-09-06 while enrolling Heimdall's signer. **`cultcache-ts` cannot read +a single-file store written by `cultcache-rs`.** Verified against a real +`idunn-provision` enrolment on yggdrasil, not inferred: + +``` +cultcache-rs [ [key, type, payload, stored_at, schema_id], ... ] +cultcache-ts [ formatVersion, catalog, records ] envelopes as named objects +``` + +Handing the Rust file to `SingleFileMessagePackBackingStore.pullAll()` throws +`invalid_type: expected object, received array` at path `[0]`. + +This matters because Idunn writes every store a target must read — the runtime +presence identity, the process write lease, the expected incarnation in the +runtime bundle. A Rust target consumes them with its own client. A TypeScript +target cannot, and has to decode the Rust layout by hand. Heimdall does that in +`src/idunn-store.ts`, which exists solely because the two forks disagree and +should be deleted the moment they are reconciled. + +Two things follow. Every future TypeScript target hits this, so it is a +per-runtime problem rather than a Heimdall quirk. And **the schemas are +identical while the container formats are not** — the identity record itself +uses the same `gamecult.provider_health_identity.private.v1` schema and the same +`linux_file_mode_machine_id_binding` protector in both runtimes, which is why an +`idunn-provision` enrolment is readable once the envelope is unwrapped +correctly. The contracts held; the store framing drifted. + +Which format is canonical is a CultLib decision and is not made here. The C# +implementation is the stated reference, so it is the tiebreaker. + ## Cutting over the authority Idunn last, and not by deploying Idunn with Idunn.