11 KiB
Running This Storage Module Against logoscore
logos-storage-module is a Logos core module that wraps the
libstorage C library to
run a decentralised storage node — upload, download, and data-management
operations over a libp2p network. This doc-test exercises this
storage-module commit end-to-end through the headless logoscore runtime:
- Build the
logoscoreCLI and thelgpmlocal package manager from their published flakes.logoscoreis the headless frontend forlogos-liblogos, so building it brings in the whole module-runtime stack (logos_host,liblogos_core, the IPC layer). - Build this storage module as an installable
.lgxpackage straight from its own flake's#lgxoutput, pinned to the commit under test — so the module you run is built from exactly what is checked out here, not the latest published release. - Install the
.lgxinto a./modulesdirectory withlgpm. - Start
logoscorein daemon mode (-D), loadstorage_module, introspect it withmodule-info, and drive a real node lifecycle: start it from a config, read its identity, upload a local file, and stop it again — verifying the module actually runs and round-trips real values through libstorage.
Because the module is built from the commit under test and then loaded and called
through a real logoscore daemon, a green run is real evidence that this change
keeps the storage module loadable and callable.
What you'll build: This storage_module, packaged as .lgx, installed with lgpm, and driven through a logoscore daemon — node start, a local file upload, and shutdown.
What you'll learn:
- How to build the
logoscoreruntime and thelgpmpackage manager from their flakes - How a module's flake exposes a ready-to-install
.lgxvia its#lgxoutput - How to install an
.lgxinto a modules directory withlgpm - How to start the
logoscoredaemon, load a module, introspect it, and call its methods - How to configure, start, exercise, and stop a libstorage node headlessly
- How to shut the daemon down and confirm it has exited
Prerequisites
- Nix with flakes enabled. Install from nixos.org, then enable flakes:
mkdir -p ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
Verify: nix flake --help >/dev/null 2>&1 && echo "Flakes enabled"
- A Linux or macOS machine.
Step 1: Build logoscore
Build the logoscore CLI from its published flake. The result is symlinked to
./logos/. logoscore is the headless frontend for logos-liblogos, so this
one build brings in the whole module-runtime stack the daemon needs.
1.1 Build the CLI
nix build 'github:logos-co/logos-logoscore-cli' --out-link ./logos
The build produces logos/bin/logoscore plus bundled runtime libraries
and a logos/modules/ directory containing the built-in
capability_module (required for the auth handshake when loading
modules).
Step 2: Build the lgpm package manager
lgpm installs .lgx packages into a modules directory and scans what is
installed. Build it from the logos-package-manager flake and link it as
./lgpm.
2.1 Build lgpm
nix build 'github:logos-co/logos-package-manager#cli' -o lgpm
The executable is at ./lgpm/bin/lgpm.
Step 3: Build and install this storage module
Build this storage module's .lgx straight from its flake's #lgx
output and install it into a local ./modules directory with lgpm. Every
module built with
logos-module-builder
exposes a ready-to-install #lgx.
The `` in the URL is what pins the build to a specific commit: the doc-test runner expands it to a concrete ref. Locally that is this checkout's
HEAD(seerun.sh); in CI it is the commit being tested. With no pin it falls back to the latestmaster.
3.1 Build the module's .lgx
Build the #lgx output and link it as ./storage-lgx. (This compiles
the module and its libstorage dependency through Nix, so the first build
is slow.)
# From inside the clone this is simply: nix build '.#lgx'
nix build 'github:logos-co/logos-storage-module/f258c7db9c4f1354f401dd68e4d0b48b3a08fd36#lgx' -o storage-lgx
The .lgx package is now under ./storage-lgx/:
ls storage-lgx/*.lgx
3.2 Seed the modules directory with the bundled capability module
storage_module is loaded through the host's capability layer, so the
modules directory also needs the capability_module that ships with
logoscore. Copy it across first.
mkdir -p modules
cp -RL ./logos/modules/. ./modules/
3.3 Install the .lgx with lgpm
Install the freshly-built package into ./modules. storage_module is
a core module, so it goes to --modules-dir. The package is unsigned
(a local dev build), so we pass --allow-unsigned.
./lgpm/bin/lgpm --modules-dir ./modules --allow-unsigned install --file storage-lgx/*.lgx
3.4 Confirm the install
Scan the directory and confirm the module landed:
./lgpm/bin/lgpm --modules-dir ./modules list
Step 4: Run the daemon and call the module
Start logoscore in daemon mode pointed at ./modules, then use the client
subcommands to load storage_module, introspect it, and drive a node
lifecycle. Daemon output is captured in logs.txt.
4.1 Start the daemon
Start logoscore in daemon mode in the background, capturing output to
logs.txt:
logoscore -D -m ./modules > logs.txt &
The -D flag starts the daemon. The client subcommands below connect to
this running process via the config written under ~/.logoscore/.
sleep 3
4.2 Inspect the startup log
Review the daemon's startup output:
cat logs.txt
4.3 Check daemon status
Verify the daemon is running:
logoscore status
4.4 List discovered modules
storage_module should be visible in the scan directory:
logoscore list-modules
4.5 Load the module
Load storage_module into the running daemon:
logoscore load-module storage_module
4.6 Confirm the module is loaded
Re-run status; the module that was not_loaded before now reports
loaded:
logoscore status
4.7 Introspect the module with module-info
module-info lists the Q_INVOKABLE methods the module exposes — the
same methods you can call:
logoscore module-info storage_module
4.8 Write the node configuration
storage_module.start takes a JSON configuration string. We keep it
minimal and let libstorage fill in sensible defaults for everything else
(listen addresses, repo kind, quota, discovery) — that already yields a
fully isolated, working node:
data-dir— the node's on-disk repo. We use an absolute path and create the directory first, because in daemon mode the module runs as its own process whose working directory may differ from this one, and libstorage opens the repo at exactly the path given.log-level/log-file— send the node's logs to a file in that dir.
The directory is created and the config written in one step (using
$(pwd) so the paths are absolute):
mkdir -p "$(pwd)/storage-data"
cat > config.json <<EOF
{
"data-dir": "$(pwd)/storage-data",
"log-level": "DEBUG",
"log-file": "$(pwd)/storage-data/storage.log"
}
EOF
4.9 Start the node
start creates and configures a libstorage node from the JSON config,
then brings the libp2p node online.
The @config.json syntax loads the file's contents as the argument. It
is asynchronous: the return value only confirms the start command was
accepted; the real outcome is delivered as a storageStart event in the
daemon log. The remaining calls all run against this started node.
logoscore call storage_module start @config.json
4.10 Wait for the node to come up
Give the node a moment to start, then inspect the log for the storageStart event:
sleep 3
cat logs.txt
Look for the emitted storageStart event carrying
{ "success": true, ... }.
4.11 Read the libstorage version
version returns the libstorage version string — a real round-trip
through the C library wrapped by the module, dispatched over liblogos'
IPC:
logoscore call storage_module version
4.12 Read the node's data directory
dataDir returns the path of the node's on-disk repo — the data-dir
from the config, resolved by libstorage:
logoscore call storage_module dataDir
4.13 Read the node's peer ID
peerId returns the node's libp2p
peer identity —
meaningful only once the node has started:
logoscore call storage_module peerId
4.14 Read the node's Signed Peer Record
spr returns the node's Signed Peer Record (its self-certified addresses):
logoscore call storage_module spr
4.15 Inspect storage space
space returns a JSON object describing the node's quota and usage. It
exercises a JSON-object round-trip:
logoscore call storage_module space
4.16 List manifests (empty baseline)
manifests lists everything stored locally. On a fresh node this is an
empty array — we'll call it again after an upload to see it change:
logoscore call storage_module manifests
4.17 Upload a local file
Create a small file and upload it. uploadUrl takes an absolute path
(the daemon resolves it from its own working directory) and a chunk size
in bytes, and returns a session ID immediately; the upload itself runs in
the background. On a fresh fs node with no peers the blocks are stored
locally, so this is a real, fully-offline round-trip.
Hello from the logos-storage-module doc-test.
logoscore call storage_module uploadUrl "$(pwd)/hello.txt" 65536
4.18 Wait for the upload to complete
Give the upload a moment, then inspect the log for the storageUploadDone event and its CID:
sleep 3
cat logs.txt
Look for the emitted storageUploadDone event carrying the new content's
cid — proof the file was chunked, stored, and a manifest written.
4.19 List manifests (now populated)
Call manifests again. The uploaded file now appears as a stored
manifest — we assert on presence rather than the (non-deterministic) CID:
logoscore call storage_module manifests
4.20 Stop the node
stop shuts the libp2p node down and releases the libstorage context.
Like start it is asynchronous; the return confirms the stop command
was sent, and a storageStop event follows in the log. The node can be
started again later by calling start @config.json.
logoscore call storage_module stop
sleep 2
4.21 Stop the daemon
Shut the daemon down cleanly:
logoscore stop
The daemon removes its state file and exits.
sleep 2
4.24 Confirm the daemon has stopped
With no daemon running, the client reports not_running and exits
non-zero, so we add || true to let the doc-test assert on the output:
logoscore status