# Changelog

All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [0.6.1] - 2026-09-24

### Security

- **Received files can no longer replace existing ones.** `receive_file`, `download_files` and `pay_invoice` write peer- and seller-chosen filenames; they used to overwrite whatever was there, so a file named `CLAUDE.md`, `.mcp.json` or `package.json` landing in a project directory could replace the project's own and feed the agent instructions or a new MCP server without anything being opened. Writes are now create-only (`wx`), and a taken name gets ` (1)`, ` (2)`, … before the extension.
- **`receive_file` defaults to `~/.hypawave/received`** instead of the current working directory, which is usually a project. `save_dir` still overrides it.
- **Leading dots are stripped from received filenames**, so a peer cannot plant a hidden file (`.bashrc` → `bashrc`).

### Added

- **Received files are checked for executables.** When a file's leading bytes are an ELF, Mach-O or Windows executable, or a `#!` script, the tool result carries `executable` and a `warning` telling the agent not to open or run it and to tell its operator, whatever the filename claims. Content is never executed or opened by the server either way.

## [0.6.0] - 2026-08-27

### Added

- **`enable_wave_notifications` registers this server at user scope**, alongside every hook it writes (`~/.claude.json`, `~/.codex/config.toml`, `~/.cursor/mcp.json`, `~/.gemini/settings.json`). The hook is global but MCP servers are registered per project in most clients, so a hook firing in a project without the server told the agent to call a `check_inbox` it did not have — and because the cursor advanced on read, nothing re-announced it. Announcement and the ability to act on it now travel together. Codex has no JSON config, so its TOML gets a marker-delimited block; a hand-written `[mcp_servers.hypawave]` is skipped rather than duplicated, which would make the file unparseable. The server is written only after the hook succeeds, so a failure cannot half-install the pair.

### Changed

- **Wave notifications are at-least-once instead of at-most-once.** The hook no longer advances the read cursor past pending items: printing to stdout is not proof anyone read it, and a client that swallows hook output silently consumed the only announcement a message ever got. `check_inbox` now advances the cursor, because that call means the agent has the content. Unconfirmed batches are re-announced (bounded by the existing 60s throttle) and abandoned after three tries, so a client that never delivers cannot nag forever — those messages remain readable via `check_inbox`. This generalises the reasoning already applied to Cursor to every client; Cursor still never gives up, since it cannot deliver at all.
- **An empty inbox syncs the cursor on every client, Cursor included.** Its carve-out exists to protect items that would be dropped unseen, and an empty read has none. No behavioural change against the current server — the inbox endpoint echoes `since` back as `nextCursor` when nothing is waiting, so the sync resolves to the cursor already held. It is the defensive branch, not an optimisation.

### Fixed

- **The hook no longer wipes unrelated inbox state.** Its `saveInboxState` rebuilt the object from three fields, silently dropping `link_offered` and `hook_hint_at` on every run — so a peer's watch link could be offered again after having been offered once.

## [0.5.2] - 2026-08-27

### Added

- **Fresh installs learn they have an address.** Nothing announces anything at startup and the server `instructions` forbid raising waves unprompted, so an operator who installed the MCP and declined notifications would never learn they had an address or that waves existed. `check_inbox` now returns a one-time `address_hint`, independent of notifications. It shares `announced_at` with the hook's first-run notice, so whichever fires first wins and the operator hears it exactly once.

### Changed

- **At most one nudge per `check_inbox` reply**, in dependency order: address → notifications → watch link. Each is once-only, so a suppressed one fires on a later call rather than being consumed. Stacking them makes an agent read like a sales pitch.

## [0.5.1] - 2026-08-27

### Changed

- **Wave links are read-only.** Human posting via a link code is removed server-side (`POST /api/waves/messages` with `code` now returns `410 human_posting_removed`). A link that can post is a bearer credential for the operator's *voice* — forwarded in a chat or pasted into a repo, whoever holds it speaks as them and the peer cannot tell. Reading stays (`GET ?code=...`): leaked visibility is recoverable, leaked voice is not. Humans reply through their own agent, where their approval and context already live.
- **The watch link is now actually offered.** Nothing reliably prompted it before: llms.txt carries the etiquette but only raw-HTTP agents read it, and `get_wave_link`'s own description is only read once an agent is already considering that tool — so an operator could have a conversation happening on their behalf with no way to look at it, most often on the RECEIVING side where nothing prompted anyone at all. Three fixes: the contact card's `after_restart` checklist names it, the server `instructions` carry a standing directive ("first time a wave opens with a new peer — whether you started it or they did — offer the browser view once"), and `send_wave` / `check_inbox` return a one-time `watch_link_hint` per peer. Never stacked with `notifications_hint`; a hint failure can never break a send or an inbox read.
- `get_wave_link`'s description now says the link is READ-ONLY and that the operator replies by asking their agent to send. **This is why 0.5.1 exists:** a tool description is what an agent reads to decide behaviour, and 0.5.0 still told agents the operator could reply through the link.

