Files
n_signer/client/n_signer_client_README.md

172 lines
7.0 KiB
Markdown

# `nsigner_client` — Linux CLI for n_signer
A standalone Linux command-line client that connects to a running [`n_signer`](https://github.com/your-org/n_signer) process and calls its JSON-RPC verbs over stdin/stdout. Designed for pipe-to-`nak` workflows.
## Build
```bash
make clients
```
Produces `build/nsigner_client`. Links `nostr_core_lib` exactly like the existing examples.
## Usage
```
nsigner_client [global options] <verb> [verb args...]
```
### Global options
| Flag | Default | Meaning |
|------|---------|---------|
| `--socket-name`, `-n <name>` | auto-discover | Abstract socket name without `@` |
| `--timeout <ms>` | `5000` | Transport timeout |
| `--tcp <host:port>` | none | TCP transport (requires `--auth-privkey`) |
| `--serial <device>` | none | USB CDC-ACM serial transport |
| `--qrexec <qube:service>` | none | Qubes qrexec transport |
| `--auth-privkey <32-byte hex>` | none | Auth envelope privkey for TCP |
| `--auth-label <text>` | none | Auth envelope label |
### Selector options (for `nostr_*` verbs)
| Flag | Meaning | JSON emitted |
|------|---------|-------------|
| `--role <name>` | Named path-role registered in the signer | `{"role":"<name>"}` |
| `--path <path>` | Full BIP-44 derivation path | `{"role_path":"<path>"}` |
### Algorithm options (for algorithm-based verbs)
| Flag | Default | Meaning |
|------|---------|---------|
| `--algorithm <alg>` | none | `secp256k1`/`ed25519`/`x25519`/`ml-dsa-65`/`slh-dsa-128s`/`ml-kem-768`/`otp` |
| `--index <N>` | `0` | Algorithm derivation index |
| `--scheme <schnork\|ecdsa>` | `schnorr` | secp256k1 `sign`/`verify` only |
| `--encoding <base64\|hex>` | `base64` | OTP `encrypt`/`decrypt` only |
| `--format <plain\|structured>` | `plain` | `nostr_get_public_key` output shape |
### Mine-event options
| Flag | Meaning |
|------|---------|
| `--difficulty <N>` | Target leading zero bits |
| `--threads <N>` | Mining threads (default 1) |
| `--timeout-sec <N>` | Mining timeout in seconds |
## Verb reference
### Utility
| Verb | stdout |
|------|--------|
| `list` | Lists running n_signer abstract sockets (one `@name` per line) |
### Metadata
| Verb | RPC method | stdout |
|------|------------|--------|
| `get-info` | `get_info` | raw result JSON (name, version, verbs, algorithms) |
### Nostr verbs (role-based)
| Verb | RPC method | stdin/argv | stdout |
|------|------------|------------|--------|
| `get-public-key` | `nostr_get_public_key` | none | pubkey hex (or structured JSON with `--format structured`) |
| `sign-event` | `nostr_sign_event` | event JSON from argv or stdin | signed event JSON |
| `mine-event` | `nostr_mine_event` | event JSON from argv or stdin | signed mined event JSON |
| `nip04-encrypt <peer>` | `nostr_nip04_encrypt` | plaintext from argv or stdin | ciphertext |
| `nip04-decrypt <peer>` | `nostr_nip04_decrypt` | ciphertext from argv or stdin | plaintext |
| `nip44-encrypt <peer>` | `nostr_nip44_encrypt` | plaintext from argv or stdin | ciphertext |
| `nip44-decrypt <peer>` | `nostr_nip44_decrypt` | ciphertext from argv or stdin | plaintext |
### Algorithm-based verbs
| Verb | RPC method | argv | stdout |
|------|------------|------|--------|
| `get-public-key` | `get_public_key` | none | structured JSON `{"algorithm":...,"public_key":...,"key_id":...}` |
| `sign <msg-hex>` | `sign` | hex bytes | structured JSON `{"signature":...,"algorithm":...,"key_id":...}` |
| `verify <msg-hex> <sig-hex>` | `verify` | hex bytes | `valid` / `invalid` (exit 0/1) |
| `derive <data>` | `derive` | UTF-8 data (argv or stdin) | structured JSON |
| `encapsulate <peer-pubkey-hex>` | `encapsulate` | hex | structured JSON |
| `decapsulate <ciphertext-hex>` | `decapsulate` | hex | structured JSON |
| `derive-shared-secret <peer-pubkey-hex>` | `derive_shared_secret` | hex | shared secret hex |
| `encrypt <plaintext>` | `encrypt` | plaintext (base64 by default) | ciphertext |
| `decrypt <ciphertext>` | `decrypt` | ciphertext | plaintext |
### Generic escape hatch
| Verb | RPC method | input | stdout |
|------|------------|-------|--------|
| `call <method>` | `<method>` | JSON params array from stdin or argv | raw result JSON |
## Selector explanation
The `nostr_*` verbs select a key via the options object using both `--role` and `--path`:
- **`--role <name> --path <path>`** — Both are required for all `nostr_*` verbs. The role authorizes the request and determines the encryption scheme. The path selects the specific key to derive. Sends `{"role":"<name>","role_path":"<path>"}` to the server.
- **`--role` without `--path`** — Client-side error: `--path is required for nostr verbs`.
- **`--path` without `--role`** — Client-side error: `--role is required for nostr verbs`.
For algorithm verbs, `--algorithm` and `--index` populate the options object instead.
## Pipe-to-nak recipes
```bash
# Get public key
nsigner_client --role main --path "m/44'/1237'/0'/0/0" get-public-key
# Sign an event and publish via nak
echo '{"kind":1,"content":"hello nostr","tags":[],"created_at":1700000000}' \
| nsigner_client --role main --path "m/44'/1237'/0'/0/0" sign-event \
| nak publish
# Mine a proof-of-work event
echo '{"kind":1,"content":"pow","tags":[],"created_at":1700000000}' \
| nsigner_client --role main --path "m/44'/1237'/0'/0/0" mine-event --difficulty 20 --threads 4
# NIP-44 encrypt
nsigner_client --role main --path "m/44'/1237'/0'/0/0" nip44-encrypt <peer-pubkey> "secret message"
# Algorithm-based signing
nsigner_client --algorithm ed25519 --index 0 sign 68656c6c6f
# Verify a signature
nsigner_client --algorithm secp256k1 verify <msg-hex> <sig-hex> && echo "valid"
# Get signer info
nsigner_client get-info
```
## Transport options
| Transport | Flag | Notes |
|-----------|------|-------|
| UNIX abstract socket | `--socket-name <name>` or auto-discover | Default. Auto-discovers if exactly one `@nsigner*` socket exists. |
| TCP | `--tcp <host:port>` | Requires `--auth-privkey` for auth envelope. |
| Serial (USB CDC-ACM) | `--serial <device>` | e.g. `--serial /dev/ttyACM0` |
| Qubes qrexec | `--qrexec <qube:service>` | e.g. `--qrexec sys-signer:qubes.NsignerRpc` |
## Exit codes
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Invalid (verify verb only — signature is invalid) |
| 2 | Error (transport, RPC, or usage error) |
For `verify`: exit 0 = valid signature, exit 1 = invalid signature, exit 2 = error.
## stdin/stdout contract
- All payload output goes to stdout as a single line, newline-terminated.
- All diagnostics (errors, warnings) go to stderr.
- `sign-event`, `nip04-*`, `nip44-*`, `derive`, `encrypt`, `decrypt` read their payload from argv if present, otherwise from stdin (one line).
- `sign`, `verify`, `encapsulate`, `decapsulate`, `derive-shared-secret` take hex from argv only (binary payloads).
- `call` reads a JSON params array from stdin (one line) or argv.
## See also
- [`n_signer_client_PLAN.md`](n_signer_client_PLAN.md) — the full implementation plan
- [`README.md`](../README.md) — n_signer main documentation (API §4)
- [`examples/sign_event_client.c`](../examples/sign_event_client.c) — reference example