logos-blockchain-module/doctests/blockchain-module-config.test.yaml

210 lines
9.1 KiB
YAML
Raw Normal View History

2026-08-07 10:59:59 +00:00
name: "Generating and Inspecting a Logos Blockchain Node Config"
output: blockchain-module-config.md
release: ""
intro: |
`blockchain_module` wraps the [`logos-blockchain`](https://github.com/logos-blockchain/logos-blockchain)
node as a Logos module. Before a node can run it needs a **user config** — a YAML
file describing the keystore, the network ports, the bootstrap peers to sync
from, and where the node keeps its state, database and logs.
The module generates that file for you through `generate_user_config`, so the
operator never has to hand-write YAML. This doc-test drives that call through
the headless `logoscore` daemon and inspects what comes out, then shows what
happens when you try to generate a second config into the same instance.
> No node is started here — config generation is entirely local and needs no
> network. For starting and stopping a node see the companion
> [`blockchain-module-runtime`](./blockchain-module-runtime.md) doc-test.
what_you_build: "A generated `user_config.yaml` for a Logos blockchain node, produced through the module's `generate_user_config` call and inspected for the values that were asked for."
what_you_learn:
- How a Logos module is packaged as `.lgx` and installed with `lgpm`
- How to call a module's methods headlessly through the `logoscore` daemon
- What `generate_user_config` writes, and which knobs it accepts
- Why a second generation into the same instance is refused, and what to use instead
- Why the generated config's paths land under the module's own per-instance directory
prerequisites:
- |
**Nix** with flakes enabled (see [nixos.org](https://nixos.org/download.html)).
- "**A Linux or macOS machine.** Nothing here needs a display or a network peer."
sections:
- title: "Build the Logos daemon"
step: true
text: |
Build the Logos runtime CLI from its published flake. The result outputs a
binary named `logoscore` under a symlinked directory named `./logos`.
`logoscore` is the headless frontend for
[`logos-liblogos`](https://github.com/logos-co/logos-liblogos) — it brings in
the whole module-runtime stack we need to load and call the module.
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 so the daemon can
discover them.
steps:
- title: "Build lgpm"
run: "nix build 'github:logos-co/logos-package-manager' --out-link ./lgpm"
code_block: |
nix build 'github:logos-co/logos-package-manager' --out-link ./lgpm
check_file: "lgpm/bin/lgpm"
- title: "Build and install the blockchain module"
step: true
text: |
The module's flake exposes an `.lgx` package — the Logos distribution format.
Building it compiles the Qt plugin *and* the bundled `logos_blockchain`
native library it links against, so this step is the slow one.
steps:
- title: "Build the .lgx package"
run: "nix build 'github:logos-blockchain/logos-blockchain-module{release}#lgx' --out-link ./blockchain-lgx"
code_block: |
nix build 'github:logos-blockchain/logos-blockchain-module{release}#lgx' --out-link ./blockchain-lgx
- title: "Install it into a modules directory"
text: "Install the package so the daemon can find it:"
run: "./lgpm/bin/lgpm --modules-dir ./modules install --dir ./blockchain-lgx"
code_block: |
lgpm --modules-dir ./modules install --dir ./blockchain-lgx
expect_contains:
- "blockchain_module"
check_file: "modules/blockchain_module/manifest.json"
- title: "Load the module in the daemon"
step: true
text: |
Start the daemon pointed at the modules directory, then load
`blockchain_module`. Loading only brings the plugin into the process — it
does **not** start a blockchain node.
steps:
- title: "Start the daemon"
run: "./logos/bin/logoscore stop >/dev/null 2>&1; sleep 2; ./logos/bin/logoscore daemon --modules-dir ./modules --persistence-path ./data >/dev/null 2>&1 &"
code_block: |
logoscore daemon --modules-dir ./modules --persistence-path ./data
- title: "Load the module"
run: "sleep 8; ./logos/bin/logoscore load-module blockchain_module"
code_block: |
logoscore load-module blockchain_module
expect_contains:
- "blockchain_module"
- title: "Inspect the methods it exposes"
text: |
`module-info` lists the `Q_INVOKABLE` methods — the same names you can
`call`. Note `generate_user_config`, `start`, `stop`, and the
`wallet_*` family:
run: "./logos/bin/logoscore module-info blockchain_module"
code_block: |
logoscore module-info blockchain_module
expect_contains:
- "generate_user_config"
- "start"
- "stop"
- title: "Generate a config with bootstrap peers"
step: true
text: |
`generate_user_config` takes a JSON object of arguments. Every field is
optional — an empty object produces a working default config. Here we pass
the two that matter most for joining a network: the bootstrap peers to sync
from, and the ports to listen on.
`use_persistence_paths` routes the config — and the node's `state`, `db`
and `logs` directories — under the module's own per-instance directory,
which is always writable. That is the safe default.
steps:
- title: "Write the arguments"
file:
path: gen-args.json
content: |
{
"initial_peers": [
"/ip4/65.109.51.37/udp/3000/quic-v1/p2p/12D3KooWFrouXfmrR4nsLMtE7wu15DoMJ6VtoUtHinREZCvbWHar"
],
"net_port": 3000,
"blend_port": 3001,
"output": "user_config.yaml",
"use_persistence_paths": true
}
- title: "Generate the config"
run: "./logos/bin/logoscore call blockchain_module generate_user_config @gen-args.json"
code_block: |
logoscore call blockchain_module generate_user_config @gen-args.json
expect_contains:
- '"success":true'
- "user_config.yaml"
post_text: |
The call returns the absolute path of the file it wrote — that path is
what you hand to `start`, as the runtime doc-test does.
`output` is worth passing even though it is optional. Omit it and the
call still reports success but returns an **empty** path, leaving you
nothing to start the node with.
- title: "Generating a second config into the same instance"
step: true
text: |
A natural next move is to generate a *second* config — say a peerless one
with `skip_ibd: true` — alongside the first. That does **not** work, and the
error is worth seeing because it is the same one operators hit when they
re-run generation over an existing setup.
The keystore is created once per instance directory. A second
`generate_user_config` against the same instance refuses rather than
overwriting it:
steps:
- title: "Write the arguments"
file:
path: gen-args-solo.json
content: |
{
"skip_ibd": true,
"net_port": 3100,
"blend_port": 3101,
"output": "solo-user-config.yaml",
"use_persistence_paths": true
}
- title: "Try to generate a second config"
run: "./logos/bin/logoscore call blockchain_module generate_user_config @gen-args-solo.json"
code_block: |
logoscore call blockchain_module generate_user_config @gen-args-solo.json
expect_contains:
- "Keystore file exists. Use `update` command."
post_text: |
Refusing is the right behaviour — silently regenerating a keystore would
discard the keys the previous config's node identity depends on. To
change an existing setup use `update_user_config`; to get a genuinely
separate configuration, use a separate instance directory.
This is also why `skip_ibd` is demonstrated in the companion
[`blockchain-module-runtime`](./blockchain-module-runtime.md) doc-test
rather than here: that spec generates exactly one config, with
`skip_ibd: true`, and then starts a node from it.
- title: "Shut down"
step: true
text: "Stop the daemon. No node was ever started, so there is nothing else to clean up."
steps:
- title: "Stop the daemon"
run: "./logos/bin/logoscore stop"
code_block: |
logoscore stop