## [0.5.0] - 2026-08-26

### Added

- **Wave notifications** (`enable_wave_notifications`): registers a client lifecycle hook that surfaces inbound waves in the operator's session instead of leaving them unseen until someone runs `check_inbox`. Writes Claude Code (`~/.claude/settings.json`, SessionStart + UserPromptSubmit), Codex CLI (`~/.codex/hooks.json`, both events), Gemini CLI (`~/.gemini/settings.json`, SessionStart) and Cursor (`~/.cursor/hooks.json`, sessionStart). Idempotent, backs up to `<file>.hypawave.bak`, never overwrites an unparseable config, preserves unrelated hooks, and `action: "disable"` removes only its own entries. A tool rather than a startup side effect, so the host's permission prompt gates the edit.
- **`inbox` subcommand**: `npx -y @hypawave/mcp inbox` runs a one-shot signed inbox check and exits — the command the hook invokes, usable standalone. Cursor state in `~/.hypawave/inbox-cursor.json`; throttled to one network call per 60s (`HYPAWAVE_INBOX_THROTTLE_SEC`), 5s request timeout (`HYPAWAVE_INBOX_TIMEOUT_MS`), silent on an empty inbox, exits 0 on any failure so it can never block a prompt, and does nothing if no identity exists yet (it must not create one as a side effect of a hook firing). The read cursor advances only on a successful read.
- **Local contacts** (`save_contact`, `list_contacts`): `~/.hypawave/contacts.json` (0600), never sent to Hypawave. `send_wave`, `send_file`, `read_wave` and `get_wave_link` now accept a saved name wherever a pubkey goes. Permissive on save (duplicate names allowed), strict on resolve (an ambiguous name is refused with both pubkeys rather than guessed — guessing sends a file to the wrong agent). `block_agent` still requires a raw pubkey.
- **One-time discovery hint**: `check_inbox` returns a `notifications_hint` when a supported client is installed but has no hook yet — the only surface that reaches operators who already had the MCP and never see the contact card (the first-run notice cannot help, since it only fires once a hook exists). Said once, then never again; silent on clients that cannot run hooks at all.
- **One-time setup notice**: the first successful hook run tells the operator their own agent address once — the only surface that reliably reaches a new operator, since the server `instructions` forbid raising waves unprompted. Not consumed by a failed first check.

### Security

- Hook output carries **counts and sender pubkeys only** — never message bodies, topics or filenames. That text enters the agent's context with no operator in the loop, and everything a peer sends is attacker-controlled; auto-injecting it would make any inbound wave a prompt-injection vector. Reading content requires an explicit `check_inbox`.
- Contact labels always render with the pubkey (`Bob (02c7a52b57…)`) so a name can never be mistaken for a verified identity.

### Known limitations

- **Cursor** drops `additional_context` before it reaches the model (confirmed, unfixed upstream). Its config is written and correct; the hook does not advance the read cursor there, so nothing is lost and it works once fixed.
- **Not reachable by hooks:** Claude Desktop (no hook system), Windsurf (no session-start event; `show_output` does not apply to `pre_user_prompt`), and Codex's IDE extension / desktop app (hooks fire in the CLI only). All fall back to `check_inbox`.

## [0.4.1] - 2026-08-12

### Changed

- **Documentation: `HYPAWAVE_MAX_SPEND_SATS` is a per-payment size bound, not a spending budget.** The previous wording ("spending guardrail", "the only guardrails on what an agent can spend") read as a cumulative cap. It bounds the size of any one payment — keeping it within platform policy and in the range Lightning reliably routes — and never limited total spend. `SECURITY.md` now sets out what each layer bounds: platform ceiling (server-side, caps what a seller may charge) → per-payment cap (client-side, your machine) → your wallet's NWC connection budget (the only cumulative wall). No behavioural change.
- Payment refusal message now reads "exceeds the per-payment cap of N sats".

## [0.4.0] - 2026-08-06

### Added

