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.
This commit is contained in:
@@ -79,6 +79,51 @@ the spec never defined.
|
||||
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
|
||||
|
||||
@@ -87,4 +132,7 @@ 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 /
|
||||
|
||||
Reference in New Issue
Block a user