CLI (lcpdctl) — protoc-gen-cobra client for go-lcpd
This document explains how to use:lcpdctl: a generated CLI client for thego-lcpdgRPC API (lcpd.v1.LCPDService)lcpd-oneshot: a small helper that runsRequestQuoteand (optionally)AcceptAndExecute
Prerequisites
- Go 1.24.4+
jq(optional, used only for pretty-printing JSON in some examples)
Install the CLI tools (recommended)
Install binaries intogo-lcpd/bin/ (no Nix required):
./bin/lcpd-grpcd./bin/lcpdctl./bin/lcpd-oneshot
Security & operational constraints
lcpd-grpcdserves plaintext gRPC (no TLS, no auth). Bind to127.0.0.1or protect it via SSH/VPN/reverse-proxy if you must access it remotely.AcceptAndExecutepays a BOLT11 invoice via yourlnd. On mainnet this spends real funds. Start with regtest and use small amounts.- LCP messaging requires an active Lightning peer connection to the Provider, and the Provider must support LCP (manifest observed).
- The only supported task type is
openai.chat_completions.v1(raw OpenAI-compatible request/response JSON bytes passthrough). - Quote execution is time-bounded by
terms.quoteExpiry; calls after expiry are expected to fail. - LCP peer messaging enforces payload/stream limits (
max_payload_bytes,max_stream_bytes,max_job_bytesin the manifest).go-lcpddefaults to16384bytes payload,4 MiBstream,8 MiBjob. job_idCLI flags are base64 (protobytes), matching protojson encoding.lcpdctl --timeoutis a dial timeout (not an RPC deadline). Cancel long-running RPCs with Ctrl-C, or uselcpd-oneshot -timeout ...for an overall deadline.
- Some older docs used step names like
CreateQuote → Execute → VerifyReceipt. Ingo-lcpdtoday, the public gRPC flow isRequestQuote → AcceptAndExecute, and invoice binding is verified insideAcceptAndExecute.
Run (gRPC daemon)
Start the gRPC daemon in another terminal.Requester mode (recommended default)
Requester mode needslnd connectivity for peer messaging and invoice payments:
Provider mode (optional)
Provider mode requires:LCPD_PROVIDER_CONFIG_PATH(YAML)LCPD_BACKEND(e.g.openai)LCPD_LND_*(because the Provider also uses peer messaging)
docs/configuration.md.
lnd disabled (gRPC only)
ListLCPPeers, RequestQuote, AcceptAndExecute) require lnd.
One-shot CLI (lcpd-oneshot)
lcpd-oneshot is a small wrapper over the gRPC API:
- always runs
RequestQuote - runs
AcceptAndExecuteonly when-pay-invoice=true
server-addr=127.0.0.1:50051model=gpt-5.2timeout=30s
lcpd-oneshotuses insecure gRPC (no TLS). Use it with a localhost-boundlcpd-grpcd.peer-idis required (66-hex compressed pubkey).
--prompt → positional args (space-joined) → stdin.
Example (text output):
--json if you want JSON output.
Note on pricing output:
- The tool prints both
price_msatand a convenienceprice_sat(decimal string, milli-sat precision).
Interactive chat mode (-chat)
-chat runs a simple interactive REPL that keeps conversation context by embedding the full transcript into each new prompt.
Each turn prints the invoice amount and a running total:
paid=<sat> sat total=<sat> sat
-chatrequires-pay-invoice=true(otherwise you can’t see model responses).- Type
/exit(or Ctrl-D) to quit. - When stdin/stdout are terminals,
lcpd-oneshotuses a small Bubble Tea TUI for readability; otherwise it falls back to the plain REPL. - Chat history may be truncated to fit
-max-prompt-bytes(default:12000). Set-max-prompt-bytes=0to disable trimming (may hit payload limits). - Optional:
-system-prompt "..."to prefix the conversation with a System instruction.
lnd integration (custom messages / lcp_manifest exchange)
When configured, go-lcpd connects to lnd over gRPC and exchanges lcp_manifest over BOLT #1 custom messages.
This enables automatic discovery of LCP-capable peers on top of an existing Lightning peer connection.
You can list them via ListLCPPeers.
Required configuration (choose one)
A) Environment variables (recommended)
- On regtest with
lnd --no-macaroons, a macaroon is not required (you can leave it unset). - If
LCPD_LND_RPC_ADDRis unset, lnd integration is disabled. - If
LCPD_LND_TLS_CERT_PATHis unset (or empty), TLS verification uses system roots. This works when lnd presents a publicly trusted certificate. For self-signed certs, setLCPD_LND_TLS_CERT_PATH. - Use
LCPD_LOG_LEVEL=debugfor more verbose logs aroundListLCPPeersand related flows.
B) lcpd-grpcd flags
If you prefer not to use env vars, you can pass these flags to lcpd-grpcd:
Smoke check (ListLCPPeers)
- Connect to the remote as an
lndpeer (e.g.,lncli connect <pubkey>@<host:port>) - Run
go-lcpdon both sides (each connected to its ownlnd) - Call
ListLCPPeers
- Right after lnd starts / wallet unlock,
SubscribePeerEvents/SubscribeCustomMessagesmay take time to stabilize. If needed, increase the timeout via-startup_timeout=60s(for example).
Quickstart (RequestQuote → AcceptAndExecute)
This section shows the full flow using onlylcpdctl lcpd ... (quote → pay → stream(result) → lcp_result).
- The examples do not write files (stdout only).
- No Python is needed (we use
jqto format JSON). jqis optional (used only for pretty-printing).
end-to-end (RequestQuote → AcceptAndExecute)
This flow assumes:lcpd-grpcdis configured withlnd(LCPD_LND_*)- the Provider is connected as an
lndpeer
1) RequestQuote (get terms + invoice)
2) AcceptAndExecute (pay invoice and wait for result)
jobId is base64 in protojson (because job_id is bytes).
AcceptAndExecuteResponse.result.result is base64-encoded bytes in JSON output.
To decode and pretty-print the raw OpenAI response JSON: