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

139 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
```sh
# 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
```sh
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 /