Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Writing a wrapper

A wrapper turns “run a process, read stdout” into function calls. The contract makes this simple if the wrapper follows a few rules. Complete, tested wrappers are in wrappers/: Python, TypeScript, Rust, Shell.

The algorithm

1. argv = [binary, "--output", "json", ...command path, ...options]   # array, no shell
   + "--yes" for writes, only with the user's consent
2. spawn with stdin closed, a timeout (≈45 s), environment with MOSTRO_PUBKEY/RELAYS
3. if spawn failed                     → TransportError(may_have_acted = false)
   if timeout                          → TransportError(may_have_acted = is_write)
4. stdout.strip() empty                → TransportError(may_have_acted = is_write and exit == 1)
   not one JSON object                 → TransportError(may_have_acted = is_write)
5. envelope.schema_version != "1"      → IncompatibleError
6. return envelope (+ exit code)       → caller branches on status, then error.code

Rules

  1. Build argv as a list. Never join a command string for a shell; values such as payment methods and invoices are user data.
  2. One --output json, placed first. Never let user data land where an option is expected. For a value that could start with -, use the attached form --payment-method=<value>: the CLI never reads an output selection inside a value.
  3. Close stdin. The CLI does not read it in JSON mode; a closed stdin makes sure a future version can never block on a prompt.
  4. Parse stdout, ignore stderr (log it, do not parse it).
  5. Branch on status, then error.code. Never on message. Keep a default branch for codes you do not know (within v1 new commands can add codes).
  6. Keep decimal strings as strings or parse them as big integers.
  7. Never retry uncertain or a write transport failure automatically. See Recovering.
  8. Serialise writes per data directory.
  9. Discover once: call schema at start-up; check every command you need is present with "json" in output_modes.
  10. Validate optionally: the per-command output schemas are strict; validating in tests (or in debug builds) catches drift between your wrapper and a new binary.

Surfacing results to a UI

EnvelopeShow
okThe data; warnings as notes
error + node_refused“The node refused: <reason>”
error (other)A sentence per code (your own text; the CLI’s message is acceptable as a fallback)
uncertain“We could not confirm whether this happened. Checking…” then run the check

Testing a wrapper without a node

Many outcomes can be produced offline:

OutcomeHow
invalid_arguments (exit 2)order list --side x
command: nullan unknown command
output_not_supportedany human-only command
configuration_missingunset MOSTRO_PUBKEY/RELAYS
configuration_invalid--mostro-pubkey not-a-key
identity_missingMCLI_DIR pointing to an empty directory, valid config
relay_connection_failed--relays ws://127.0.0.1:1 (with an identity, for identity commands)
schema successschema

For the rest, feed your parser the files in examples/ (all 120 are real-shaped envelopes; examples/index.json gives the exit code to pair with each).