# Security

The library enforces most of these rules. You are responsible for the rest:
secret handling, pricing, and any custom repository.

- Keep the receive-only NWC code and the payment attempt's
  [`swap_data`](/guides/automated-swaps.md) on the server. A receive-only code cannot
  spend your funds. But anyone who has it can create invoices against your
  wallet and read your payment history. Before you deploy, search your built
  browser bundles and their source maps for `nostr+walletconnect://` and
  `lightning+swapconnect://`, and for the names `NWC_URI` and `LSC_URI_`: none
  may appear there.
- Wallet preflight refuses to start if the NWC connection advertises spend
  methods such as `pay_invoice`. Create a receive-only code. If the wallet
  cannot, and you accept the risk, set
  [`allowSpendCapableWallet: true`](/api_docs.md#createopenreceive)
  (Rails: `config.allow_spend_capable_wallet = true`) or
  `OPENRECEIVE_ALLOW_SPEND_CAPABLE_NWC=true`. The library still logs a loud
  warning.
- Recompute prices from your own order data
  ([`amountFor`](/api_docs.md#createhost) /
  [`config.amount_for`](/api_docs.md#openreceiveconfigure)). A create
  request carries an order id, never a price. Your
  [`authorize`](/api_docs.md#the-authorize-context) policy runs on every
  order-scoped route. See [Authorization](/guides/authorization.md).
- The attempt row is committed before the invoice is shown. Concurrent creates
  for the same order run one at a time. A custom `PaymentRepository` must keep
  that guarantee.
- Accept settlement only from a wallet finality signal (`settled_at` or wallet
  state `settled`). A preimage is never enough.
- Settlement is written once and never changed. `onPaid` / `config.on_paid`
  runs only for the order's first settled attempt, inside the settlement
  transaction.
- The server clock alone never closes an unpaid attempt. The library waits for
  a successful wallet scan at or after expiry.
- Treat `swap_data` as a provider credential. Never put it in HTTP responses or
  logs. Provider completion alone does not fulfill an order. Wallet settlement
  does.

## Why a receive-only NWC code is the only wallet credential

An NWC code is a connection string. It holds a client secret that the wallet
created for **this** connection, plus a relay. The wallet decides which methods
that secret may call.

A receive-only connection can create invoices and list incoming payments. It
cannot pay anyone. The secret might leak through a compromised server, a `.env`
file in git, or a log line. An attacker who gets it can create invoices payable
*to you* and read your history. They cannot drain the wallet.

OpenReceive has no code path that sends payments. The spend-capable override
above only widens what a leaked code could do from some other NWC client.

### What preflight proves

On boot, OpenReceive asks the wallet what **this** connection may do. Node
adapters do this on the first request instead. It checks three things:

1. It can `make_invoice` and `list_transactions`.
2. It speaks an encryption mode the library supports.
3. It does not advertise spend methods.

If any check fails, your application does not start. Await the adapter's
`ready` promise in a deploy health check. Tests can
[inject a fake wallet](/guides/host-testing.md#inject-a-fake-wallet-client)
and skip NWC entirely.

### What receive-only does not cover

- The code is still a secret. If it leaks, revoke it at the wallet and create a
  new one. The library redacts it from its own logs. `npx openreceive doctor`
  only reports whether it is present or missing, never the value.
- The wallet service holds custody of the funds.
- The relay can delay or drop traffic. It cannot fake a settlement, because
  responses are encrypted between your client key and the wallet.
- Get a receive-only code from a wallet that issues them:
  <https://openreceive.org/get_a_nwc_code_to_receive_payments>.

## Further reading

- [Deploying OpenReceive](/guides/deploying.md)
- [Payment storage](/guides/storage.md)
- [Automated swaps](/guides/automated-swaps.md) and
  [Lightning Swap Connect](/guides/lightning-swap-connect.md)
- [Rate limiting](/guides/rate-limiting.md)
- [Authorization](/guides/authorization.md)
