Files
cubelinux-2/cubesys/docs/integration.md
T
CUBELinux-2 06ea3252eb cubesys: add cube systemd daemon (cube-server) + socket client (cubec)
Implements the requested cube service: a long-lived daemon that holds ONE
CubeStore for its whole lifetime and serves the cube command language over a
Unix-domain socket, plus cubec to talk to it.

- cubesys::commands: factored the single command interpreter (Session::exec)
  so cube REPL, cubec client, and the daemon run identical logic
- cubesys::net: dependency-free length-framed AF_UNIX transport
- cubesys::persist: dependency-free JSON snapshot (atomic tmp+rename) so the
  store -- including sealed/encrypted records -- survives daemon restarts
- cube-server: listens on $XDG_RUNTIME_DIR/cube/cube.sock, snapshots to
  $XDG_STATE_HOME/cube/cube-store.json, replays on startup
- cubec: one-shot + REPL client over the socket
- cube.rs trimmed to a thin REPL/script/demo driver (help text updated)
- /etc/systemd/system/cube.service: runs as luulu, ProtectSystem=strict,
  RestrictAddressFamilies=AF_UNIX, Restart=on-failure; enabled + active
- integration.md documents the daemon + caveat (open rewrites plaintext)

Verified: ./check (fmt+tests+clippy -D warnings) green; ./check mount (27
FUSE e2e) green; socket CLI round-trips; sealed record survived a full
service restart and reopened+r with original value.
2026-08-11 00:10:45 -04:00

6.9 KiB
Raw Blame History

CUBELinux-2 system integration (cubesys)

This crate is the composition layer the PDF implies but does not name as a "Package 6". It binds Packages 3-5 (cubefs, cubecode, cubecrypt) into one running system over a single shared CubeStore, which is the literal CUBELinux premise: data addressed by coordinate, not path.

The one invariant

The cube is the single source of truth. cubefs is a view of it, cubecode is a kind of record in it, cubecrypt is a transform applied to records in it. None of them own storage.

That is what makes the integration real instead of three crates side by side: every layer operates on the same backend, so a write through one is visible to the others at the same coordinate.

What binds to what

Layer Role in the system Shares via
cubefs POSIX namespace; a path C/Z/Y/X is a coordinate CubeStore, path_to_czyx
cubecode code cells stored as records; VM runs them CubeStore, CodeCell
cubecrypt seals records under a CubeEnv; key in Null cubes CubeStore, CubeEnv

Two primitives in this crate do the binding:

  • path_to_czyx(path) — the single bijection (re-exported from cubefs) so path and coordinate never disagree.
  • load_code_cell(store, path) — reads the record at a path's coordinate and decodes it as cubevm bytecode. A cubefs file is a runnable VM cell.

End-to-end properties proved by the tests

  1. A path is a function. Writing bytecode at /c012/z001/y001/x021 makes cubefs list it as a file and the VM run it at Czyx::new(12,1,1,21) — same coordinate, two views. (Note: an axis value of 0 is Null control space and is rejected as a path component, so every user path maps to a coordinate with all axes in 1..=255; keys and metadata live in Null space and are addressed directly by C.Z.Y.X.)
  2. Seal then run is the same record. CubeEnv::put_encrypted at a coordinate leaves the cube carrying an encrypted body (encrypted flag set); the raw body no longer decodes as bytecode. To execute a sealed record the system decrypts it back into a plaintext record at the same coordinate, then runs the VM (the VM runs code located by coordinate; reading the envelope directly would fail decode). open + CodeCell::from_record + Vm::run recovers and executes it on the same store.

Bugs found and fixed while integrating (real, cross-crate)

Building the integration turned up two latent cross-crate bugs that the per-package test suites never exercised:

  • CubeHeader::refresh_flags() dropped out-of-band flag bits. It recomputed flags only from structured fields (bits 07), so cubecrypt's HEADER_FLAG_ENCRYPTED (bit 12), which is set out-of-band, was wiped on every refresh. put_encrypted calls refresh_flags before storing, so an encrypted record lost its flag. Fixed by preserving spare bits (8..=15) in refresh_flags. (cubecoords)
  • The record codec never serialized flags. record_codec::encode_header wrote only structured fields, and decode_header rebuilt flags from them — so even a correctly-set encrypted bit was lost on the store round-trip. Added tag 12 (raw flag bits) to the TLV codec so out-of-band bits survive. (cubestore)

Both are now guarded by cubesys integration tests.

Why this, and not a "Package 6"

The PDF arc ends at cubecrypt; the only named next package is cubeai, which the directive explicitly excluded (hardware cannot run it). Integration is the honest completion: it turns five isolated crates into the coherent coordinate-addressed system the design describes, without inventing a package the spec never defined.

Binaries

  • cube-demo — self-contained tour (write two linked cells, run; seal a record, reopen + run). Prints evidence at each step.
  • cube — CLI: write, run, ls, stat, seal, open over one store, interactively (repl), from a file (script), or as the demo (demo).
  • cube-server — long-lived daemon: holds ONE CubeStore for its whole lifetime and serves the same command language over a Unix-domain socket. Snapshots the store to a JSON file on every request so the cube survives restarts (see below).
  • cubec — client for cube-server: one-shot (cubec prog ...) or REPL (cubec with stdin), talking the framed Unix socket.

The daemon: cube-server + cubec

The directive asked for "a cube systemd service that holds the store and exposes the CLI over a socket." That is cube-server + cubec:

  • One store, one process. The daemon owns a single [Session] (one CubeStore) and serves requests sequentially. Every front-end — cube, cubec, the daemon — executes the identical command interpreter (cubesys::commands::Session::exec), so behavior cannot drift.
  • Socket transport (cubesys::net): each request is one frame ([u32 len][utf8 command line]), one reply frame. No delimiters, no partial reads. Default socket is $XDG_RUNTIME_DIR/cube/cube.sock.
  • Persistence (cubesys::persist): every record is serialized to a JSON snapshot ($XDG_STATE_HOME/cube/cube-store.json) after each mutating command (atomic temp-write + rename). On startup the daemon replays the snapshot so sealed/encrypted state survives a restart. Verified: a record sealed, then the service restarted, then open+run still halts with the original value.
  • systemd unit (/etc/systemd/system/cube.service): runs as luulu, Restart=on-failure, ProtectSystem=strict, RestrictAddressFamilies=AF_UNIX (socket only), writes confined to /home/luulu/.cubelinux and the runtime dir. Enabled + running.
# daemon already running via systemd; talk to it:
cubec prog /c005/z001/y001/x007 const 7 halt
cubec run  /c005/z001/y001/x007
cubec seal /c005/z001/y001/x007 0.1.0.1 gcm
cubec open /c005/z001/y001/x007 0.1.0.1 gcm
cubec ls /

Caveat (inherited from the CLI): open decrypts a sealed record and writes the plaintext back to the same coordinate before running, so the encrypted state is replaced by plaintext after one open. That is the original cube/cubesys behavior and is acceptable for a demo/control socket; a read-only "open" (decrypt into a scratch coordinate, run, leave the sealed record intact) would be the fix if sealed-at-rest must be preserved across reads.

Building / testing

cd /home/CUBELinux/CUBELinux-2
./check quick      # fmt + clippy -D warnings + tests (the gate)
./check mount      # also builds cubefs --features mount (FUSE adapter)
cargo run -p cubesys --bin cube-demo
cargo run -p cubesys --bin cube -- repl      # local in-process REPL
# daemon mode:
systemctl status cube.service
cubec --socket /run/user/1000/cube/cube.sock ls /