name: "Running This Execution-Zone Module Against logoscore" output: logos-execution-zone-runtime.md release: "" intro: | `logos-execution-zone` is a Logos `core` module that wraps the [Logos execution-zone wallet library](https://github.com/logos-blockchain/lssa) (`wallet_ffi`) to provide wallet lifecycle, account management, balance and block queries, transfers, pinata claiming, and account-id encoding. This doc-test exercises **this** execution-zone-module commit end-to-end through the headless `logoscore` runtime: 1. Build the `logoscore` CLI and the `lgpm` local package manager from their published flakes. `logoscore` is the headless frontend for `logos-liblogos`, so building it brings in the whole module-runtime stack (`logos_host`, `liblogos_core`, the IPC layer). 2. Build **this** execution-zone module as an installable `.lgx` package straight from its own flake's `#lgx` output, **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. 3. Install the `.lgx` into a `./modules` directory with `lgpm`. 4. Start `logoscore` in daemon mode (`-D`), load `logos_execution_zone`, introspect it with `module-info`, and call its wallet-free methods — verifying the module actually runs and round-trips real values through `wallet_ffi`. The methods exercised here — `name`/`version` and the base58 account-id codec (`account_id_to_base58` / `account_id_from_base58`) — are the module's deterministic, **offline** surface: they need neither an open wallet nor a live sequencer, so a green run is reproducible in CI. The stateful operations (creating accounts, transfers, sync, pinata claims) require a running sequencer and network, and are covered by the module's unit tests (mocked `wallet_ffi`) and integration tests (real `wallet_ffi`) instead. 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 execution-zone module loadable and callable. what_you_build: "This `logos_execution_zone` module, packaged as `.lgx`, installed with `lgpm`, and called through a `logoscore` daemon." what_you_learn: - How to build the `logoscore` runtime and the `lgpm` package manager from their flakes - How a module's flake exposes a ready-to-install `.lgx` via its `#lgx` output - How to install an `.lgx` into a modules directory with `lgpm` - How to start the `logoscore` daemon, load a module, introspect it, and call its methods - How to shut the daemon down and confirm it has exited prerequisites: - | **Nix** with flakes enabled. Install from [nixos.org](https://nixos.org/download.html), then enable flakes: ```bash 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.**" sections: - title: "Build logoscore" step: true text: | 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. steps: - title: "Build the CLI" run: "nix build 'github:logos-co/logos-logoscore-cli' --out-link ./logos" code_block: | nix build 'github:logos-co/logos-logoscore-cli' --out-link ./logos check_file: "logos/bin/logoscore" post_text: | 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). - title: "Build the lgpm package manager" step: true text: | `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`. steps: - title: "Build lgpm" run: "nix build 'github:logos-co/logos-package-manager#cli' -o lgpm" check_file: "lgpm/bin/lgpm" post_text: "The executable is at `./lgpm/bin/lgpm`." - title: "Build and install this execution-zone module" step: true text: | Build **this** execution-zone 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`](https://github.com/logos-co/logos-module-builder) exposes a ready-to-install `#lgx`. > The `{release}` 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` (see `run.sh`); in CI it is the commit being tested. With > no pin it falls back to the latest `master`. steps: - title: "Build the module's .lgx" text: | Build the `#lgx` output and link it as `./lez-lgx`. (This compiles the module and the `wallet_ffi` library it depends on through Nix, so the first build is slow.) run: "nix build 'github:logos-blockchain/logos-execution-zone-module{release}#lgx' -o lez-lgx" code_block: | # From inside the clone this is simply: nix build '.#lgx' nix build 'github:logos-blockchain/logos-execution-zone-module{release}#lgx' -o lez-lgx post_text: "The `.lgx` package is now under `./lez-lgx/`:" extra_run: run: "ls lez-lgx/*.lgx" - title: "Seed the modules directory with the bundled capability module" text: | `logos_execution_zone` 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. run: | mkdir -p modules cp -RL ./logos/modules/. ./modules/ check_file: "modules/capability_module/manifest.json" - title: "Install the .lgx with lgpm" text: | Install the freshly-built package into `./modules`. `logos_execution_zone` is a `core` module, so it goes to `--modules-dir`. The package is unsigned (a local dev build), so we pass `--allow-unsigned`. run: "./lgpm/bin/lgpm --modules-dir ./modules --allow-unsigned install --file lez-lgx/*.lgx" expect_contains: - "Installed to:" - title: "Confirm the install" text: "Scan the directory and confirm the module landed:" run: "./lgpm/bin/lgpm --modules-dir ./modules list" expect_contains: - "lez_core" check_file: "modules/lez_core/manifest.json" - title: "Run the daemon and call the module" step: true text: | Start `logoscore` in daemon mode pointed at `./modules`, then use the client subcommands to load `logos_execution_zone`, introspect it, and call several of its methods. Daemon output is captured in `logs.txt`. steps: - title: "Start the daemon" text: | Start logoscore in daemon mode in the background, capturing output to `logs.txt`: run: "sh -c './logos/bin/logoscore -D -m ./modules > logs.txt 2>&1 &'" code_block: "logoscore -D -m ./modules > logs.txt &" post_text: | The `-D` flag starts the daemon. The client subcommands below connect to this running process via the config written under `~/.logoscore/`. - run: "sleep 3" - title: "Inspect the startup log" text: "Review the daemon's startup output:" run: "cat logs.txt" - title: "Check daemon status" text: "Verify the daemon is running:" run: "./logos/bin/logoscore status" code_block: "logoscore status" - title: "List discovered modules" text: "`lez_core` should be visible in the scan directory:" run: "./logos/bin/logoscore list-modules" code_block: "logoscore list-modules" expect_contains: - "lez_core" - title: "Load the module" text: "Load `lez_core` into the running daemon:" run: "./logos/bin/logoscore load-module lez_core" code_block: "logoscore load-module lez_core" expect_contains: - "lez_core" - title: "Confirm the module is loaded" text: | Re-run `status`; the module that was `not_loaded` before now reports `loaded`: run: "./logos/bin/logoscore status" code_block: "logoscore status" expect_contains: - "lez_core" - '"status":"loaded"' - title: "Introspect the module with module-info" text: | `module-info` lists the methods the module exposes — the same methods you can `call`: run: "./logos/bin/logoscore module-info lez_core" code_block: "logoscore module-info lez_core" expect_contains: - "lez_core" - "account_id_to_base58" - "account_id_from_base58" - title: "Read the module name" text: | `name` returns the module's own identifier — the simplest possible round-trip through the loaded plugin over liblogos' IPC: run: "./logos/bin/logoscore call lez_core name" code_block: "logoscore call lez_core name" expect_contains: - '"result":"lez_core"' - title: "Read the module version" text: | `version` returns the module's semantic version (`0.2.0`), matching `metadata.json`: run: "./logos/bin/logoscore call lez_core version" code_block: "logoscore call lez_core version" expect_contains: - '"result":"0.3.0"' - title: "Encode an account id to base58" text: | `account_id_to_base58` takes a 32-byte account id as 64 hex characters and returns its base58 form. This is a pure encoding helper in `wallet_ffi` — no open wallet and no network required, so it runs entirely offline: run: "./logos/bin/logoscore call lez_core account_id_to_base58 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" code_block: "logoscore call lez_core account_id_to_base58 aaaaaaaa...aaaa # 64 hex chars" expect_contains: - '"result"' - title: "Round-trip it back to hex" text: | `account_id_from_base58` is the inverse: feed it the base58 string we just produced and it returns the original 64-hex account id. Encoding then decoding the same id and recovering the input is a deterministic, end-to-end proof that the codec — and the IPC path to this module — work: run: | ENC=$(./logos/bin/logoscore call lez_core account_id_to_base58 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) B58=$(printf '%s' "$ENC" | sed -n 's/.*"result":"\([^"]*\)".*/\1/p') echo "encoded: $B58" ./logos/bin/logoscore call lez_core account_id_from_base58 "$B58" code_block: | # Encode, then decode the result back — the round-trip returns the input. B58=$(logoscore call lez_core account_id_to_base58 aaaa...aaaa) logoscore call lez_core account_id_from_base58 "$B58" expect_contains: - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - title: "Reject malformed base58" text: | Decoding obvious garbage fails cleanly: `account_id_from_base58` returns an empty result rather than crashing the module or the daemon: run: "./logos/bin/logoscore call lez_core account_id_from_base58 '!!!not-base58!!!'" code_block: "logoscore call lez_core account_id_from_base58 '!!!not-base58!!!'" expect_contains: - '"result":""' - title: "Stop the daemon" text: "Shut the daemon down cleanly:" run: "./logos/bin/logoscore stop" code_block: "logoscore stop" post_text: | The daemon removes its state file and exits. - run: "sleep 2" - title: "Confirm the daemon has stopped" text: | 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: run: "./logos/bin/logoscore status || true" code_block: "logoscore status" expect_contains: - '"status":"not_running"'