Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ with the pop in `Authorization: Payment …`.
1. **Use pops** — hold/lock BTC, mint pops, and pay HTTP-402-gated resources.
Drive the `pop` CLI wallet from its machine contract:
**[skills/pop-wallet.md](skills/pop-wallet.md)** (exact per-command JSON, the
frozen 31-code error table, and the safety rails for locking real BTC).
frozen 33-code error table, and the safety rails for locking real BTC).
`pop pay <URL> --token <cashuB>` runs the 402 dance for you.

2. **Accept pops** — gate your own HTTP service so it charges a pop per request.
Expand All @@ -41,7 +41,7 @@ build with no special access (`cdk-common` is a normal crates.io `0.16` dep). Th
`pop` CLI is a Cargo workspace member: `cargo build -p pop` / `cargo test -p
pop`. The toolchain is pinned in `rust-toolchain.toml` (Rust 1.95). The other
crates: `pops-core-verify` (the verifier), `pops-gateway` (the reverse-proxy),
`pops-core-funder` (the in-repo funder crypto kernel, extracted from `cdk-pop`) /
`pops-core-types` (support), plus `ts/` (WASM bindings + a Next.js serverless
demo). The gateway image and the WASM bindings are also published (ghcr +
`wasm-pkg` branch) if you want to consume rather than build.
and `pops-core-funder` (the in-repo funder crypto kernel, extracted from
`cdk-pop`), plus `ts/` (WASM bindings + a Next.js serverless demo). The gateway
image and the WASM bindings are also published (ghcr + `wasm-pkg` branch) if
you want to consume rather than build.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> **Agents → [AGENTS.md](AGENTS.md).** That's the entry point: it maps the two
> pathways — **use pops** (drive the `pop` wallet from its machine contract,
> **[skills/pop-wallet.md](skills/pop-wallet.md)** — exact per-command JSON, the
> frozen 31-code error table, and the safety rails for locking real BTC) and
> frozen 33-code error table, and the safety rails for locking real BTC) and
> **gate your own service** (**[skills/gate-a-service.md](skills/gate-a-service.md)**)
> — plus the shared `Payment` wire format.

Expand All @@ -15,8 +15,11 @@ token — and presents it to reach a gated resource. No upfront payment and no
account: spam-resistance comes from the **locked capital** ("power"), which
can't be cheaply farmed at scale. It's HTTP-402-native (a gated resource answers
with `402` + a `WWW-Authenticate: Payment` challenge; the client retries with
the pop in `Authorization: Payment …`). The verifier is **ecash-agnostic** —
pops are one supported credential type.
the pop in `Authorization: Payment …`), conforming to `draft-cashu-charge-01`:
challenges are stateless-bound (HMAC `id`) and expire, value at or above the
charge is accepted (excess retained), and a mint-trust DLEQ incident is
flagged to the operator rather than failing a settled payment. The verifier is
**ecash-agnostic** — pops are one supported credential type.

**Non-custodial:** there is no third party. A spent pop is bearer ecash that the
operator redeems into fresh proofs **only they control** — the operator keeps the
Expand Down
4 changes: 3 additions & 1 deletion crates/pop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ path = "src/main.rs"
pops-core-funder = { path = "../pops-core-funder" }

# The cashu-free `Payment` auth-scheme codec (HTTP-402 client primitives:
# parse_payment_params / decode_request_envelope / encode_payment_credentials +
# parse_payment_params / decode_charge_request / encode_payment_credentials +
# their structs). `default-features = false` drops the crate's `native` surface
# (cdk/axum/http/uuid) — `pay` only needs the envelope codec. See `crate::http402`
# for the thin re-export the pay command uses.
Expand Down Expand Up @@ -60,6 +60,8 @@ chrono = { version = "0.4", default-features = false, features = ["clock"] }

