Skip to content

Commit 990d173

Browse files
ralyodioclaude
andauthored
feat(pit): the operator sets the price, not the form (#170)
$2 a name and $5 an ending are defaults, not ceilings. The server already knew that -- setTldPrice deliberately does not enforce MAX_CHILD_PRICE_USD, which is why .love sits at $10,000 today. The claim form did not: it shipped max="2" on the price input, so the UI refused what the API would have accepted and looked like policy while being a typo's worth of markup. The remaining bound was $1,000,000, low enough to be a policy decision nobody made. It is now MAX_LISTING_PRICE_USD, a named overflow guard at $1e9 whose only job is keeping Infinity, NaN and 1e300 out of a column that later gets charged. MAX_CHILD_PRICE_USD is untouched: it is part of the vendored namespace rules that must match the published package byte for byte in behaviour, and it is still the right default. It just is not a limit. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 681ca9e commit 990d173

3 files changed

Lines changed: 41 additions & 11 deletions

File tree

apps/pwa/src/moshpit.mjs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ export {
2929
parseTldList, tldRejection, normalizeMode, resolutionPreference,
3030
} from "./lib/moshpit-name.mjs";
3131

32+
/**
33+
* The largest number this column will accept.
34+
*
35+
* Not a policy cap. What an ending's names cost is the operator's call -- the
36+
* defaults ($2 a name, $5 an ending) are a starting point, not a ceiling, and
37+
* an ending somebody wants seven figures for is their business. This exists
38+
* only so a fat finger or a hostile client cannot park Infinity, a NaN or 1e300
39+
* in a column that later gets charged.
40+
*
41+
* It replaced a $1,000,000 bound, which was low enough to be a policy decision
42+
* nobody had made.
43+
*/
44+
export const MAX_LISTING_PRICE_USD = 1_000_000_000;
45+
3246
const COLS = `tld, user_id, owner_email, alias_of, price_usd, created_at`;
3347

3448
export async function getTld(tld) {
@@ -334,12 +348,11 @@ export async function setTldPrice({ tld: tldInput, userId, priceUsd }) {
334348
// NaN/Infinity would be stored verbatim and then charged; a negative or
335349
// zero price would let anyone drain the namespace for free.
336350
if (!Number.isFinite(price) || price <= 0) return { ok: false, error: "price must be a positive number" };
337-
// Not capped at MAX_CHILD_PRICE_USD here on purpose. PRD 0005 R3 caps the
338-
// annual child price at $1.99, but that requirement arrives with terms,
339-
// renewals and the ledger, and today this same column also carries prices
340-
// set before any cap existed. The forms default to the cap and hint at it;
341-
// enforcing it is a migration, not a validation tweak.
342-
if (price > 1_000_000) return { ok: false, error: "price is implausibly large" };
351+
// Not capped at MAX_CHILD_PRICE_USD on purpose, and the forms no longer
352+
// pretend otherwise: $2 is what a new ending defaults to, not the most it
353+
// may charge. PRD 0005 R3's annual cap arrives with terms, renewals and the
354+
// ledger; this column already carries prices set before any cap existed.
355+
if (price > MAX_LISTING_PRICE_USD) return { ok: false, error: "price is implausibly large" };
343356
price = Math.round(price * 100) / 100;
344357
}
345358

@@ -773,7 +786,7 @@ function* chunksOf(list, size) {
773786
function normalizePrice(value) {
774787
if (value === null || value === undefined || String(value).trim() === "") return null;
775788
const price = Number(value);
776-
if (!Number.isFinite(price) || price <= 0 || price > 1_000_000) return undefined;
789+
if (!Number.isFinite(price) || price <= 0 || price > MAX_LISTING_PRICE_USD) return undefined;
777790
return Math.round(price * 100) / 100;
778791
}
779792

apps/pwa/src/routes/moshpit.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
listTldsForUser,
4343
listTldsNotOwnedBy,
4444
MAX_BULK_TLDS,
45-
MAX_CHILD_PRICE_USD,
45+
MAX_LISTING_PRICE_USD,
4646
normalizeLabel,
4747
normalizeMode,
4848
normalizePinKind,
@@ -658,9 +658,9 @@ moshpitRouter.get("/api/moshpit/resolve", async (req, res) => {
658658
const claimDefaults = (req) => `
659659
<div class="pit-defaults">
660660
<label>Price each
661-
<span class="pit-dot">$</span><input name="price_usd" type="number" min="0.01" step="0.01" max="${MAX_CHILD_PRICE_USD}"
661+
<span class="pit-dot">$</span><input name="price_usd" type="number" min="0.01" step="0.01" max="${MAX_LISTING_PRICE_USD}"
662662
value="${DEFAULT_TLD_PRICE_USD}" placeholder="unlisted" autocomplete="off"
663-
aria-label="price per name, in dollars — clear it to keep them off the market"></label>
663+
aria-label="price per name, in dollars — what you charge is yours to set; clear it to keep them off the market"></label>
664664
<label>Point at
665665
<span class="pit-dot">.</span><input name="alias_of" placeholder="nothing"
666666
autocomplete="off" spellcheck="false" aria-label="an ending you already hold"></label>

apps/pwa/test/moshpit-sales.test.mjs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,31 @@ test("moshpit name sales", { skip: installed ? false : "pwa dependencies not ins
4444
});
4545

4646
await t.test("a price must be a positive, plausible number", async () => {
47-
for (const bad of [0, -5, "abc", Infinity, NaN, 5_000_000]) {
47+
// The ceiling is an overflow guard, not a policy price: what an operator
48+
// charges is their call, so only nonsense is refused.
49+
for (const bad of [0, -5, "abc", Infinity, NaN, 5_000_000_000]) {
4850
const r = await m.setTldPrice({ tld: "whatever", userId: SELLER, priceUsd: bad });
4951
assert.equal(r.ok, false, `${bad} should be refused`);
5052
}
5153
// A zero or negative price would let anyone drain the namespace for free.
5254
assert.equal((await m.getTldWithPrice("whatever")).price_usd, null);
5355
});
5456

57+
await t.test("an operator may ask seven figures for a name", async () => {
58+
// $2 is what a new ending defaults to, not the most it may charge — the
59+
// form used to cap the input at the default and call it a rule.
60+
const r = await m.setTldPrice({ tld: "whatever", userId: SELLER, priceUsd: 1_000_000 });
61+
assert.equal(r.ok, true, r.error);
62+
assert.equal(r.priceUsd, 1_000_000);
63+
64+
const quoted = await m.quoteName({ tld: "whatever", label: "expensive", buyerId: BUYER });
65+
assert.equal(quoted.ok, true, "and a buyer can be quoted it");
66+
assert.equal(quoted.priceUsd, 1_000_000);
67+
68+
await m.setTldPrice({ tld: "whatever", userId: SELLER, priceUsd: null });
69+
assert.equal((await m.getTldWithPrice("whatever")).price_usd, null);
70+
});
71+
5572
await t.test("listing sets a price, rounded to cents", async () => {
5673
const r = await m.setTldPrice({ tld: "whatever", userId: SELLER, priceUsd: "12.345" });
5774
assert.equal(r.ok, true);

0 commit comments

Comments
 (0)