DATA AND LOGIC FLOW
Data and logic flow
How each thing is done, who holds what, what trusts what. the in-between of the design map (why) and the code (how exactly). also shown as it is.
Data and logic flow. how each thing is done, who holds what, and what trusts what. 2026-11-05.
companion to "Design map" (why) and the code (how, exactly). this is the in-between: the flows, the files, the tables, the rules.
00the three parties
the app (client/, c++). renders. rolls. holds files. trusts only the registry's public key.
the registry (CHAOS/, sveltekit on cloudflare). keeps the books: who owns which seed, what it is worth, which copy is current. holds the only private key. derives everything itself, trusts nothing the app wrote.
the file (.chaos, 512 bytes). a receipt and a claim ticket. carries the seed, the ticket, the cert. never the truth, only evidence.
rule of thumb: the app can be reverse engineered, so nothing the app does is a security boundary. the registry is the boundary. the app gates (offline rule, previews) are policy for honest users, not walls.
at the boundary: hooks.server.ts runs first (honeypot traps -> decoy + flag; flagged addresses refused on ticket / claim / list / acquire / revoke / sign-in; security headers), then every body passes src/lib/server/input.ts (sizes, exact shapes) before any registry call. CHAOS/src/lib/server/honeypot.ts, input.ts, migrations/0002_flags.sql, static/robots.txt.
01a seed, start to pixels
seed_full: 32 bytes. identity and picture, the same number. 2^256.
sub streams: for each system X, state = blake3(seed_full || "X") into xoshiro256**. systems: rarity, mode, shape, mutation, skin, palette, lights, motion, satellites, twin, planes, debris, particles, rings, background, mood, runtime. a new system is a new label, old seeds do not move.
order of the roll (client/src/scene/dimension.cpp rollDimension, props.cpp deriveProps):
1 rarity stream -> tier (fixed odds 60/25/10/4/0.9/0.1) -> unlocks
2 mode stream -> native mode (20/35/35/10), forced bleed at legendary+
3 shape stream -> geo params, generator id (forced compound at epic+) -> family bucket
4 mutation -> lineage (twist, maybe warp, ripple, then 1..4 more gens, + unlock extras)
5 skin, palette (+ third stop at rare+), lights, motion, satellites (+ twin at rare+), planes, debris, particles, rings (x2 at legendary+), background, mood
steps 1..3 are all the registry ever needs (tier, mode, family). it runs exactly those, through the same code shape, and the vectors prove the ts and the c++ agree.
frame (client/src/render/renderer.cpp): dimension.update (mood ease, mutation clock, palette schedule) -> backdrop dome -> main berry -> satellites -> rings, debris, planes -> wire ghost -> particles -> post (bloom -> duotone -> smear if bleed -> film -> afterimage) -> target.
mode is a preset over this: zen pins mood 0, no jumps, no mutation, no swarm. calm caps mood. chaos is the roll. bleed pins wild, hard cuts, long afterimage, smear.
02seed forms
hex 64 / base58 43-44 / 24 bip39 words: the same 32 bytes three ways. --seed takes any.
short hex (8 bytes): display prefix, vault file name. not replayable.
none of these prove anything. a seed is public the moment it is seen. ownership is the registry row, not the seed.
03the roll, three kinds
preview (offline, free): seed from os entropy. file state preview, ticket zero, client_random = the raw seed. renders, can be kept, never claimable. worth nothing. allowed only when online under the offline rule (section 8), which is a policy choice, not a need.
claimable (online, commit-reveal):
a. app draws client_random (32), computes commit = blake3("CHAOS-COMMIT-v3" || client_random). chaos --commit out.reveal writes client_random, prints the commit.
b. app -> registry: POST /api/ticket { commit }. the registry checks the allowance (1/day, bank 14), draws server_random (32), signs ticket = { id(16), user, issued_ms, commit, server_random } with the private key, returns the 160 bytes, remembers the ticket id as issued and unused.
c. app: seed_full = blake3("CHAOS-SEED-v3" || ticket_id || server_random || client_random). rolls it. chaos --ticket t --reveal out.reveal. the app refuses a reveal whose commit is not the ticket's.
d. the kept file: state rolled, seed, instance_id (metadata), client_random, the whole ticket.
why nobody can grind: the client fixed its random before the registry's random existed (commit is under the signature), the registry fixed its random before it could know the client's (it only had the hash). with the ticket fixed, the seed is fixed. one ticket, one seed, and tickets are rationed.
replay: --seed <known seed> renders anyone's dimension. that is fine. it does not make it yours.
04claim
app or browser -> registry: POST /api/claim { file } (512 bytes, base64). only state rolled.
the registry, in order, refuses at the first failure:
layout (magic, version 3, ranges)
ticket signature (its own key)
commit == blake3(tag || client_random)
seed_full == blake3(tag || ticket_id || server_random || client_random)
ticket was issued here, unused, to this user
seed not already in the seeds table
the claim may carry a preview png (the app shrinks its vault png to 640 wide first); the registry checks it is a png under PREVIEW_MAX_BYTES and keeps it in r2, served at GET /api/preview/:seed.
then it ignores what the file says about itself: deriveProps(seed) -> tier, family, mode, mutation_seed written over the file's bytes. owner_id, claimed_at, a fresh nonce (16), seed_display (base58), state claimed. cert_sig = ed25519("CHAOS-CERT-v3" || bytes 8..288). value from value.ts. rows: seeds (owner, props, value, state owned, nonce), files (the signed 512 bytes), ticket used.
back to the app: the signed file. chaos --import verifies it offline (ticket sig, commit, seed, cert sig against the shipped pubkey) and stores it in the vault. the app then asks POST /api/check and records the answer (section 8).
05transfer
list: owner -> POST /api/list { seed, price }. row state listed. unlist: DELETE /api/revoke/:seed. the owner can always pull the current signed bytes: GET /api/file/:seed.
acquire: buyer -> POST /api/acquire { seed }. the registry takes the stored bytes of that seed (never the buyer's upload), sets owner = buyer, claimed_at, a new nonce, re-signs the cert, stores the new bytes, row owner/nonce/state owned, transfers row (price recorded, money not collected until v1.3).
the seller still holds a file with a real signature. offline it verifies. POST /api/check says: known, owner = buyer, this copy STALE (nonce mismatch). the app's sync (v1.0) marks it transferred. copying a file never moved anything: only this flow moves ownership.
06check (public)
anyone -> POST /api/check { file }. no login. answer:
check: layout, ticket sig, commit, seed derivation, cert sig, and the first failure in words
file: state, tier, family, mode, owner, seed (as written, unverified until the row says so)
registry: known? owner, state, is this exact copy current (nonce), value
/check is the page. this is "is it legit and what is it worth".
07verify, offline (client/src/chaos_file/format.cpp, same rules in format.ts)
preview: layout, and no ticket.
rolled: layout, ticket sig, commit, seed derivation.
claimed: all of that, cert sig, owner != 0.
offline verify answers "was this ever real". only the registry answers "is this copy current" and "who owns it now".
7b. the app's session (v1.0)
the app never talks to google. <registry>/login?app=1 signs you in and shows the session jwt. the app stores it in ~/.config/chaos/session (0600) and sends it as Authorization: Bearer on /api/me, /api/ticket, /api/claim, /api/vault, /api/sync, /api/file/:seed.
claimable roll, one go (chaos --roll, or the panel button): draw client_random, commit, POST /api/ticket { commit }, check the ticket's signature and commit, seed = derive(ticket, client_random), roll, keep (state rolled). then claim (--claim, or "claim this roll"): POST /api/claim, verify the reply with the shipped pubkey, store it over the rolled file, activate.
sync (--sync, or "sync vault"): POST /api/sync with every non-preview seed you hold -> each activation entry updated (current, or transferred away). then GET /api/vault, and every seed the registry says you own but you do not hold is fetched from GET /api/file/:seed, verified, stored, activated. that is how a seed acquired on the web reaches the buyer's desktop and leaves the seller's.
08the offline rule (policy, app side)
"once loaded online, usable offline. never generated offline."
at start the app pings GET /api/health (2.5 s) and checks the registry's pubkey is the one it ships. online or not.
online: everything. loading a vault file also calls POST /api/check and, if valid and known, writes ~/.local/share/chaos/activated.json { seed: { at, owner, state, current, value } }. that is the activation. --import does the same.
signup is a must (2026-11-06): the app generates nothing, previews included, without a session the registry accepts (App::canGenerate = devOffline || online && signed in; cli RegistryState::signedIn from a GET /api/me at start). without one it opens on a kept or activated seed, else the lobby, and says "sign in (account tab) to roll". claimable rolls: one per ROLL_PERIOD_DAYS (7) per account, BANK_MAX 1.
offline: no previews, no rerolls, no tickets, no replaying arbitrary seeds. allowed: loading a vault file whose seed is in activated.json, and wallpaper mode (app and plasma plugin, client/src/wallpaper_seed.cpp) on any seed kept in this install's vault or activated here; with no seed configured it plays the first activated one, else the newest kept one, else (plugin only) the lobby. relaxed 2026-11-06: showing again what this machine already rolled is not generation. the panel shows OFFLINE and the roll tab says why.
--dev-offline lifts the gate for development. it exists because the gate is client code and a wall it is not: the registry is where the real limits live (tickets, one claim per seed, server-side rarity).
09wallpaper mode
chaos --wallpaper: forces x11 (xwayland on wayland), one desktop-type window per monitor (_NET_WM_WINDOW_TYPE_DESKTOP, below, sticky, no input region), own gl context and renderer each. seed from flags, else settings.json, else random (online) or an activated one (offline). fps cap (30), pause when the active window is fullscreen or on battery (polled every 0.5 s). no panel, no keys: drive it from the normal window's settings tab and restart. autostart is an xdg .desktop entry.
10what lives where
app, per install
~/.config/chaos/instance 16 random bytes, in every file, metadata (bannable, never hashed)
~/.config/chaos/settings.json seed, mode, monitors, wallpaper_fps, pauses, vsync, fps_cap, poster, registry_url
~/.config/autostart/chaos.desktop when autostart is on
~/.local/share/chaos/vault/<short>.chaos + .png
~/.local/share/chaos/activated.json
~/.config/chaos/session the registry session jwt, 0600
shipped in the binary: the registry public key (shared/server_pubkey.h). nothing secret.
registry (D1)
users id, google_sub, email, display_name, banked, last_accrual_ms, created_ms
tickets id_hex, user_id, issued_ms, used
seeds seed_hex, owner_id, family, tier, mode, rarity_score, value_cents, state, nonce_hex, price_cents, claimed_ms, updated_ms
files seed_hex, bytes (the current signed 512)
transfers id, seed_hex, from_user, to_user, price_cents, state, created_ms
secrets: CHAOS_SERVER_KEY (the private key), SESSION_SECRET, GOOGLE_CLIENT_ID
the file (shared/chaos_format.h, v3)
0 magic, version, state
8 seed_full
40 instance_id
56 client_random
88 ticket: id, user, issued, commit, server_random, sig (160, what /api/ticket returns)
248 family, tier, mode, mutation_seed
256 claimed_at, owner, nonce, cert_sig
352 preview_hash, seed_display, display_len, reserved
11identity and sessions
google identity services on /login -> id token -> POST /api/auth/google. the registry verifies it against google's jwks (jose), finds or creates the user, issues a 30-day HS256 jwt. browser: httpOnly cookie chaos_session. app (v1.0): the same jwt as Authorization: Bearer. the app never sees a google password; the registry never sees one either.
12value and config
rarity is the seed's. value is the market's. value_cents = floor(tier) * family * mode * age * supply, in CHAOS/src/lib/server/value.ts. the numbers (floors, multipliers, allowance, session, preview cap, page sizes, site name) come from CHAOS/src/lib/server/config.ts, which reads the environment: the cloudflare dashboard (variables + secrets) in production, the defaults locally. the app never knows these numbers; it asks /api/check. the client's own constants: CHAOS_REGISTRY_URL (cmake option), timeouts and the preview upload width/cap in client/src/sync/.
13trust table
who holds trusts
app pubkey, files, settings registry signatures. nothing else.
registry private key, the books its own derivations. not the file's bytes, not the client's claims.
file evidence nobody. it is read, verified, then ignored in favour of the row.
browser a session cookie the registry's answers.
14where each piece of code is
seed + rng + forms client/src/seed/
roll + scene + modes client/src/scene/, client/src/shape/
render + post client/src/render/, client/shaders/
.chaos read/verify/sign client/src/chaos_file/format.cpp <-> CHAOS/src/lib/server/format.ts
registry rules client/tools/registry_dev.cpp (dev) <-> CHAOS/src/lib/server/registry.ts
props the registry derives client/src/scene/props.cpp <-> CHAOS/src/lib/server/props.ts, rng.ts
vault, activation, settings client/src/vault/, client/src/settings.cpp
registry api client client/src/sync/api.cpp (health, check, session, me, ticket, claim, vault, sync, file), client/src/sync/session_flow.cpp (roll in one go, claim, sync)
wallpaper client/src/wallpaper.cpp, client/src/platform/x11_wallpaper.cpp, client/src/platform/wl_layer.cpp
wallpaper seed rule client/src/wallpaper_seed.cpp (chaos_core: pickWallpaperSeed, lobbySeed), client/src/platform/power.cpp
registry env + bindings CHAOS/src/env.ts (every variable, kit 3 defineEnvVars), CHAOS/src/lib/server/config.ts ($app/env/private by key), context.ts (D1 via cloudflare:workers env)
panel + poster client/src/ui/panel.cpp (imgui, the site's tokens in tk::, helpers lbl / h2 / tag / key / msg)
draw layers + quality client/src/render/renderer.h (Layers: satellites / swarm / particles; Quality: scale + msaa. display choices, never part of the seed), settings.json keys layers / render_scale / msaa
your model (.cheng) client/src/model/cheng.cpp (import, prepare, write, read, modelsDir), scene/dimension.cpp applyModel, renderer mono / wire skins, settings model_*, panel tabModel, cli --cheng / --model, plugin Model* config + chaositem.cpp
covered desktop client/plasma/package/contents/ui/Coverage.qml (TasksModel -> coverage 0/1/2), ChaosView::setCoverage / repace
the mark client/src/ui/mark.h (16x16 bitmap; window icon, panel brand), CHAOS/static/favicon.svg, packaging/chaos.svg (generated from the same rows)
plasma plugin client/plasma/chaositem.cpp (ChaosView fbo item, FboRenderer, vsync or timer pacing, scaled fbo, status bus -> ~/.cache/chaos/plasma.log, ChaosVault singleton for the settings page), client/plasma/package/
panel + poster client/src/ui/
http routes CHAOS/src/routes/api/*/+server.ts
pages CHAOS/src/routes/*/+page.svelte (layout + theme in +layout.svelte, shared bits in src/lib/ui.ts)
cross-language proof client/tools/vectors.cpp -> CHAOS/test/vectors -> vitest