From 7a3c35c645f2ada0d11387a246724c860741a5b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 02:14:11 +0000 Subject: [PATCH] docs: document cashu, admin, serve, fetch code mode, key validate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the amy docs to reflect the new command surface: - cli/README.md — add the Cashu (NIP-60/61), Relay management (NIP-86 admin), and Run-a-relay (serve) sections; document fetch's nip19/nip05 code mode and key validate. - cli/DEVELOPMENT.md — add cashu.json to the on-disk layout and pin the command-family --json contracts (cashu keys/error codes + pointer to the cashu plan, admin {relay,method,result}, serve startup object). - .claude/skills/amy-expert/SKILL.md — extend the "where things live" tree with AdminCommand/ServeCommand/cashu/, the commons/cashu + relayManagement shared modules, and the allowed :geode dependency. - .claude/CLAUDE.md — note cli may depend on :geode (for serve), never on :amethyst/:desktopApp. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_011SapGdtAc1j7woifoCZ9fY --- .claude/CLAUDE.md | 4 ++- .claude/skills/amy-expert/SKILL.md | 17 +++++++++- cli/DEVELOPMENT.md | 17 ++++++++++ cli/README.md | 51 ++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index c1ba392b9d..3ee4d735e9 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -76,7 +76,9 @@ amethyst/ - `nestsClient/` = MoQ + audio-rooms client; takes `:quic` as transport, Quartz for crypto, `MediaCodec` / `AudioRecord` / `AudioTrack` for audio. - `amethyst/` & `desktopApp/` = Platform-native layouts and navigation -- `cli/` = Thin assembly layer over `quartz/` + `commons/` (no new logic allowed) +- `cli/` = Thin assembly layer over `quartz/` + `commons/` (no new logic + allowed). May also depend on `:geode` (for `amy serve`, which embeds the + standalone relay); never on `:amethyst` or `:desktopApp`. **Plans per module:** design docs for new subsystems live in the owning module's `plans/YYYY-MM-DD-.md` (e.g. `cli/plans/`, `commons/plans/`). diff --git a/.claude/skills/amy-expert/SKILL.md b/.claude/skills/amy-expert/SKILL.md index f6cbce8416..b7eaa489f6 100644 --- a/.claude/skills/amy-expert/SKILL.md +++ b/.claude/skills/amy-expert/SKILL.md @@ -189,15 +189,30 @@ cli/ ├── MessageCommands.kt ├── MarmotResetCommand.kt ├── AwaitCommands.kt - └── StoreCommands.kt + ├── StoreCommands.kt + ├── AdminCommand.kt # `amy admin RELAY METHOD` (NIP-86) + ├── ServeCommand.kt # `amy serve` (embeds :geode) + └── cashu/ # `amy cashu …` (NIP-60/61) — thin wrappers + ├── CashuCommands.kt # over commons CashuWalletOps / CashuWalletReader + ├── CashuWalletCommands.kt + CashuBalanceCommand.kt + CashuMintCommands.kt + └── CashuReceiveCommands.kt + CashuSendCommands.kt + + CashuMaintenanceCommands.kt + CashuMintRecCommands.kt ``` Shared logic consumed by Amy lives in `commons/`: - `commons/account/` — account bootstrap - `commons/marmot/` — MLS / group state +- `commons/cashu/` — `ops/CashuWalletOps` (jvmAndroid) + `CashuWalletReader` + + `CashuKeysetCounterStore`; the NIP-60/61 wallet, shared with Android. +- `commons/relayManagement/Nip86Retriever` — NIP-86 HTTP client, shared with + the Android relay-management screen. - `commons/defaults/` — default relays, kinds - Consult `commons/plans/` for cross-cutting design work in flight. +A few amy verbs lean on modules beyond `quartz`/`commons`: `amy serve` +depends on `:geode` (the standalone relay) — the one allowed extra module +dependency. `:amethyst` / `:desktopApp` remain forbidden (Rule 5). + ## Common mistakes to refuse - **Adding protocol logic to `cli/`.** Push back, offer to extract. diff --git a/cli/DEVELOPMENT.md b/cli/DEVELOPMENT.md index b9f37b73b8..6b9eb42376 100644 --- a/cli/DEVELOPMENT.md +++ b/cli/DEVELOPMENT.md @@ -227,6 +227,22 @@ contract. - Errors via `Output.error("code","detail")` — single lower_snake code, free-form detail. +**Command-family schemas:** + +- **Cashu** (`amy cashu …`, NIP-60/61): the full per-verb `--json` key table + and the `cashu.json` NUT-13 counter layout are pinned in + [`plans/2026-05-28-cashu-cli.md`](./plans/2026-05-28-cashu-cli.md). Common + keys: `wallet_event_id`, `mint_url`, `amount_sats` (Long), `proofs_count`, + `token_event_id`, `history_event_id`, `quote_id`, `p2pk_pubkey`. Error codes + include `no_wallet`, `no_mint`, `insufficient_funds`, `mint_unreachable`, + `mint_http_`, `mint_proofs_spent`, `mint_quote_gone`. +- **Admin** (`amy admin …`, NIP-86): `{relay, method, result}` where `result` + is the relay's raw JSON-RPC result (list/boolean/null). Relay-side failures + surface as `error: relay_error`. +- **Serve** (`amy serve`): a single startup object `{listening, host, port, + path, persistent, admin_pubkeys[]}`, then the process blocks (it embeds + geode; teardown is on SIGINT). + --- ## Testing @@ -360,6 +376,7 @@ events. │ ├── identity.json # nsec/npub/hex — the account │ ├── state.json # sync cursors (giftWrapSince, groupSince) │ ├── aliases.json # local name → npub map (init writes a self-entry) +│ ├── cashu.json # NIP-60 NUT-13 counters: {"keyset_counters":{"":}} │ └── marmot/ │ ├── keypackages.bundle # MLS KeyPackage bundles (NostrSignerInternal) │ └── groups/ diff --git a/cli/README.md b/cli/README.md index bfef61f701..548b1b94c7 100644 --- a/cli/README.md +++ b/cli/README.md @@ -215,6 +215,7 @@ Army-knife verbs that operate purely on their arguments. They never touch | `amy key public NSEC\|HEX` | Derive the public key from a secret key. | | `amy key encrypt NSEC\|HEX --password X` | NIP-49 encrypt a secret key to an `ncryptsec1…`. | | `amy key decrypt NCRYPTSEC --password X` | NIP-49 decrypt back to nsec/hex/npub. | +| `amy key validate NPUB\|HEX` | Parse-check a public key. Prints `{valid, pubkey, npub}` or `{valid:false}` — never errors, so scripts branch on the field. | | `amy filter [filter flags]` | Assemble and print a NIP-01 filter JSON from the same flags `fetch`/`subscribe` use — no query is sent. | | `amy nip N` / `amy nip list` | Look up a NIP — the `nostr-protocol/nips` repo first, then a Nostr wiki/long-form fallback. `list` fetches the index. | | `amy kind N` / `amy kind NAME` | Look up an event kind's label + defining NIP (number), or search labels by name. Backed by quartz's `KindNames` registry. | @@ -264,6 +265,7 @@ Filter flags are shared by `fetch` and `subscribe`: `--kind K[,K]`, `--author U[ | Command | What it does | |---|---| | `amy fetch [filter flags] [--timeout SECS]` | One-shot query — collect until every relay sends EOSE (or `--timeout`, default 8s), dedupe, sort newest-first, print and exit. `--limit` defaults to 100. | +| `amy fetch CODE [--timeout SECS]` | Code mode — pass a single `nevent`/`naddr`/`nprofile`/`npub`/`note` or `name@domain`. Resolves relays the outbox way: the hints embedded in the code **plus** the author's NIP-65 write relays (draining their kind:10002 on a cache miss), exactly how the app opens a shared link. | | `amy subscribe [filter flags] [--timeout SECS]` | Live stream — print each matching event as it arrives (NDJSON under `--json`). Runs until `--timeout` SECS or until interrupted. | | `amy count [filter flags] [--timeout SECS]` | NIP-45 COUNT — per-relay match counts, no event download. | | `amy outbox USER [--refresh] [--timeout SECS]` | Show USER's NIP-65 read/write relays (outbox model). Cache-first; `--refresh` forces a relay drain. | @@ -308,6 +310,55 @@ nak's `clone`/`push`/`pull` (git-packfile transport over relays/GRASP) are out o | `amy blossom check --server URL HASH[,HASH]` | HEAD-check the server has each blob; exit 1 if any is missing. | | `amy blossom mirror --server URL SOURCE-URL` | Ask the server to mirror a blob from SOURCE-URL (BUD-04). | +### Cashu wallet (NIP-60 / NIP-61) + +A NIP-60 ecash wallet + NIP-61 nutzaps, driven by the **same** shared +`commons` `CashuWalletOps` / `CashuWalletReader` the Android wallet runs — so +amy's on-relay events match the app's. NUT-13 counters persist in +`~/.amy//cashu.json`. `mint ping`/`info` are stateless (no account). + +| Command | What it does | +|---|---| +| `amy cashu wallet create [--mint URL] [--mints a,b] [--privkey HEX] [--relay r1,r2]` | Publish a kind:17375 wallet + kind:10019 nutzap info. Advertises your outbox relays for nutzaps unless `--relay` overrides. | +| `amy cashu wallet show` | P2PK pubkey, mints, balance, per-mint balances, proof/history/pending counts. | +| `amy cashu wallet export-key` | Decrypt and print the wallet's P2PK private key. | +| `amy cashu wallet destroy` | Withdraw the nutzap advertisement and NIP-09 delete the wallet (leaves token events — the ecash still lives at the mint). | +| `amy cashu balance [--mint URL]` | Spendable balance from the local store (optionally one mint). | +| `amy cashu mint ping URL` / `info URL` | Stateless `/v1/info` probe (name/pubkey/version) / full DTO. | +| `amy cashu receive ln SATS [--mint URL]` | Request a mint quote; prints the bolt11 + kind:7374 quote. | +| `amy cashu receive complete QUOTE_ID` / `resume QUOTE_ID` | Poll the quote; once the invoice is settled, mint proofs (kind:7375 + kind:7376). | +| `amy cashu receive token TOKEN` | Redeem a `cashuB…` token into the wallet. | +| `amy cashu receive nutzap-sweep [--mint URL]` | Redeem inbound NIP-61 nutzaps locked to your wallet key. | +| `amy cashu send ln INVOICE [--mint URL]` | Melt proofs to pay a bolt11 (scrubs stale proofs first). | +| `amy cashu send token SATS [--mint URL] [--memo S]` | Export a `cashuB…` token of SATS. | +| `amy cashu send nutzap USER SATS [--zapped EVENT_ID] [--message S]` | Send a P2PK-locked nutzap to USER (resolves their kind:10019). | +| `amy cashu maintenance scrub [--mint URL]` | NUT-07 + NIP-09 prune of spent proofs. | +| `amy cashu maintenance restore MINT_URL` | NUT-09 restore unspent proofs from the wallet seed. | +| `amy cashu maintenance migrate-keysets [--mint URL]` | Consolidate proofs onto each mint's active keyset. | +| `amy cashu mint-rec show [--author NPUB]` / `add URL [--dtag X] [--review T]` / `remove EVENT_ID` | NIP-87 mint recommendations (kind:38000). | + +### Relay management — admin (NIP-86) + +Signs a NIP-98 request with the active account and POSTs it to the relay's +HTTP endpoint. Reuses quartz's `Nip86Client` and the shared `Nip86Retriever` +(the same path Amethyst's relay-management screen runs). + +| Command | What it does | +|---|---| +| `amy admin RELAY supported-methods` | List the NIP-86 methods the relay implements. | +| `amy admin RELAY ban-pubkey HEX [--reason R]` / `unban-pubkey HEX` / `list-banned-pubkeys` | Pubkey ban list. | +| `amy admin RELAY allow-pubkey HEX [--reason R]` / `unallow-pubkey HEX` / `list-allowed-pubkeys` | Pubkey allow list. | +| `amy admin RELAY ban-event ID [--reason R]` / `allow-event ID` / `list-banned-events` / `list-needing-moderation` | Event moderation. | +| `amy admin RELAY allow-kind N` / `disallow-kind N` / `list-allowed-kinds` | Kind allow list. | +| `amy admin RELAY block-ip IP [--reason R]` / `unblock-ip IP` / `list-blocked-ips` | IP block list. | +| `amy admin RELAY change-name S` / `change-description S` / `change-icon URL` | Relay metadata. | + +### Run a relay — serve + +| Command | What it does | +|---|---| +| `amy serve [--host H] [--port N] [--path P] [--db FILE] [--admin NPUBS]` | Run a Nostr relay by embedding **geode** (the standalone Ktor relay on quartz's relay-server code). In-memory by default; `--db FILE` for SQLite. The active account is always an admin, so `amy admin ws://host:port …` works against it. Blocks until interrupted. | + ### Identity | Command | What it does |