# Amy Roadmap The live checklist for growing `amy` from a Marmot test-bed into a full command-line mirror of Amethyst. **How to use this file:** update it in the same PR that ships a feature. Move rows between tables, adjust ordering, add non-goals. This is the single source of truth for "what's left". - What amy is + how to use it: [README.md](./README.md) - The public contract + how to extend amy: [DEVELOPMENT.md](./DEVELOPMENT.md) - Ongoing design plans: [plans/](./plans/) - Shared work consumed here: [../commons/plans/](../commons/plans/) --- ## North-star goal > For every feature of the Amethyst Android app, there is a way to > exercise it through `amy`, with byte-identical on-relay behaviour. Why: - Interop tests against the ~100 other Nostr clients need a reproducible harness that does not require running Android. - Agents and LLMs can script real Amethyst flows without a GUI. - Regressions in shared logic (signing, encryption, filter building, event parsing) become shell-scriptable. - Power users get a command-line Amethyst for free. **Non-goal:** Amy is not a second Nostr client implementation. It is a thin assembly layer over `quartz` + `commons`. Protocol and business logic in `cli/` are bugs. --- ## Parity matrix Status legend: โœ… shipped ยท ๐Ÿ“ฆ logic lives in `commons/`, needs a command ยท ๐Ÿ†• needs extraction from `amethyst/` first ยท โš ๏ธ blocked (see Notes). | Area | Status | Notes | |---|---|---| | Identity create / import (`nsec`, `ncryptsec`, mnemonic, `npub`, `nprofile`, hex, NIP-05) | โœ… | `LoginCommand` + Quartz NIP-05 / NIP-06 / NIP-49 | | Account bootstrap (nine events) | โœ… | `commons/account/AccountBootstrapEvents.kt` | | Relay config + NIP-65 / NIP-10050 publish | โœ… | `RelayCommands` | | MLS KeyPackage publish + fetch | โœ… | `commons/marmot/MarmotManager` | | Marmot group create / add / rename / promote / demote / remove / leave | โœ… | `commons/marmot/` | | Marmot message send / list | โœ… | `commons/marmot/` | | `await` polling (KP / group / member / admin / message / rename / epoch) | โœ… | `AwaitCommands` | | NIP-01 note publish (`amy notes post TEXT`) | โœ… | `PostCommand` โ€” outbox via `RelayCommands` configured set. | | NIP-01 feed read (`amy notes feed [--following \| --author NPUB]`) | โœ… | `FeedCommand`. Hashtag / community feeds still pending. | | NIP-02 follow list add / remove / list | ๐Ÿ†• | Logic in `amethyst/model/nip02FollowLists/`. | | NIP-09 event deletion | ๐Ÿ†• | Builder exists in quartz. | | NIP-17 DMs send / list / await | โœ… | `DmCommands` โ€” reuses Quartz `NIP17Factory` + `RecipientRelayFetcher`; filter extracted to `commons/relayClient/nip17Dm/`. Plan: [`cli/plans/2026-04-23-nip17-dm.md`](./plans/2026-04-23-nip17-dm.md). | | NIP-18 reposts / quotes | ๐Ÿ†• | | | NIP-25 reactions | โœ… in groups ยท ๐Ÿ†• elsewhere | `marmot message react` covers MLS group reactions; outer-event reactions still pending. | | NIP-51 lists (bookmarks, mute, follow sets) | ๐Ÿ†• | `amethyst/model/nip51Lists/` | | NIP-57 zaps (send + verify) | ๐Ÿ†• | Needs LN-URL plumbing; `amethyst/service/lnurl/`. | | NIP-65 outbox model queries | ๐Ÿ†• | | | NIP-72 communities | ๐Ÿ†• | | | NIP-78 app-specific data (settings sync) | ๐Ÿ†• | | | Long-form (NIP-23) publish / read | ๐Ÿ†• | | | Live activities / chess (NIP-53 / NIP-64) | ๐Ÿ†• | | | Blossom uploads (NIP-B7) | ๐Ÿ†• | | | NIP-47 Wallet Connect | ๐Ÿ†• | | | NIP-46 bunker signer | ๐Ÿ†• | Needs a signers abstraction in Amy. | | Profile view (`amy profile show NPUB`) + edit | โœ… | `ProfileCommands`. Cache-first; `--refresh` forces a relay drain. | | Thread view (`amy thread show EVENT_ID`) | โš ๏ธ | Same. | | Notifications feed | ๐Ÿ†• | | | Search (NIP-50) | ๐Ÿ†• | | ### `nak` parity โ€” army-knife primitives Tracking [`fiatjaf/nak`](https://github.com/fiatjaf/nak)'s command surface. amy adapts the verbiage where its own conventions differ (`req` โ†’ one-shot `fetch` vs streaming `subscribe`). Stateless verbs run with no account or network. | nak command | amy verb | Status | Notes | |---|---|---|---| | `decode` | `amy decode` | โœ… | NIP-19/21 โ†’ JSON. Quartz `Nip19Parser`. | | `encode` | `amy encode` | โœ… | npub/nsec/note/nevent/nprofile/naddr. | | `verify` / `validate` | `amy verify` | โœ… | id-hash + signature, reported separately. | | `key` | `amy key generate\|public` | โœ… in part ยท ๐Ÿ†• | generate + derive done; NIP-49 encrypt/decrypt pending. | | `event` | `amy event` | โœ… | build/sign an arbitrary event, optional `--publish`/`--relay`. | | `publish` | `amy publish` | โœ… | broadcast a pre-made event JSON (verified first). | | `req` (one-shot) | `amy fetch` | โœ… | filter โ†’ collect-until-EOSE, dedupe, sort, cap. | | `req` (stream) | `amy subscribe` | โœ… | filter โ†’ live NDJSON stream to stdout. | | `count` | `amy count` | โœ… | NIP-45, per-relay counts. | | `encrypt` / `decrypt` | `amy encrypt\|decrypt` | โœ… | raw NIP-44 (default) / NIP-04. | | `gift` | `amy gift wrap\|unwrap` | โœ… | NIP-59 seal+wrap / unwrap+unseal. | | `relay` (NIP-11) | `amy relay info` | โœ… | stateless NIP-11 doc fetch. | | `outbox` | `amy outbox` | โœ… | NIP-65 read/write relays, cache-first. | | `filter` | `amy filter` | โœ… | stateless โ€” assemble + print a filter JSON. | | `blossom` | `amy blossom` | โœ… | upload/download/list/delete (reuses commons `BlossomClient`). | | `kind` / `nip` | `amy kind` / `amy nip` | ๐Ÿ†• | reference lookups (needs a kind registry โ€” only remaining Tier-1 gap). | | `sync` | `amy sync` | โœ… | NIP-77 Negentropy reconcile with the local store (down/up/both). | | `git` | `amy git` | โœ… in part | NIP-34 repo announce/list/show/issue. clone/push (packfile transport) out of scope. | | `podcast` | `amy podcast` | โœ… | NIP-F4 show metadata (10154) + episode publish (54) + list. | | `bunker` / `serve` / `admin` / `wallet` / `mcp` / `fs` / `spell` | โ€” | ๐Ÿ†• (tier 2/3) | larger/niche; some pull new deps. | **Tier 1 status:** shipped โ€” `decode`, `encode`, `verify`, `key`, `event`, `publish`, `fetch`, `subscribe`, `count`, `encrypt`, `decrypt`, `gift`, `filter`, `relay info`, `outbox`, `blossom`. Remaining: `kind` / `nip` (reference lookups, pending a kind registry). --- ## Order of operations Proposed sequencing. Each step is one PR. Each step should extract at least one file from `amethyst/` into `commons/`; if it doesn't move anything, re-audit โ€” you're probably duplicating logic. 1. **Event rendering core** in `commons/commonMain/.../rendering/` with renderers for kinds 0 / 1 / 3 / 6 / 7 / 10002 / 10050. Unblocks all the ๐Ÿ†• and โš ๏ธ read-path rows below. Design: `commons/plans/2026-04-21-event-renderer.md`. 2. **`amy notes post` / `amy notes show` / `amy notes react`** โ€” smallest end-to-end write+read loop outside Marmot. Post + feed โœ… shipped; `notes show` and outer-event `react` still pending. 3. **`amy notes feed home|profile|hashtag|thread`** reading through the renderer. `--following` and `--author NPUB` โœ…; hashtag/thread variants still pending. 4. **`amy follow add|remove|list`** (NIP-02) โ€” proves extraction of list-building logic from `amethyst/model/`. 5. **`amy dm send|list`** (NIP-17) โ€” โœ… shipped. Reuses the gift-wrap path also exercised by Marmot. 6. **`amy list bookmarks|mute|pin โ€ฆ`** (NIP-51). 7. **`amy zap send|verify`** (NIP-57). 8. **Distribution** โ€” Homebrew + Scoop + `.deb` in the same release pipeline as desktop. Plan: `cli/plans/2026-04-21-cli-distribution.md`. 9. **Test suite** โ€” end-to-end against a local relay. Marmot interop is covered by `cli/tests/marmot/marmot-interop-headless.sh`; NIP-17 DM interop between two `amy` clients is covered by `cli/tests/dm/dm-interop-headless.sh` (text + file + strict 10050 + fallback + cursor-advance). Neither runs in CI yet (both need Rust + ~3 min cold relay build). 10. **Everything else in the matrix.** --- ## Non-goals - Interactive TUI or REPL. - Native image (GraalVM) until Quartz has a pure-Kotlin signer fallback โ€” `secp256k1-kmp-jni-*` needs JNI today. - A Gradle dependency on `:amethyst` or `:desktopApp`. Ever. - Re-implementing any Nostr protocol piece that's already in `quartz/` or in another client's library.