one mint concept: retire the real-mint fence and the play-money class, leaving the seat's accepted list as the only gate - #964
Open
maxy-player wants to merge 6 commits into
Conversation
added 6 commits
September 3, 2026 08:14
Owner ruling (Bob, 2 Sep 2026): "the concept of real mint doesn't make sense - its just a mint. the seller chooses what mint to accept or not" and "there should be no 'testmint' concept". One rule is left: a mint is usable by this seat when it is on this seat's configured list, and not otherwise. - `allow_real_mints` is gone: the config field, its serde default, the env row, and the config-template block. A config.toml still carrying the key LOADS — it is dropped before the `deny_unknown_fields` parse and the drop is announced once, so no operator's boot breaks. `MAXPLAYER_ALLOW_REAL_MINTS` joins the reserved env seams for the same reason. - `home::mint_allowed(url, switch)` becomes `home::mint_url_supported(url)`: a SHAPE rule (http:// or https:// with a host), not a policy. `http://` is admitted deliberately — a seat's own sidecar mint runs on loopback. - `WalletOpsError::RealMintDisallowed` is gone with the duplicate fences that raised it: send/receive/melt already gate on the configured list, and the seller's redeem path already gates on the stored creq's accepted set. `plan_payment` / `select_source_mint` / `AwardFilters` lose the parameter, so their callers change with them. - `DEFAULT_MINT_URL` loses its name. Nothing in the product is called the default mint except the configured list (shipped default: minibits), which is now what `default_mint()`'s fallback, the seller setup seed and the dead-host migration read. Test fixtures use `FIXTURE_MINT_URL`, a plain example host. - Product docs that taught the removed concept are rewritten to the one rule: the crossmint smoke script (its dead env export was already a no-op), both served skill.md files, README's play-money paragraph, docs/DOCKER.md and docs/BUYER-QUICKSTART.md. The money class (`MoneyType`, `is_autopay_mint`, `refuse_silent_play_money` and the play-money row marker) is still PRESENT here, re-anchored on `LEGACY_TESTNUT_MINT_URL` so this commit compiles and the split is reviewable one change at a time. The next commit deletes it.
## Removed by the owner's one-mint ruling Bob, thread messages 1544914875195723838 (2026-09-02 03:40:09Z) and 1544916134657269771 (03:45:09Z): a mint is just a mint, the seller chooses what mint to accept, and there is no "testmint" concept. This removes the MakePrisms#445 `refuse_silent_play_money` refusal and the `MoneyType` class, both dead once the default host is gone — the class could only ever answer `Play` for one hardcoded URL, and the refusal could only ever fire for a home whose default was that URL, telling the operator to affirm play money at a host nothing seeds any more. Gone with them: `is_autopay_mint`, the auto-complete branch of `mint_async` (every mint now returns the invoice for the caller to pay), the `play_money=true` row marker on `wallet balance` / `wallet mints list` / the setup summaries, the seven tests that covered them, and `pub const LEGACY_TESTNUT_MINT_URL` — the host it named is not dead and not special, so it gets no constant. `DEAD_TESTNUT_MINT_HOST` and its bootstrap migration are untouched: that host really is dead and the migration only ever removes it. Two consequential edits, named so the scope is legible: the two balance tests that only assert on a stray mint's row and the totals line needed *a* configured default rather than *the* auto-paying one, so each has one token changed, `seed_testnut_default_home` -> `seed_fixture_default_home`; and a stale doc line plus a duplicate `#[cfg(feature = "wallet")]` left above that fixture helper by an earlier edit go here too.
…t in prose Advisor DENY of 0e80e32 on checks D and F; appended, not rewritten. D — `mint_url_supported` called "host" everything after the scheme and only checked that tail was non-empty, so `http:///x`, `http://:8080` and `http:// ` were all waved through. It now splits the authority at the first `/`, `?` or `#`, drops any `user@` prefix and any numeric `:port`, and requires a non-empty host with no whitespace. `http://` STAYS ADMITTED — a seat's own sidecar mint runs on loopback, and the doc comment saying so is unchanged. New test `mint_url_supported_requires_a_scheme_and_a_real_host` names all twelve malformed cases plus the admitted forms and the non-http schemes. F — fourteen survivor lines still taught the retired real-mint / play-money concept, in prose the G1-G4 census never searched for: - `web/app/.well-known/skills/debug-buying/skill.md` — SERVED to operators, said the hop happens "if real mints are allowed" and that a mismatch means being pinned to "test-only mints". Both are gone; the paragraph now says a mismatch blocks only when there is no route. - doc comments in `authorize_pay.rs` (x3), `buyer_fund.rs`, `buyer/lifecycle.rs`, `job_lifecycle.rs` (x2) that named a fence which no longer exists, and one in `wallet_cli.rs` naming the "held play-money class" that the previous commit deleted. - `scripts/crossmint-smoke.sh`: `arm_real_mints` is renamed `arm_mint_lists`, for what it actually does — export the accepted and extra mint lists and the budget cap. All three call sites updated. The parked testnut dry-run lines in the smoke script are untouched.
…hint Advisor's ninth F line (maxie's ruling, 2026-09-03 17:08Z), appended. `with_prereq_hint` matched `real-mint fence` to decide that a tool failure was a funding prerequisite. Both producers of that string — crossmint.rs's two fence refusals — went with the fence itself, so at this head the phrase had exactly one hit tree-wide: this disjunct. It was an unreachable branch asserting in product code that the retired class still gates a payment. Deleted; the `;` moves onto the `mint_unreachable` disjunct. The other three disjuncts are untouched. No test referenced the phrase — the funds-prereq test drives the "no balance at any accepted mint" disjunct — and the tree-wide count for `real-mint fence` is now 0.
Advisor check H. `home.rs` carried two `#[test]` attributes stacked on `mint_url_supported_requires_a_scheme_and_a_real_host` and none on `shipped_defaults_are_real_money_and_usable`, so that test was compiled but never run. The defect entered with the D commit `4673cfa`. The net `#[test]` count concealed it: 50 -> 51 across that commit reads as "one test added", but it was +2 (double registration) and -1 (lost registration). Removing the duplicate and restoring the missing attribute leaves the count at 51 and every suite total unchanged, which is exactly why the count is not the instrument. The compiler had already said so: `duplicate_macro_attributes`, and "function `shipped_defaults_are_real_money_and_usable` is never used". Exactly one attribute on each function; nothing else in this commit.
Advisor check D, on the route addendum 13 corrects. The predicate still hand-split the authority and called a non-numeric port part of the host, so `https://mint.example:abc` and `https://[::1` both returned true. The remedy first ordered — `url::Url::parse` — is withdrawn, on two grounds maxie names: `url` is a DEV-dependency of this crate, so product code cannot reach it without changing a money-adjacent crate's shipped dependency set; and the parser would break the contract it was ordered to enforce, because `Url::parse("http:///x")` yields the host `x` (WHATWG special schemes skip repeated slashes) and would ADMIT a case this predicate refuses today. No manifest edit is in this commit. So the validator stays hand-rolled and becomes correct, in the six steps of addendum 13 §2: scheme, authority to the first `/`, `?` or `#`, host after the last `@`, a bracketed IPv6 literal that must close and may carry only an all-digit port, an unbracketed host with no brackets and at most one `:` before an all-digit port, and finally a non-empty host with no whitespace. Two new admissions with a product reason: a seat's own sidecar mint may sit on IPv6 loopback, so `http://[::1]:3338` and `https://[::1]` are admitted. `http://` stays admitted. The 7 previously admitted cases are all still admitted and all 17 previously refused are all still refused. Also drops the gratuitous blank line the H commit added after `mod tests {`, per addendum 13 §4.
|
Someone is attempting to deploy a commit to the MakePrisms Team on Vercel. A member of the Team first needs to authorize it. |
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
A mint is the service that issues and redeems the digital-cash tokens a maxplayer seat pays
and gets paid with. Until now the code carried a second idea on top of that: some mints were
"real" and some were "test", a
allow_real_mintsswitch fenced the real ones off, a "play-money"class existed for the test ones, and a test mint (
testnut) was the shipped default.The owner's ruling (03:40Z, the night of 2 Sep 2026): a mint is just a mint. There is no "real mint" or
"testmint" concept; the seller chooses what mint to accept or not. The shipped default is minibits.
This PR makes the code say exactly that. The seat's configured accepted list is the whole
admission decision. If a mint URL is on the operator's list, the seat deals with it; if it is
not, the seat does not. The real-mint fence, its config key, its environment variable, the
play-money class and the test-mint default all go with the retired concept. The one shape rule that
remains — "is this string even a mint URL" — is now implemented correctly.
What changed
allow_real_mintsis no longer a setting andMAXPLAYER_ALLOW_REAL_MINTSno longer does anything. An old
config.tomlthat still carries the key loads with a one-linewarning instead of refusing to boot; the environment variable is reserved so an old
exportcannot refuse a boot either. Those five lines of back-compat are the only places the old names
survive.
MoneyType::Play,refuse_silent_play_money) — commit0e80e32, the second of six. It goes with the retired concept on the owner's ruling of3 Sep 2026 23:54Z; it is not held open for a revert.
DEFAULT_MINT_URLno longer exists anywhere outside untouchedweb fixtures; the dead
testnut.cashu.spacehost survives only as a migration source thatrewrites an old config to minibits. Test fixtures use one clearly named constant of their own.
smoke script and code comments no longer teach "real mint" versus "test mint".
mint_url_supported(home.rs:1473-1521) acceptshttp://orhttps://with a real, non-empty host, and nothing else. It refuses what the oldauthority tail-check waved through — a non-numeric port (
https://mint.example:abc) and anunterminated IPv6 literal (
https://[::1) — and admits a sidecar on IPv6 loopback(
http://[::1]:3338,https://[::1]).Six append-only commits over
upstream/main8c3bc9b, 22 files, +659/−1012. Zero manifestor dependency changes (
Cargo.toml/Cargo.locktouched: 0).credential_proxy.rsis not amongthe 22 files.
Grade versus release. The forge advisor graded this head A–L passing at
14e149b. That is agrade only; it authorises no push, PR, merge or payment by itself. The push and this PR were
released by maxie and confirmed by the owner. The merge is the owner's act.
The URL validator, and why it is written by hand
home.rs:1473-1521implements six steps: scheme must behttps://orhttp://; the authority isthe text up to the first
/,?or#; the host is what follows the last@; a bracketed IPv6literal must close and may be followed only by
:and an all-digit port; an unbracketed hostcarries no brackets and at most one
:, followed by an all-digit port; and the host must benon-empty with no whitespace.
The test (
home::tests::mint_url_supported_requires_a_scheme_and_a_real_host) names 9 admittedcases (
home.rs:2681-2690) and 19 refused (14 malformed authorities at:2697-2711, 5 wrong ormissing schemes at
:2718).url::Url::parseis deliberately not used. It is a browser-compatibility parser:Url::parse( "http:///x")returnsOkwith host"x"(WHATWG special schemes skip repeated slashes), so arule of "scheme ∈ {http, https} and non-empty host" built on it would admit
http:///x, whichthis whitelist refuses. Independently,
url2.5.8 is a dev-dependency ofmaxplayer-core(
Cargo.toml:138, under[dev-dependencies]at:113;[dependencies]opens at:57), and theonly
use url::in the crate sit inside#[cfg(test)]modules. Reaching it from product codewould have changed a money-adjacent crate's shipped dependency set, which is not this job.
http://on loopback stays admitted on purpose — a seat's own sidecar mint runs there(
home.rs:2681-2682and:2689-2690, reason at:1465-1467).A test that was silently not running — disclosed
At the earlier head
f719f1e, two#[test]attributes were stacked on the new URL test andshipped_defaults_are_real_money_and_usablehad none, so that test compiled and ran zerotimes. The compiler had said so (
duplicate_macro_attributes, "function … is never used") and itwas read past. Repaired at
5e7c68e: exactly one attribute on each.The inventory, measured on
cargo test … --lib -- --listat both heads: executed test lines1365 at BOTH heads (the duplicate attribute produced two entries under one name; the harness's
own
running 1365 tests= 1361 passed + 1 failed + 3 ignored), and unique test names 1364 atf719f1e→ 1365 at14e149b— exactly one name added,home::tests::shipped_defaults_are_real_money_and_usable, nothing removed. The unchanged total is why a count is not coverage.Test evidence, with its bounds
This is not CI-green. The evidence is two independent local ladders on the five
cargo teststeps of
ci.yml, plus the relay-policy step. Ordinals below are per ladder.Worker ladder (six steps,
--locked, at14e149b): step 1-p maxplayer-core381/0 · step 2--features acp430/0 (1 ignored) · step 3--release --features acp,gateway,git-delivery,wallet1361 passed / 1 failed / 3 ignored, exit 101 · step 4
-p maxplayer136/0 · step 5--features acp,wallet162/0 (1 ignored) · step 6-p maxplayer-relay-write-policy12/0.hearth ladder (five steps, independent detached worktree, at
14e149b): hearth steps 1, 2, 4and 5 exit 0; hearth step 3 exits 101 on
credential_proxy::tests::a_declared_over_cap_body_is_refused_before_the_upstream_sees_it(
credential_proxy.rs:3520:14, a ConnectionReset). hearth measured that test at 1 pass / 5failures over 6 attempts at BOTH base
8c3bc9band head14e149b. It is a pre-existingflake, not a regression: same rate at base, and
credential_proxy.rsis not in this diff.The
live-mintsmoney-path job (ci.yml:233) was not run by anyone here — it reaches a livethird-party mint, which the standing money bound forbids.
Who measured what. Worker: its own six-step ladder and the job report. hearth: the independent
five-step ladder and the graded package. Advisor: graded A–L from the diff and ran no code. maxie:
re-derived the lock, the scope, the validator span, the 19/9 case gate and the H name-set diff.
Forensic detail — commits, gates, locators (re-derived at 14e149b)
Commits over
8c3bc9b, append-only, no amend/rebase/force:git diff --shortstat 8c3bc9b 14e149b: 22 files changed, 659 insertions(+), 1012 deletions(-).Whole-tree gates at
14e149b(git grep, hits / files):-w -E 'allow_real_mints|RealMintDisallowed'4 / 2 — the back-compat drop athome.rs:1719/1723/1725and a test asserting the name is absent atwallet_ops.rs:1121·MAXPLAYER_ALLOW_REAL_MINTS1 / 1 — the reserved env var athome.rs:1840·-w DEFAULT_MINT_URLoutsideweb/network/test0 ·MoneyType::Play|refuse_silent_play_money0 ·LEGACY_TESTNUT_MINT_URL0 ·real-mint fence0 ·const FIXTURE_MINT_URLdeclarations 8 ·-w -i testnut118 / 27, every hit marked (cfg-test 71 · fixture 31 · smoke-dry-run 11 ·infra 4 · migration 1; unmarked 0).
DEAD_TESTNUT_MINT_HOST("testnut.cashu.space", migration source only):home.rs:75declaration ·
:1620doc ·:1677migration guard ·:2575migration test.Validator case verdicts at
14e149b, printed from the code: 7 original admitted → 7true;12 original malformed + 5 scheme cases → 17
false;https://mint.example:abcandhttps://[::1→
false(both weretrueatf719f1e);http://[::1]:3338andhttps://[::1]→true.http:///xrefused: its authority is empty under step 2.Sequencing. Same base as #962
(
8c3bc9b); independent trees. Owner's merge order: #962 → this PR → slice 2 of the ecash lane,which is held local, unpushed, and rebases onto this head before its own review.