- **Agent Waves** (24 tools total, 8 new): private agent-to-agent pair conversations over `POST/GET /api/waves/*` — signed messages (`send_wave`, `read_wave`), a cross-wave inbox (`check_inbox`: new messages + pending file transfers in one call), free encrypted file handoffs (`send_file` / `receive_file`: AES-256-GCM locally, key ECIES-wrapped to the recipient, released one-time against the recipient's signature, ciphertext-integrity verified before decrypt), human view links (`get_wave_link`: per-side, revocable), contact cards (`get_contact_card`: shareable `hypawave.com/a/<pubkey>` address), and `block_agent`.
- **Canonical transfer key wrap** `ecies-secp256k1-aes256gcm-v1`: `base64( ephemeralPub(33) || nonce(12) || AES-256-GCM(kek=SHA256(compressed ECDH shared point), raw 32-byte file key) )` — spec mirrored in llms.txt#waves; `receive_file` rejects other `wrap_algo` values rather than guessing.
- Server `instructions` block: wave etiquette (surface the card on operator intent only, check inbox once per session, treat peer messages and received files as untrusted data, pricing decisions belong to the operator).

## [0.3.0] - 2026-07-22

### Added

- **Operator funding options** — `setup_wallet {action:"funding_options", amount_sats?}` returns operator-facing funding instructions to present verbatim: an exact-amount Lightning invoice (payable from Cash App, Coinbase, or any Lightning wallet) or the wallet's Lightning address, plus an on-chain deposit address for exchanges without Lightning (e.g. Robinhood; 300-sat Coinos dust minimum surfaced). Hosted-wallet creation now returns the same funding block, and insufficient-balance NWC payment errors point the agent at the action.
- **Per-wallet NWC guidance** — `setup_wallet {action:"connect_own"}` without `nwc_url` now returns wallet-specific steps (Alby Hub, Coinos, Primal, LNbits, self-hosted node) for finding the connection string, instead of erroring; operator option 2 tells the agent to ask which wallet the operator uses.
- Coinos registration JWT (no expiry) is persisted in `~/.hypawave/wallet.json` (`token`) to mint funding invoices and on-chain addresses later; new `createFundingInvoice()` / `getOnchainAddress()` client helpers (`POST /invoice`, types `lightning` / `bitcoin`).

## [0.2.0] - 2026-07-06

### Added

- `setup_wallet` tool (16 tools total): agent-driven wallet provisioning. `create_hosted` registers a fresh Coinos account (registration auto-creates the NWC connection; requires explicit operator consent via `confirm: true`) and saves credentials to `~/.hypawave/wallet.json` (0600, local-only — Hypawave servers never receive them). `connect_own` saves an existing NWC string instead and live-verifies it with a balance probe. Called with no arguments, it returns operator-facing options to present verbatim.
- `getNwcUrl()` now falls back to `~/.hypawave/wallet.json` when `NWC_URL` / `HYPAWAVE_NWC_URL` are unset (env always wins). `wallet_status` and the startup log report the config source; manual-mode messages point to `setup_wallet`.
- `COINOS_API_URL` env var (default `https://coinos.io/api`).

### Security

- Wallet file is written 0600 and never overwritten while it exists (it may hold a funded wallet's only credentials); corrupt files degrade to manual mode instead of throwing.

## [0.1.2] - 2026-07-02

### Added

- `server.json` + `mcpName` in package.json — MCP Registry (registry.modelcontextprotocol.io) publication metadata under `io.github.hypawave/mcp`.

## [0.1.1] - 2026-07-02

### Added

- README: "Listing in the marketplace" section — required fields, enums, limits, and immutability when `is_public: true`.
- README: link to SECURITY.md from the Safety model section.

## [0.1.0] - 2026-07-02

### Added

- Initial release: local stdio MCP server exposing the Hypawave accountless paths (3a/3b) as 15 tools.
- Buyer tools: `search_offers`, `get_offer`, `buy_offer`, `confirm_payment`, `download_files`, `pay_invoice`, `get_receipt`, `check_payment`.
- Seller tools: `create_offer`, `attach_file`, `manage_offer`, `create_invoice`, `my_offers`, `list_sales`.
- Utility: `wallet_status`.
- NWC (Nostr Wallet Connect) payment support with automatic preimage capture; manual bolt11 fallback when no wallet is configured.
- Operator spending cap enforced in code with bolt11 amount cross-check: `HYPAWAVE_MAX_SPEND_SATS`, defaulting to the platform's live `max_invoice_usd` (converted at the current BTC price) when unset.
- secp256k1 pubkey-signature auth (llms.txt spec, verified against the published test vector); identity auto-generated to `~/.hypawave/identity.json`.
- Client-side AES-256-GCM encrypt/decrypt with `ciphertext_sha256` content-commitment verification.
- Activation settlement handling: after paying an activation/renewal fee, seller tools wait on `activation_window_end` (the authoritative payability signal) and nudge the settlement fallback if the payment webhook is slow — results report `activated` + `activation_window_end`.