[dev-dependencies]
tempfile = "3"
# Hand-builds legacy/raw `request` auth-param blobs in the pay-command tests.
base64 = "0.22"
# Recovery-path integration tests (`tests/pay_recovery.rs`) stand up an
# in-process TCP stub that plays both the pops-gateway (the 402 + retry) and the
# mint (`/v1/keysets`, `/v1/keys`, `/v1/swap`) so the post-swap value-recovery
Expand Down
42 changes: 32 additions & 10 deletions crates/pop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
a CLTV-locked Bitcoin UTXO. This wallet is the funder's single tool to:

- **`init`** — create a seed (the only secret),
- **`quote`** — create + verify a funding quote, persist it, and exit (no
funding poll; resume later with `mint --resume`),
- **`mint`** — lock BTC in a CLTV-backed P2TR, wait for funding, and get a
`cashuB` credential token,
- **`pay`** — spend a held pop at an HTTP-402 gated endpoint,
- **`recover`** — reclaim the locked BTC after the timelock matures,
- **`list` / `status`** — track deposits, and
- **`balance`** — an aggregated ledger summary (total locked, per-state
Expand Down Expand Up @@ -45,7 +48,8 @@ cargo build --release # from the repo root, or: cargo build -p pop
```

The binary is `target/release/pop`. Install with
`cargo install --path .` (installs a single `pop` binary).
`cargo install --path crates/pop` from the repo root (the root `Cargo.toml` is
a virtual workspace manifest, so `--path .` does not install).

## Wallet directory

Expand Down Expand Up @@ -89,13 +93,28 @@ pubkey) and the on-chain recovery key (x-only pubkey).
### init

```
pop init [--words 12|24] [--network mainnet|testnet|signet|regtest] [--esplora-url URL] [--force]
pop init [--words 12|24] [--mnemonic "<words>"] [--show-mnemonic]
[--network mainnet|testnet|signet|regtest] [--esplora-url URL] [--force]
```

Generates a BIP-39 mnemonic, writes the derived seed in plaintext (file `seed`,
perms `0600`), writes `config.toml` and an empty db, and prints the mnemonic
**once**. Write it down — it is the only secret and the only backup. There is no
passphrase. Default network is **mainnet**.
Generates a BIP-39 mnemonic (or imports an existing one via `--mnemonic`),
writes the derived seed in plaintext (file `seed`, perms `0600`), writes
`config.toml` and an empty db, and prints the mnemonic **once** — to stderr by
default; `--show-mnemonic` additionally includes it in the stdout JSON. Write
it down — it is the only secret and the only backup. There is no passphrase.
Default network is **mainnet**.

### quote

```
pop quote --mint-url URL --amount SATS --mint-pubkey HEX33
[--duration 30d | --unit pop_<ts>] [--label TEXT]
```

The non-blocking first half of `mint`: creates the funding quote,
**independently re-verifies** the returned address, persists the deposit +
recovery file, prints the funding address, and **exits** without polling. Fund
it on your own schedule, then continue with `pop mint --resume <deposit_id>`.

### mint

Expand Down Expand Up @@ -142,12 +161,15 @@ machine contract).
### recover

```
pop recover (--deposit ID | --all) --dest ADDRESS [--fee 200] [--no-broadcast]
pop recover (--deposit ID | --all) --dest ADDRESS
[--fee SATS | --target BLOCKS] [--no-broadcast]
```

Refuses (with an ETA) any deposit whose CLTV has not matured — maturity is
evaluated against the chain tip's **median-time-past** (BIP-113), not
wall-clock. For matured deposits it rebuilds the construction from stored
The fee is sized from the esplora mempool feerate estimate at `--target`
confirmation blocks (default 6); an explicit `--fee <sats>` (absolute)
overrides the estimate. Refuses (with an ETA) any deposit whose CLTV has not
matured — maturity is evaluated against the chain tip's **median-time-past**
(BIP-113), not wall-clock. For matured deposits it rebuilds the construction from stored
params, derives the funder privkey, fetches the funding UTXO, asserts the
on-chain scriptPubKey matches, builds the `nLockTime = ts_expiry` script-path
spend, schnorr-signs, and broadcasts. `--no-broadcast` prints the raw tx hex
Expand Down
Loading