diff --git a/plans/names-codes/mockups/domains.js b/plans/names-codes/mockups/domains.js index a2aab0f926..ac9e007908 100644 --- a/plans/names-codes/mockups/domains.js +++ b/plans/names-codes/mockups/domains.js @@ -57,6 +57,8 @@ const paidTotal = () => el("div", { class: "total" }, el("span", { class: "label const btn = (text, cls = "", ...extra) => el("button", { class: `btn ${cls}`.trim(), type: "button" }, text, ...extra); const disabled = (node) => { node.setAttribute("disabled", ""); return node; }; const link = (text) => el("a", { href: "#" }, text); +// the term as the page words it: a code's or a search's years, and the 30 days every registration adds +const termText = (years) => `${years} years + 30 days`; const chip = (text, cls = "", dot = false) => el("span", { class: `chip ${cls}`.trim() }, dot ? el("span", { class: "dot" }) : null, text); const alertBox = (tone, ic, title, text) => el("div", { class: `alert ${tone}` }, icon(ic), el("div", {}, title ? el("b", {}, title) : null, text)); const nameTitle = (label, cls = "name") => el("div", { class: cls }, label, el("span", {}, ".simplex")); @@ -111,7 +113,7 @@ function bento() { el("div", { class: "section-head" }, el("h2", { class: "display" }, "How it works")), el("div", { class: "bento" }, step("1", "Search", "Any name of 6 letters or more: a–z, 0–9 and single hyphens between them. The search sends only its hash and length."), - step("2", "Pay", "One price for 2 years, shown with the name.", + step("2", "Pay", "One price for 2 years, shown with the name, and 30 more days free.", el("div", { class: "pays" }, ...["card", "btc", "xmr"].map((m) => el("span", {}, methodMark(m), METHODS[m][0])))), step("3", "It's yours", "Registered to a wallet made in your browser. Its name code is the only key, and moves the name to your app."), el("article", { class: "tile wide" }, @@ -140,7 +142,7 @@ function availableResult(label, { coded = false, credit = false } = {}) { const what = el("div", { class: "what" }, nameTitle(label), el("div", { class: "meta" }, chip("Available", "ok", true), chip(`${label.length} letters`), - chip(coded ? `${APPLIED_YEARS} years` : "2 years"), coded ? chip("Code applied", "brand") : null, credit ? chip("Covered by your payment", "brand") : null), + chip(coded ? termText(APPLIED_YEARS) : termText(2)), coded ? chip("Code applied", "brand") : null, credit ? chip("Covered by your payment", "brand") : null), coded ? el("div", { class: "covered" }, "Covered by your code", el("span", { class: "mono nowrap" }, APPLIED_SHORT), "·", link("Remove")) : null); const priceBox = coded ? el("div", { class: "price" }, el("b", {}, el("s", {}, dollars(price)), "$0"), el("span", {}, `for ${APPLIED_YEARS} years`)) @@ -172,10 +174,10 @@ function searchScreen(state) { case "reserved": return [hero("support", { after: [takenResult("support", chip("Reserved", "warn", true), [el("p", { class: "note" }, "Reserved names are not sold here. If it is yours to use, contact the SimpleX team."), el("div", { style: "margin-top: 12px" }, btn("Contact the SimpleX team", "quiet"))])] }), bento()]; - case "unchecked": return [hero(NAME, { after: [under(alertBox("warn", "alert", "This name could not be checked", "The name service did not answer. Try again in a moment."))] }), bento()]; + case "unchecked": return [hero(NAME, { after: [under(el("div", {}, alertBox("warn", "alert", "This name could not be checked", "The name service did not answer."), el("div", { style: "margin-top: 14px" }, btn("Try again", "quiet", icon("refresh")))))] }), bento()]; case "limited": return [hero(NAME, { box: { off: true }, after: [under(alertBox("warn", "clock", "Try again in 42 seconds", "Too many names were searched from here. Searching is paused until then."))] }), bento()]; case "credit": return [hero("dynamos", { - banner: el("div", { class: "banner" }, icon("wallet"), el("span", {}, el("b", {}, `Your payment is kept: ${PAID}. `), `Choose another name of up to ${PAID}; it is registered with what you paid.`)), + banner: el("div", { class: "banner" }, icon("wallet"), el("span", {}, el("b", {}, `Your payment is kept: ${PAID}. `), `Choose another name of up to ${PAID}; nothing more is charged.`)), after: [availableResult("dynamos", { credit: true })] })]; case "fromApp": return [hero(NAME, { banner: el("div", { class: "banner" }, icon("phone"), el("span", {}, el("b", {}, "Opened from SimpleX. "), `The name is registered to your app's wallet, ${APP_OWNER_SHORT}.`)), @@ -255,7 +257,7 @@ function checkoutScreen(opts = {}) { let paySec; if (covered) { paySec = el("div", { class: "panel-sec" }, el("div", { class: "sec-title" }, el("h3", {}, "Payment")), - el("div", { class: "covered-box" }, icon("check"), el("div", {}, "Paid with your code", el("p", {}, `Registered for ${APPLIED_YEARS} years, with nothing to pay.`)))); + el("div", { class: "covered-box" }, icon("check"), el("div", {}, "Paid with your code", el("p", {}, `Registered for ${termText(APPLIED_YEARS)}, with nothing to pay.`)))); } else if (opts.openOrder) { paySec = el("div", { class: "panel-sec" }, el("div", { class: "sec-title" }, el("h3", {}, "Payment method")), alertBox("info", "info", "You have an order waiting to be confirmed", "A second order would be a second charge, so this one cannot start yet. When that payment is confirmed or expires, you can start a new one here."), @@ -269,7 +271,7 @@ function checkoutScreen(opts = {}) { const storage = opts.noStorage ? el("div", { class: "panel-sec" }, alertBox("warn", "alert", "This browser will not keep your name code", "The next step shows it: your saved copy will be the only one.")) : null; const main = el("div", { class: "panel" }, storage, linksSec, paySec); - const term = covered ? `${APPLIED_YEARS} years` : "2 years"; + const term = covered ? termText(APPLIED_YEARS) : termText(2); const until = covered ? CODE_UNTIL_SHORT : UNTIL_SHORT; const rows = [["Term", term], ["Registered until", `about ${until}`]]; if (opts.code !== "entry" && !opts.openOrder) rows.push(covered ? ["Code", APPLIED_SHORT] : ["Paying with", METHODS[selected][0]]); @@ -308,7 +310,7 @@ function codeScreen() { el("p", { class: "sec-note" }, "Copy it somewhere safe or write it down. You redeem it in the app later."), ...codeBlock(), el("label", { class: "check" }, el("span", { class: "box" }), "I have saved my code"))), - orderCard(NAME, [["Term", "2 years"], ["Registered until", `about ${UNTIL_SHORT}`], ["Paying with", "Bitcoin"]], + orderCard(NAME, [["Term", termText(2)], ["Registered until", `about ${UNTIL_SHORT}`], ["Paying with", "Bitcoin"]], paidTotal(), disabled(btn("Continue to payment", "lg block", icon("arrow"))), el("div", { class: "why" }, @@ -322,7 +324,7 @@ function priceChanged() { } function rateLimited() { - return status("warn", "clock", "Too many attempts", "Try again in 41 seconds. Nothing was charged.", [disabled(btn("Try again in 41 s")), btn("Back", "quiet")]); + return status("warn", "clock", "Too many attempts", "Too many orders were tried from here, so ordering is paused. Nothing was charged.", [disabled(btn("Try again in 41 seconds")), btn("Back", "quiet")]); } function invoiceFailure() { @@ -337,16 +339,16 @@ function copyField(label, value, copy = true) { function cryptoScreen({ partial = false } = {}) { const due = partial ? CRYPTO_REMAINING : CRYPTO.amount; const main = el("div", { class: "panel" }, el("div", { class: "panel-sec" }, - partial ? el("div", { style: "margin-bottom: 18px" }, alertBox("warn", "alert", "Part of the amount has arrived", `${CRYPTO.partial} of ${CRYPTO.amount} BTC is seen. Send the remaining ${CRYPTO_REMAINING} BTC to the same address.`)) : null, + partial ? el("div", { style: "margin-bottom: 18px" }, alertBox("warn", "alert", "Part of the payment arrived", `${CRYPTO.partial} of ${CRYPTO.amount} BTC is seen. Send the remaining ${CRYPTO_REMAINING} BTC to the same address.`)) : null, el("div", { class: "pay-head" }, el("div", {}, el("span", { class: "label" }, partial ? "Send the rest" : "Send"), el("div", { class: "amount" }, `${due} BTC`), el("div", { class: "usd" }, `${PAID} in total`)), chip(partial ? "Rate held 45:59" : "Rate held 59:59", "brand")), el("div", { class: "pay-body" }, el("div", { class: "qr" }, qrSvg(`bitcoin:${CRYPTO.address}?amount=${due}`, "Bitcoin payment")), el("div", {}, copyField("Amount", `${due} BTC`), copyField("Bitcoin address", CRYPTO.address), copyField("Reference", ORDER_ID, false))), - el("div", { class: "waiting" }, el("span", { class: "live" }, partial ? "Waiting for the rest of the payment" : "Waiting for the payment"), link("Cancel this invoice")))); + el("div", { class: "waiting" }, el("span", { class: "live" }, partial ? "Waiting for the rest of the payment" : "Waiting for payment"), partial ? null : link("Cancel this invoice")))); return [pageHead(3, "Pay with Bitcoin", "Bookmark this page: the address and the countdown live on its link."), - twoCols(main, orderCard(NAME, [["Term", "2 years"], ["Paying with", "Bitcoin"]], paidTotal()))]; + twoCols(main, orderCard(NAME, [["Term", termText(2)], ["Paying with", "Bitcoin"]], paidTotal()))]; } function confirmingScreen(card) { @@ -358,8 +360,8 @@ function confirmingScreen(card) { ? "Stripe usually confirms within a minute. Registration starts by itself; this page moves on." : `${CRYPTO.amount} BTC arrived. Registration starts once it confirms; this page moves on by itself.`), el("div", { class: "meter" }, el("i")), - el("div", { class: "waiting" }, el("span", { class: "live" }, "Confirming"), el("span", { class: "mono", style: "font-size: 13px; color: var(--ink-3)" }, ORDER_ID)))); - return [pageHead(3, card ? "Paying by card" : "Payment received"), twoCols(main, orderCard(NAME, [["Term", "2 years"], ["Paying with", card ? "Card" : "Bitcoin"]], paidTotal()))]; + el("div", { class: "waiting" }, el("span", { class: "live" }, "Confirming payment"), el("span", { class: "mono", style: "font-size: 13px; color: var(--ink-3)" }, ORDER_ID)))); + return [pageHead(3, card ? "Paying by card" : "Confirming your payment"), twoCols(main, orderCard(NAME, [["Term", termText(2)], ["Paying with", card ? "Card" : "Bitcoin"]], paidTotal()))]; } function cardScreen() { @@ -372,7 +374,7 @@ function cardScreen() { el("div", { class: "two" }, f("Expiry", "MM / YY"), f("CVC", "CVC")), f("Country", "Portugal")), el("div", { style: "margin-top: 18px" }, btn(`Pay ${PAID}`, "lg block")))); - return [pageHead(3, "Pay by card"), twoCols(main, orderCard(NAME, [["Term", "2 years"], ["Paying with", "Card"]], paidTotal()))]; + return [pageHead(3, "Pay by card"), twoCols(main, orderCard(NAME, [["Term", termText(2)], ["Paying with", "Card"]], paidTotal()))]; } // a single outcome, centred, as the badge store's endings are @@ -390,7 +392,7 @@ function track(states) { const stage = (st, n, title, text) => el("div", { class: `stage ${st}` }, el("span", { class: "node" }, st === "done" ? "✓" : st === "failed" ? "✕" : n), el("b", {}, title), el("span", {}, text)); return el("div", { class: "track" }, stage(states[0], "1", states[0] === "done" ? "Committed" : "Committing", "The name, hidden, goes on chain"), - stage(states[1], "2", "Waiting", "Every registration waits a minute"), + stage(states[1], "2", "Waiting", "Every registration waits after its commit"), stage(states[2], "3", states[2] === "done" ? "Registered" : "Registering", "Then it is yours")); } @@ -407,12 +409,12 @@ function registeringScreen(states) { const active = states.indexOf("active"); const box = [ el("div", { class: "countdown" }, el("b", {}, icon("clock")), el("p", {}, "Waiting for the commit to be mined. The wait starts once it is.")), - el("div", { class: "countdown" }, el("b", {}, "0:42"), el("p", {}, "The contract makes every registration wait a minute after its commit, so nobody can take a name they saw being registered.")), + el("div", { class: "countdown" }, el("b", {}, "0:42"), el("p", {}, "The contract makes every registration wait after its commit, so nobody can take a name they saw being registered.")), el("div", { class: "countdown" }, el("b", {}, icon("clock")), el("p", {}, "The register transaction is being mined; the name is yours once it is, and final 3 blocks later.")), ][active]; - return [pageHead(4, ["Registering ", el("span", { class: "grad" }, `${NAME}.simplex`)], "About two minutes. Keep this page open: after the wait it sends the name to finish."), + return [pageHead(4, ["Registering ", el("span", { class: "grad" }, `${NAME}.simplex`)], "About two minutes. Keep this page open: after the wait it signs the name to finish it."), twoCols(el("div", { class: "panel" }, el("div", { class: "panel-sec" }, track(states), box), txRows(states)), - orderCard(NAME, [["Paid", `${PAID} · Bitcoin`], ["Term", "2 years"], ["Reference", SHORT_REF]]))]; + orderCard(NAME, [["Paid", `${PAID} · Bitcoin`], ["Term", termText(2)], ["Reference", SHORT_REF]]))]; } function celebrate(label, lead) { @@ -424,13 +426,12 @@ function celebrate(label, lead) { function registeredScreen({ saved = true, years = 2, until = UNTIL } = {}) { const next = (ic, title, text, action) => el("div", { class: "item" }, el("span", { class: "ic" }, icon(ic)), el("div", {}, el("b", {}, title), el("p", {}, text), action)); - return [celebrate(NAME, `Registered for ${years} years, until ${until}, to ${OWNER_SHORT}, a wallet made in this browser.`), + return [celebrate(NAME, `Registered for ${termText(years)}, until ${until}, to ${OWNER_SHORT}, a wallet made in this browser.`), twoCols( el("div", { class: "panel" }, el("div", { class: "panel-sec" }, el("div", { class: "sec-title" }, el("h3", {}, "Your name code")), - el("p", { class: "sec-note" }, "The same code you saved at checkout. It owns the name."), + el("p", { class: "sec-note" }, "The name code it was ordered with. It owns the name."), ...codeBlock(), - el("p", { class: "sec-note", style: "margin: 12px 0 0" }, link("Show it as 12 words, for another wallet")), saved ? alertBox("warn", "shield", null, "Whoever has this code controls the name. Nobody else has a copy, not even us.") : alertBox("bad", "alert", "This code could not be saved in this browser", "The copy you saved before paying is the only one."))), @@ -567,9 +568,9 @@ const SCREENS = { N3h: () => codeScreen(), N4: () => cryptoScreen(), N4a: () => cryptoScreen({ partial: true }), - N4b: () => status("info", "x", "Invoice cancelled", "This invoice was cancelled and nothing was received.", [btn("Get a new invoice")], { reference: true }), - N4c: () => status("info", "clock", "This invoice expired", "Nothing was received before its rate window closed.", [btn("Get a new invoice")], { reference: true }), - N4d: () => status("warn", "alert", "This invoice expired", `${CRYPTO.partial} BTC arrived, which is not the full amount. The rate window has closed; quote the reference below and we will sort it out.`, [btn("Get a new invoice", "quiet")], { reference: true }), + N4b: () => status("info", "x", "Invoice cancelled", "This invoice was cancelled and nothing was received.", [btn("Order the name again")], { reference: true }), + N4c: () => status("info", "clock", "Invoice expired", "Nothing was received before its rate window closed.", [btn("Get a new invoice")], { reference: true }), + N4d: () => status("warn", "alert", "Invoice expired", `${CRYPTO.partial} BTC arrived, which is not the full amount. Write to chat@simplex.chat with the reference below and we will sort it out.`, [btn("Get a new invoice", "quiet")], { reference: true }), N5: () => confirmingScreen(false), N6: () => registeringScreen(["active", "", ""]), N6a: () => registeringScreen(["done", "active", ""]), @@ -583,7 +584,7 @@ const SCREENS = { N8b: () => takenCode(), N9: () => failedAfterPaying(), N10: () => cardScreen(), - N10a: () => status("warn", "alert", "The card form did not load", "Stripe's form was blocked or failed to load. Nothing was charged.", [btn("Try again"), btn("Get a new invoice", "quiet")], { reference: true }), + N10a: () => status("warn", "alert", "The card form did not load", "Stripe's form was blocked or failed to load. Nothing was charged.", [btn("Load the card form again"), btn("Get a new invoice", "quiet")], { reference: true }), N10b: () => confirmingScreen(true), N10c: () => status("warn", "clock", "This is taking longer than expected", "The payment has not been confirmed. This page keeps working: come back to it later, or quote the reference below.", [btn("Check again")], { reference: true }), N11: () => status("info", "link", "This link does not work", "It names no order this service knows. Check the link, or start again.", [btn("Search a name")]), diff --git a/plans/names-codes/mockups/layout.mjs b/plans/names-codes/mockups/layout.mjs index 625858c3a5..646599f4c1 100644 --- a/plans/names-codes/mockups/layout.mjs +++ b/plans/names-codes/mockups/layout.mjs @@ -95,7 +95,7 @@ export const FRAMES = { N3: { url: `${HOST}/#/checkout`, from: "N2", label: `Register ${NAME}.simplex`, color: "blue", title: "Check your order, and pay", - text: "Step 2 of 5. The browser makes the name's wallet: 128-bit entropy, shown as a name code (SN- and 30 characters: the entropy and 22 bits of its SHA-256), the key at m/44'/60'/0'/0/0 of the BIP-39 phrase with that entropy. The order carries the address, the name's hash and length, the commitment, and the name and links sealed under a key from the code; never the plain name. Optional links, the payment method, and the order card with Continue, I have a code and the investor line.", + text: "Step 2 of 5. The browser makes the name's wallet: 128-bit entropy, shown as a name code (SN- and 30 characters: the entropy and 22 bits of its SHA-256), the key at m/44'/60'/0'/0/0 of the BIP-39 seed from that entropy (the phrase is never shown). The order carries the address, the name's hash and length, the commitment, and the name and links sealed under a key from the code; never the plain name. Optional links, the payment method, and the order card with Continue, I have a code and the investor line.", }, N4: { url: ORDER, from: "N3h", label: "code saved: Bitcoin", color: "blue", @@ -104,7 +104,7 @@ export const FRAMES = { }, N5: { url: ORDER, from: "N4", label: "the full amount arrives", color: "blue", - title: "Payment received", + title: "Confirming your payment", text: "Settlement hands the order to the registration worker; the page moves on by itself.", }, N6: { @@ -145,7 +145,7 @@ export const FRAMES = { N2b: { url: HOST, from: "N1a", label: "taken, linked to nothing", color: "orange", title: "Taken, pointing nowhere", - text: "Registered without links: a name, bought here or elsewhere, whose owner has not set records yet, or one this service is registering or registered within the last hour, before the resolver has it. Nothing is held before that.", + text: "Registered without links: a name, bought here or elsewhere, whose owner has not set records yet, or one this service is registering, while its transaction is being mined. Nothing is held before that.", }, N2c: { url: HOST, from: "N1a", label: "reserved", color: "orange", @@ -153,7 +153,7 @@ export const FRAMES = { text: "With a way to reach the SimpleX team, as the app does.", }, N2d: { - url: HOST, from: "N1a", label: "503: the resolver is down", color: "orange", + url: HOST, from: "N1a", label: "503: the chain cannot be read", color: "orange", title: "Not checked", text: "Nothing goes forward on a guess.", }, @@ -273,7 +273,7 @@ export const FRAMES = { text: "A second order would be a second charge, so Continue is off while a submitted card payment awaits confirmation, until it settles or expires.", }, N4a: { url: ORDER, from: "N4", label: "less than the amount", color: "orange", title: "Part of the amount", text: "The rest to send, with the rate's remaining time." }, - N4b: { url: ORDER, from: "N4", label: "Cancel this invoice", color: "orange", title: "Cancelled", text: "Get a new invoice makes a new order for the same name and wallet." }, + N4b: { url: ORDER, from: "N4", label: "Cancel this invoice", color: "orange", title: "Cancelled", text: "Order the name again leads back to the name's search result, to order it anew and choose how to pay; nothing is ordered until then." }, N4c: { url: ORDER, from: "N4", label: "the window closes, nothing sent", color: "orange", title: "Expired, nothing received", text: "Nothing was received." }, N4d: { url: ORDER, from: "N4a", label: "the window closes", color: "orange", title: "Expired, part paid", text: "The buyer quotes the reference." }, N10: { @@ -358,7 +358,7 @@ export const FRAMES = { export const NOTES = [ "Design document, not screenshots of a build: the names screens are rendered from mockups/domains.js on simplex.domains' fonts, logos and brand blue, with a palette of its own, prototypes for the names app in that repo; the badges landing is the real badges webapp.", "Two stores, two webapps: names at simplex.domains, from its own repo (a submodule of simplex-chat); badges at badges.simplex.chat. One service (StoreService) exports both; one library (@simplex-chat/store) holds the logic they share.", - "Non-custodial: the browser makes each name's wallet (128-bit entropy, shown as an SN- name code; the key at m/44'/60'/0'/0/0 of its BIP-39 phrase) with vendored @noble/secp256k1 and @noble/hashes, and sends the address and a commitment, revealing the name only after the commit; the service pays for and sends commit and register with that owner and never owns the name.", + "Non-custodial: the browser makes each name's wallet (128-bit entropy, shown as an SN- name code; the key at m/44'/60'/0'/0/0 of its BIP-39 seed; no phrase is shown) with vendored @noble/secp256k1 and @noble/hashes, and sends the address and a commitment, revealing the name only after the commit; the service pays for and sends commit and register with that owner and never owns the name.", "A paid name registers for 2 years, a code for its own term; the price is the service's answer for the searched name. Codes (SN6-, SN7-, SN20-, ...) come from the operator, the app (not yet in #7530) or, for a taken order, the page, derived from the name code; never sold here; the service's table is their truth.", "Regenerate after npm run build in apps/simplex-badge-service/web (apps/simplex-store-service/web after the rename), with a simplex.domains checkout beside this repo: PLAYWRIGHT= node plans/names-codes/mockups/board.mjs", ]; diff --git a/plans/names-codes/names-flow.svg b/plans/names-codes/names-flow.svg index b9eb5a89b9..cd04e392cf 100644 --- a/plans/names-codes/names-flow.svg +++ b/plans/names-codes/names-flow.svg @@ -1,6 +1,6 @@ - + - + SimpleX names - the store at simplex.domains, every screen one tree per surface, left to right - blue is the normal path, orange a variant or a failure, the dashed arrow hands off from the site to the app, and app screens are outlined in grey dashes @@ -8,14 +8,14 @@ generated by mockups/board.mjs - - - - - - - - + + + + + + + + @@ -38,77 +38,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simplex.domains the names store: search a name, pay, and the name is registered to a wallet made in your browser Paying with a code the service's table says what a code covers, and a covered name costs $0 -When registering does not go through -the payment is never lost: it registers another name, becomes a code, or is retried at no charge -Opened from the app -the app's link carries the name and its own owner address: no name code, no links, no "I have a code"; the app has to sign the registration intent (open) -Where it meets the app -redeeming the code moves the name to the app's wallet, both signatures relayed by the service; drawn as the app might, not built here -Refused at checkout -nothing is charged; each ends on the names store's own pages -The other ways a payment ends -the same endings as the badges store, on the names store's own pages; the last row is about the name code, and a link to nothing -Your names, the badges store, and the dark theme -each store keeps its own history, on its own host; the badges store only gains a link here -simplex.domains on a phone -the same screens at 390 px -For the operator -codes issued by hand, as for badges; they are not sold on the web +When registering does not go through +the payment is never lost: it registers another name, becomes a code, or is retried at no charge +Opened from the app +the app's link carries the name and its own owner address: no name code, no links, no "I have a code"; the app has to sign the registration intent (open) +Where it meets the app +redeeming the code moves the name to the app's wallet, both signatures relayed by the service; drawn as the app might, not built here +Refused at checkout +nothing is charged; each ends on the names store's own pages +The other ways a payment ends +the same endings as the badges store, on the names store's own pages; the last row is about the name code, and a link to nothing +Your names, the badges store, and the dark theme +each store keeps its own history, on its own host; the badges store only gains a link here +simplex.domains on a phone +the same screens at 390 px +For the operator +codes issued by hand, as for badges; they are not sold on the web Registering a name, start to finish What a search can find Before paying: links, then the name code Check your code, then register for $0 -A code at checkout, and codes that do not work -After the payment -The app sends the buyer here to pay -Redeeming the name code -What stops an order at checkout -Paying in Bitcoin or Monero -Paying by card -A code this browser cannot show, and a link to nothing -History, the other store, then three screens dark -Registering a name at phone width -The terminal and the managed group +A code at checkout, and codes that do not work +After the payment +The app sends the buyer here to pay +Redeeming the name code +What stops an order at checkout +Paying in Bitcoin or Monero +Paying by card +A code this browser cannot show, and a link to nothing +History, the other store, then three screens dark +Registering a name at phone width +The terminal and the managed group @@ -116,7 +116,7 @@ simplex.domains - + N1. Search The first screen: a frosted header, the headline, and one search bar ending in a .simplex tag and Search; Enter searches @@ -129,7 +129,7 @@ simplex.domains - + N1a. Typed Nothing is sent while typing. Search sends keccak(name) and its length, GET /api/name/:hash?length=7: the service never sees a @@ -141,7 +141,7 @@ simplex.domains - + N2. Available, with its price The answer right under the search: the name, chips for its length and term, and the service's price for 2 years; prices @@ -154,17 +154,17 @@ simplex.domains/#/checkout - + N3. Check your order, and pay Step 2 of 5. The browser makes the name's wallet: 128-bit entropy, shown as a name code (SN- and 30 characters: the entropy and 22 bits of its SHA-256), the key at -m/44'/60'/0'/0/0 of the BIP-39 phrase with that entropy. The -order carries the address, the name's hash and length, the -commitment, and the name and links sealed under a key from the -code; never the plain name. Optional links, the payment method, -and the order card with Continue, I have a code and the -investor line. +m/44'/60'/0'/0/0 of the BIP-39 seed from that entropy (the +phrase is never shown). The order carries the address, the +name's hash and length, the commitment, and the name and links +sealed under a key from the code; never the plain name. +Optional links, the payment method, and the order card with +Continue, I have a code and the investor line. @@ -172,7 +172,7 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - + N4. Paying in crypto Step 4 of 5: amount, QR, address and reference, the rate held for an hour. The payment logic is the shared package's; this @@ -184,8 +184,8 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N5. Payment received + +N5. Confirming your payment Settlement hands the order to the registration worker; the page moves on by itself. @@ -195,7 +195,7 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - + N6. Committing Step 5 of 5: the app's steps (canvas 9), run by the service from its own funded key, with the buyer's address as owner. The @@ -207,7 +207,7 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - + N6a. Waiting a minute The controller's minCommitmentAge. Then this page reveals the registration and signs the owner's RegisterName intent, so it @@ -220,7 +220,7 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - + N6b. Registering After the reveal, the register transaction, dry-run first; registered at its receipt plus 3 blocks. Search answers @@ -232,7 +232,7 @@ simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - + N7. Yours, with its name code The name code owns the name; the QR carries it to the app. Next steps: redeem in the app, point the name at an address, keep @@ -244,7 +244,7 @@ simplex.domains - + N1b. Too short Judged when searching, not while typing: the rules line appears only now, after the mistake. Editing the name and searching @@ -256,7 +256,7 @@ simplex.domains - + N1c. Not a name Judged on the page; upper case is folded as typed, not refused. @@ -266,7 +266,7 @@ simplex.domains - + N2a. Taken: what uses it The record's address and channel, as canvas 3b. The display name and picture, read by the service's chat core @@ -279,12 +279,12 @@ simplex.domains - + N2b. Taken, pointing nowhere Registered without links: a name, bought here or elsewhere, whose owner has not set records yet, or one this service is -registering or registered within the last hour, before the -resolver has it. Nothing is held before that. +registering, while its transaction is being mined. Nothing is +held before that. @@ -292,19 +292,19 @@ simplex.domains - + N2c. Reserved With a way to reach the SimpleX team, as the app does. - + simplex.domains - -N2d. Not checked -Nothing goes forward on a guess. + +N2d. Not checked +Nothing goes forward on a guess. @@ -312,7 +312,7 @@ simplex.domains - + N2e. Too many searches Search stays off for Retry-After. @@ -322,7 +322,7 @@ simplex.domains/#/checkout - + N3g. Linking at registration Optional. Each link is previewed through POST /api/link/preview, so a typo shows before paying. The page puts @@ -336,7 +336,7 @@ simplex.domains/#/checkout - + N3h. The name code before paying Shown before the order exists, and Continue waits for the checkbox: if the browser crashes or loses its storage after @@ -349,24 +349,24 @@ simplex.domains - + K1. Check your code A popup over the page: POST /api/code/check says what the prepaid code covers and that it is unused, spending nothing. This is an investor code, SN6- for names of 6+ letters and 5 years; the service's table, not the text, decides. - + simplex.domains - -K2. Covered: $0 -While the prepaid code is applied, a name it covers shows $0 -and the code's own term, 5 years here, with Remove beside the -code. + +K2. Covered: $0 +While the prepaid code is applied, a name it covers shows $0 +and the code's own term, 5 years here, with Remove beside the +code. @@ -374,7 +374,7 @@ simplex.domains/#/checkout - + K3. Checkout at $0 No payment: the method section reads Paid with your code. After the name code (N3h), POST /api/name/register reserves the @@ -388,489 +388,489 @@ simplex.domains/?registration=r4Hq9TzW2mXc7LpN5vBd8g - + K8. Yours for the code's term The registered screen with the prepaid code's years: 5 for this investor code. The name code is handled as in N7. - - - - - - -simplex.domains - -K5. Already used -Each code registers one name. - - - - - - -simplex.domains - -K6. Not valid -The check character catches a typo before any request. - - - - - - -simplex.domains/#/checkout - -K4. A code at checkout -I have a code opens the entry in the order card; a covering -code takes the total to $0. - - - - - - -simplex.domains/#/checkout - -K7. Does not cover this name -The code covers 8+ letters and the name has 7; the total stays -and the buyer can still pay. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N9. Paid, not registered -Drawn for a commit the service's retries gave up on. A lapse, -the page not open to reveal within 24 hours of the commit, -shows the same screen with the waiting step failed and that -reason; Try again (POST /api/registration/:id/retry) charges -nothing (canvas 10b). Refunds are manual. A code order whose -retry finds its code used, held by another registration, -revoked or expired shows the same screen with that outcome (not -drawn). - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N8. Someone was faster -Another registrar, or another order here, registered it first: -nothing is held. Usually the register dry run catches it and -nothing is sent; if both land in one block, the mined register -reverts. The payment is kept (canvas 10a). A code order ends -here too, its code released unspent (not drawn). - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N8a. Another name, same payment -Search on the order's own page: any name at or under what was -paid, committed and revealed again for the same wallet, so the -name code owns the new name. A cheaper one refunds nothing; a -dearer one cannot be registered. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N8b. A code instead -A code for the taken name's length (8+ for longer names), for 2 -years. The page derives it from the name code, so the name code -recovers it, and keeps it under Your names; the service keeps -only its hash. - - - - - - -simplex.domains/#/?name=dynamis&owner=0x2F0b…94aE - -L1. Opened from the app -The app passes the name it searched and its own account's -address, shown here and at checkout so a crafted link cannot -pass unnoticed. The page makes no wallet. - - - - - - -simplex.domains/#/checkout - -L2. Checkout, without I have a code -The app redeems prepaid codes and sets links itself, so this -flow offers no links section and no I have a code, only card, -Bitcoin and Monero. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -L3. Back to SimpleX -Registered straight to the app's account: nothing to import. - -SimpleX app - -A1. Redeeming in the app -Not built here: the app reads the -name code and finds its names on -chain (the base registrar is -enumerable); the name moves to the -app's wallet as the code's wallet -signs ApproveName and the app's -wallet ClaimName (PR #35), the -service relaying approveWithSig -and claimWithSig. - -SimpleX app - -A2. The name is in the app -After canvas 10's alert, retitled -"The name is in your app" and -without its "points at" line and -calendar button: the app shows the -name it has just redeemed before -reading its records; from here the -app's wallet owns it and sets its -links. - - - - - - -simplex.domains/#/checkout - -N3a. A method is unavailable -Shown on the tile, which cannot be chosen; another method is -selected. - - - - - - -simplex.domains/#/checkout - -N3b. The price changed -Nothing was charged; Search again shows the new price. - - - - - - -simplex.domains/#/checkout - -N3c. Too many attempts -Try again stays off for Retry-After. - - - - - - -simplex.domains/#/checkout - -N3d. That did not go through -Nothing was charged. An order may still have been created: Try -again resends the same body, and a paid order gets its open -invoice back, a code order its registration; later, Restore -with a name code finds either. - - - - - - -simplex.domains/#/checkout - -N3e. An order is already waiting -A second order would be a second charge, so Continue is off -while a submitted card payment awaits confirmation, until it -settles or expires. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N4a. Part of the amount -The rest to send, with the rate's remaining time. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N4b. Cancelled -Get a new invoice makes a new order for the same name and -wallet. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N4c. Expired, nothing received -Nothing was received. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N4d. Expired, part paid -The buyer quotes the reference. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N10. Paying by card -Stripe's Payment Element, drawn as a stand-in, beside the order -card. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N10a. The card form did not load -Try again, or a new invoice. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N10b. Card submitted -Once paid, registration starts at N6. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N10c. Taking longer than expected -The page keeps working; the reference is for support. - - - - - - -simplex.domains/#/checkout - -N3f. The code will not be kept -Checked before the order is created. The buyer can still pay; -the code lives in this page and in the copy saved at the next -step. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N7a. The code could not be saved -Storage was refused at checkout, which warned ("This browser -will not keep your name code"); the copy saved before paying is -the only one. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -N7b. Not on this device -The order link opened where the code is not stored: the page -asks for the saved code, as N7c does (the order's sealed copy -supplies the name once the code opens it) and carries on, -revealing if the registration waits for it. - - - - - - -simplex.domains/#/names - -N7c. Restore with a name code -The page derives the owner and commitment secret from the code, -checks the owner matches, and opens the order's sealed name and -links. From Your names (Restore with a name code) it finds the -code's orders with POST /api/registration/find, signed by the -code's key; no name is asked. - - - - - - -simplex.domains/?order=k2Vn8… - -N11. This link does not work -Search a name starts again. - - - - - - -simplex.domains/#/names - -N12. Your names -Seen a little after the dynamis order completed. Names with -their status, where they point, Show code and Edit links; -registrations in progress; prepaid codes. Add a code and -Restore with a name code sit above. Each store keeps its own -history, on its own host. - - - - - - -simplex.domains/#/names - -N12a. Add a code -Asks the service what a code covers and adds it to the list, -where Use applies it to a search. - - - - - - -badges.simplex.chat - -B0. The badges store -Unchanged but for one link to simplex.domains; the names store -links back from its header. - - - - - - -simplex.domains/#/names - -N12b. Changing links later -For each changed key the page signs an EIP-712 SetText with the -name's key and POST /api/name/records relays -SimplexResolver.setTextWithSig (PR #35), the service paying the -gas and capping edits itself. - - - - - - -simplex.domains - -D2. N2, dark -The dark theme follows simplex.domains: navy ground, cyan -accents. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -D6a. N6a, dark -The progress track on the dark ground. - - - - - - -simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A - -D7. N7, dark -The QR keeps its white ground, so it still scans. - -simplex.domains - -M2. N2 at 390 px -The search bar keeps its button as -an arrow; the result stacks. - -simplex.domains/#/checkout - -M3. N3 at 390 px -One column; the order card follows -the details. - -simplex.domains/?order=q7ZpL2dN9xWc4… - + + + + + + +simplex.domains + +K5. Already used +Each code registers one name. + + + + + + +simplex.domains + +K6. Not valid +The check character catches a typo before any request. + + + + + + +simplex.domains/#/checkout + +K4. A code at checkout +I have a code opens the entry in the order card; a covering +code takes the total to $0. + + + + + + +simplex.domains/#/checkout + +K7. Does not cover this name +The code covers 8+ letters and the name has 7; the total stays +and the buyer can still pay. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N9. Paid, not registered +Drawn for a commit the service's retries gave up on. A lapse, +the page not open to reveal within 24 hours of the commit, +shows the same screen with the waiting step failed and that +reason; Try again (POST /api/registration/:id/retry) charges +nothing (canvas 10b). Refunds are manual. A code order whose +retry finds its code used, held by another registration, +revoked or expired shows the same screen with that outcome (not +drawn). + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N8. Someone was faster +Another registrar, or another order here, registered it first: +nothing is held. Usually the register dry run catches it and +nothing is sent; if both land in one block, the mined register +reverts. The payment is kept (canvas 10a). A code order ends +here too, its code released unspent (not drawn). + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N8a. Another name, same payment +Search on the order's own page: any name at or under what was +paid, committed and revealed again for the same wallet, so the +name code owns the new name. A cheaper one refunds nothing; a +dearer one cannot be registered. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N8b. A code instead +A code for the taken name's length (8+ for longer names), for 2 +years. The page derives it from the name code, so the name code +recovers it, and keeps it under Your names; the service keeps +only its hash. + + + + + + +simplex.domains/#/?name=dynamis&owner=0x2F0b…94aE + +L1. Opened from the app +The app passes the name it searched and its own account's +address, shown here and at checkout so a crafted link cannot +pass unnoticed. The page makes no wallet. + + + + + + +simplex.domains/#/checkout + +L2. Checkout, without I have a code +The app redeems prepaid codes and sets links itself, so this +flow offers no links section and no I have a code, only card, +Bitcoin and Monero. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +L3. Back to SimpleX +Registered straight to the app's account: nothing to import. + +SimpleX app + +A1. Redeeming in the app +Not built here: the app reads the +name code and finds its names on +chain (the base registrar is +enumerable); the name moves to the +app's wallet as the code's wallet +signs ApproveName and the app's +wallet ClaimName (PR #35), the +service relaying approveWithSig +and claimWithSig. + +SimpleX app + +A2. The name is in the app +After canvas 10's alert, retitled +"The name is in your app" and +without its "points at" line and +calendar button: the app shows the +name it has just redeemed before +reading its records; from here the +app's wallet owns it and sets its +links. + + + + + + +simplex.domains/#/checkout + +N3a. A method is unavailable +Shown on the tile, which cannot be chosen; another method is +selected. + + + + + + +simplex.domains/#/checkout + +N3b. The price changed +Nothing was charged; Search again shows the new price. + + + + + + +simplex.domains/#/checkout + +N3c. Too many attempts +Try again stays off for Retry-After. + + + + + + +simplex.domains/#/checkout + +N3d. That did not go through +Nothing was charged. An order may still have been created: Try +again resends the same body, and a paid order gets its open +invoice back, a code order its registration; later, Restore +with a name code finds either. + + + + + + +simplex.domains/#/checkout + +N3e. An order is already waiting +A second order would be a second charge, so Continue is off +while a submitted card payment awaits confirmation, until it +settles or expires. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N4a. Part of the amount +The rest to send, with the rate's remaining time. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N4b. Cancelled +Get a new invoice makes a new order for the same name and +wallet. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N4c. Expired, nothing received +Nothing was received. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N4d. Expired, part paid +The buyer quotes the reference. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N10. Paying by card +Stripe's Payment Element, drawn as a stand-in, beside the order +card. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N10a. The card form did not load +Try again, or a new invoice. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N10b. Card submitted +Once paid, registration starts at N6. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N10c. Taking longer than expected +The page keeps working; the reference is for support. + + + + + + +simplex.domains/#/checkout + +N3f. The code will not be kept +Checked before the order is created. The buyer can still pay; +the code lives in this page and in the copy saved at the next +step. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N7a. The code could not be saved +Storage was refused at checkout, which warned ("This browser +will not keep your name code"); the copy saved before paying is +the only one. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +N7b. Not on this device +The order link opened where the code is not stored: the page +asks for the saved code, as N7c does (the order's sealed copy +supplies the name once the code opens it) and carries on, +revealing if the registration waits for it. + + + + + + +simplex.domains/#/names + +N7c. Restore with a name code +The page derives the owner and commitment secret from the code, +checks the owner matches, and opens the order's sealed name and +links. From Your names (Restore with a name code) it finds the +code's orders with POST /api/registration/find, signed by the +code's key; no name is asked. + + + + + + +simplex.domains/?order=k2Vn8… + +N11. This link does not work +Search a name starts again. + + + + + + +simplex.domains/#/names + +N12. Your names +Seen a little after the dynamis order completed. Names with +their status, where they point, Show code and Edit links; +registrations in progress; prepaid codes. Add a code and +Restore with a name code sit above. Each store keeps its own +history, on its own host. + + + + + + +simplex.domains/#/names + +N12a. Add a code +Asks the service what a code covers and adds it to the list, +where Use applies it to a search. + + + + + + +badges.simplex.chat + +B0. The badges store +Unchanged but for one link to simplex.domains; the names store +links back from its header. + + + + + + +simplex.domains/#/names + +N12b. Changing links later +For each changed key the page signs an EIP-712 SetText with the +name's key and POST /api/name/records relays +SimplexResolver.setTextWithSig (PR #35), the service paying the +gas and capping edits itself. + + + + + + +simplex.domains + +D2. N2, dark +The dark theme follows simplex.domains: navy ground, cyan +accents. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +D6a. N6a, dark +The progress track on the dark ground. + + + + + + +simplex.domains/?order=q7ZpL2dN9xWc4KfR8tYb1A + +D7. N7, dark +The QR keeps its white ground, so it still scans. + +simplex.domains + +M2. N2 at 390 px +The search bar keeps its button as +an arrow; the result stacks. + +simplex.domains/#/checkout + +M3. N3 at 390 px +One column; the order card follows +the details. + +simplex.domains/?order=q7ZpL2dN9xWc4… + M6a. N6a at 390 px The track and the countdown at phone width. - -simplex.domains/?order=q7ZpL2dN9xWc4… - -M7. N7 at 390 px -The code, then the next steps. - -simplex.domains - -M2a. N2a at 390 px -Links hide; pictures, names and -Open stay. - - -OP1. Issuing by hand -//issue name <length> [years <Y>] [paid|unpaid|free]: every -prepaid code covers a length, SN6- for investors (5 years here); 2 -years by default and at least 2. //revoke takes a badge code or a -prepaid code. - - -OP2. Issuing in the group -/issue name and /bulk name for moderators -and above; every member reads what is -posted. + +simplex.domains/?order=q7ZpL2dN9xWc4… + +M7. N7 at 390 px +The code, then the next steps. + +simplex.domains + +M2a. N2a at 390 px +Links hide; pictures, names and +Open stay. + + +OP1. Issuing by hand +//issue name <length> [years <Y>] [paid|unpaid|free]: every +prepaid code covers a length, SN6- for investors (5 years here); 2 +years by default and at least 2. //revoke takes a badge code or a +prepaid code. + + +OP2. Issuing in the group +/issue name and /bulk name for moderators +and above; every member reads what is +posted. simplex.domains types a name Search: available @@ -886,7 +886,7 @@ taken, and linked taken, linked to nothing reserved -503: the resolver is down +503: the chain cannot be read 429 Point it at your SimpleX Continue @@ -894,56 +894,56 @@ Use this code Register with code the steps finish, as N6-N7 -a used code -a mistyped code -I have a code -a code for longer names -the commit fails, or the reveal comes too late -the register dry run: no longer available -Choose another name -Take a code -the app's link -Register -paid, then the steps as N6 -scan the code with the app -Redeem -503 provider_unavailable -catalog_changed -429 -any other error at checkout -a card payment awaits confirmation -less than the amount -Cancel this invoice -the window closes, nothing sent -the window closes -code saved: Card -Stripe.js does not load -the card is accepted -fifteen minutes, still open -this browser refuses storage -paid anyway, then registered -an order opened in another browser -Restore with a name code -?order= names no invoice -Your names, in the header -Add a code -SimpleX badges, in the header -Edit links -dark theme -dark theme -dark theme -a phone -Register -paid -registered -another name: taken -the operator's terminal, --run-cli -the managed group - -Design notes -Design document, not screenshots of a build: the names screens are rendered from mockups/domains.js on simplex.domains' fonts, logos and brand blue, with a palette of its own, prototypes for the names app in that repo; the badges landing is the real badges webapp. -Two stores, two webapps: names at simplex.domains, from its own repo (a submodule of simplex-chat); badges at badges.simplex.chat. One service (StoreService) exports both; one library (@simplex-chat/store) holds the logic they share. -Non-custodial: the browser makes each name's wallet (128-bit entropy, shown as an SN- name code; the key at m/44'/60'/0'/0/0 of its BIP-39 phrase) with vendored @noble/secp256k1 and @noble/hashes, and sends the address and a commitment, revealing the name only after the commit; the service pays for and sends commit and register with that owner and never owns the name. -A paid name registers for 2 years, a code for its own term; the price is the service's answer for the searched name. Codes (SN6-, SN7-, SN20-, ...) come from the operator, the app (not yet in #7530) or, for a taken order, the page, derived from the name code; never sold here; the service's table is their truth. -Regenerate after npm run build in apps/simplex-badge-service/web (apps/simplex-store-service/web after the rename), with a simplex.domains checkout beside this repo: PLAYWRIGHT=<playwright index.mjs> node plans/names-codes/mockups/board.mjs +a used code +a mistyped code +I have a code +a code for longer names +the commit fails, or the reveal comes too late +the register dry run: no longer available +Choose another name +Take a code +the app's link +Register +paid, then the steps as N6 +scan the code with the app +Redeem +503 provider_unavailable +catalog_changed +429 +any other error at checkout +a card payment awaits confirmation +less than the amount +Cancel this invoice +the window closes, nothing sent +the window closes +code saved: Card +Stripe.js does not load +the card is accepted +fifteen minutes, still open +this browser refuses storage +paid anyway, then registered +an order opened in another browser +Restore with a name code +?order= names no invoice +Your names, in the header +Add a code +SimpleX badges, in the header +Edit links +dark theme +dark theme +dark theme +a phone +Register +paid +registered +another name: taken +the operator's terminal, --run-cli +the managed group + +Design notes +Design document, not screenshots of a build: the names screens are rendered from mockups/domains.js on simplex.domains' fonts, logos and brand blue, with a palette of its own, prototypes for the names app in that repo; the badges landing is the real badges webapp. +Two stores, two webapps: names at simplex.domains, from its own repo (a submodule of simplex-chat); badges at badges.simplex.chat. One service (StoreService) exports both; one library (@simplex-chat/store) holds the logic they share. +Non-custodial: the browser makes each name's wallet (128-bit entropy, shown as an SN- name code; the key at m/44'/60'/0'/0/0 of its BIP-39 seed; no phrase is shown) with vendored @noble/secp256k1 and @noble/hashes, and sends the address and a commitment, revealing the name only after the commit; the service pays for and sends commit and register with that owner and never owns the name. +A paid name registers for 2 years, a code for its own term; the price is the service's answer for the searched name. Codes (SN6-, SN7-, SN20-, ...) come from the operator, the app (not yet in #7530) or, for a taken order, the page, derived from the name code; never sold here; the service's table is their truth. +Regenerate after npm run build in apps/simplex-badge-service/web (apps/simplex-store-service/web after the rename), with a simplex.domains checkout beside this repo: PLAYWRIGHT=<playwright index.mjs> node plans/names-codes/mockups/board.mjs \ No newline at end of file diff --git a/plans/names-codes/screens/D2.jpg b/plans/names-codes/screens/D2.jpg index d776edaea4..261d19f1f4 100644 Binary files a/plans/names-codes/screens/D2.jpg and b/plans/names-codes/screens/D2.jpg differ diff --git a/plans/names-codes/screens/D6a.jpg b/plans/names-codes/screens/D6a.jpg index cab26e871c..b3189dacd1 100644 Binary files a/plans/names-codes/screens/D6a.jpg and b/plans/names-codes/screens/D6a.jpg differ diff --git a/plans/names-codes/screens/D7.jpg b/plans/names-codes/screens/D7.jpg index fb8bcc58ad..e84c185bc6 100644 Binary files a/plans/names-codes/screens/D7.jpg and b/plans/names-codes/screens/D7.jpg differ diff --git a/plans/names-codes/screens/K1.jpg b/plans/names-codes/screens/K1.jpg index 2f4dc6ffa4..a6da39375e 100644 Binary files a/plans/names-codes/screens/K1.jpg and b/plans/names-codes/screens/K1.jpg differ diff --git a/plans/names-codes/screens/K2.jpg b/plans/names-codes/screens/K2.jpg index ab79968363..882be45839 100644 Binary files a/plans/names-codes/screens/K2.jpg and b/plans/names-codes/screens/K2.jpg differ diff --git a/plans/names-codes/screens/K3.jpg b/plans/names-codes/screens/K3.jpg index f02acebd72..20daf0ff37 100644 Binary files a/plans/names-codes/screens/K3.jpg and b/plans/names-codes/screens/K3.jpg differ diff --git a/plans/names-codes/screens/K4.jpg b/plans/names-codes/screens/K4.jpg index 325663808e..fdf6be9182 100644 Binary files a/plans/names-codes/screens/K4.jpg and b/plans/names-codes/screens/K4.jpg differ diff --git a/plans/names-codes/screens/K5.jpg b/plans/names-codes/screens/K5.jpg index 412ab0bf90..42c932d398 100644 Binary files a/plans/names-codes/screens/K5.jpg and b/plans/names-codes/screens/K5.jpg differ diff --git a/plans/names-codes/screens/K6.jpg b/plans/names-codes/screens/K6.jpg index 1d053cfe9e..0dfaac3d46 100644 Binary files a/plans/names-codes/screens/K6.jpg and b/plans/names-codes/screens/K6.jpg differ diff --git a/plans/names-codes/screens/K7.jpg b/plans/names-codes/screens/K7.jpg index a7366a1b4b..44566f7be0 100644 Binary files a/plans/names-codes/screens/K7.jpg and b/plans/names-codes/screens/K7.jpg differ diff --git a/plans/names-codes/screens/K8.jpg b/plans/names-codes/screens/K8.jpg index c189cabb8f..a5282ddd82 100644 Binary files a/plans/names-codes/screens/K8.jpg and b/plans/names-codes/screens/K8.jpg differ diff --git a/plans/names-codes/screens/L1.jpg b/plans/names-codes/screens/L1.jpg index 4014fac1b5..ba71421443 100644 Binary files a/plans/names-codes/screens/L1.jpg and b/plans/names-codes/screens/L1.jpg differ diff --git a/plans/names-codes/screens/L2.jpg b/plans/names-codes/screens/L2.jpg index 025575bbd4..2f93da5df1 100644 Binary files a/plans/names-codes/screens/L2.jpg and b/plans/names-codes/screens/L2.jpg differ diff --git a/plans/names-codes/screens/L3.jpg b/plans/names-codes/screens/L3.jpg index a833b0133f..1f3125eeb0 100644 Binary files a/plans/names-codes/screens/L3.jpg and b/plans/names-codes/screens/L3.jpg differ diff --git a/plans/names-codes/screens/M2.jpg b/plans/names-codes/screens/M2.jpg index 477d041066..fadefb1862 100644 Binary files a/plans/names-codes/screens/M2.jpg and b/plans/names-codes/screens/M2.jpg differ diff --git a/plans/names-codes/screens/M2a.jpg b/plans/names-codes/screens/M2a.jpg index 3d1f0a05b1..ee45ef18dd 100644 Binary files a/plans/names-codes/screens/M2a.jpg and b/plans/names-codes/screens/M2a.jpg differ diff --git a/plans/names-codes/screens/M3.jpg b/plans/names-codes/screens/M3.jpg index 8220ee4cf3..3301c419c3 100644 Binary files a/plans/names-codes/screens/M3.jpg and b/plans/names-codes/screens/M3.jpg differ diff --git a/plans/names-codes/screens/M6a.jpg b/plans/names-codes/screens/M6a.jpg index a581e09bb0..27b4443402 100644 Binary files a/plans/names-codes/screens/M6a.jpg and b/plans/names-codes/screens/M6a.jpg differ diff --git a/plans/names-codes/screens/M7.jpg b/plans/names-codes/screens/M7.jpg index 2203d992ac..dad1148df1 100644 Binary files a/plans/names-codes/screens/M7.jpg and b/plans/names-codes/screens/M7.jpg differ diff --git a/plans/names-codes/screens/N1.jpg b/plans/names-codes/screens/N1.jpg index 842f6121ea..7692716973 100644 Binary files a/plans/names-codes/screens/N1.jpg and b/plans/names-codes/screens/N1.jpg differ diff --git a/plans/names-codes/screens/N10.jpg b/plans/names-codes/screens/N10.jpg index 18ba5ea1ca..e5549353b2 100644 Binary files a/plans/names-codes/screens/N10.jpg and b/plans/names-codes/screens/N10.jpg differ diff --git a/plans/names-codes/screens/N10a.jpg b/plans/names-codes/screens/N10a.jpg index 822efe1932..e697d0e952 100644 Binary files a/plans/names-codes/screens/N10a.jpg and b/plans/names-codes/screens/N10a.jpg differ diff --git a/plans/names-codes/screens/N10b.jpg b/plans/names-codes/screens/N10b.jpg index a752a6b933..8367d71058 100644 Binary files a/plans/names-codes/screens/N10b.jpg and b/plans/names-codes/screens/N10b.jpg differ diff --git a/plans/names-codes/screens/N12b.jpg b/plans/names-codes/screens/N12b.jpg index e6dd6d724f..f76f0ed95c 100644 Binary files a/plans/names-codes/screens/N12b.jpg and b/plans/names-codes/screens/N12b.jpg differ diff --git a/plans/names-codes/screens/N1a.jpg b/plans/names-codes/screens/N1a.jpg index cc9f8f22b4..eadda7be73 100644 Binary files a/plans/names-codes/screens/N1a.jpg and b/plans/names-codes/screens/N1a.jpg differ diff --git a/plans/names-codes/screens/N1b.jpg b/plans/names-codes/screens/N1b.jpg index 07a6d3a284..7f2a566814 100644 Binary files a/plans/names-codes/screens/N1b.jpg and b/plans/names-codes/screens/N1b.jpg differ diff --git a/plans/names-codes/screens/N1c.jpg b/plans/names-codes/screens/N1c.jpg index 5b38f3fe16..9f8d16d927 100644 Binary files a/plans/names-codes/screens/N1c.jpg and b/plans/names-codes/screens/N1c.jpg differ diff --git a/plans/names-codes/screens/N2.jpg b/plans/names-codes/screens/N2.jpg index 2afb00f6b4..0b3180ec6d 100644 Binary files a/plans/names-codes/screens/N2.jpg and b/plans/names-codes/screens/N2.jpg differ diff --git a/plans/names-codes/screens/N2a.jpg b/plans/names-codes/screens/N2a.jpg index 98ea4967d5..cf03992efc 100644 Binary files a/plans/names-codes/screens/N2a.jpg and b/plans/names-codes/screens/N2a.jpg differ diff --git a/plans/names-codes/screens/N2b.jpg b/plans/names-codes/screens/N2b.jpg index 9dcd1ddd3e..8939427cb2 100644 Binary files a/plans/names-codes/screens/N2b.jpg and b/plans/names-codes/screens/N2b.jpg differ diff --git a/plans/names-codes/screens/N2c.jpg b/plans/names-codes/screens/N2c.jpg index 14640c46fe..912da84648 100644 Binary files a/plans/names-codes/screens/N2c.jpg and b/plans/names-codes/screens/N2c.jpg differ diff --git a/plans/names-codes/screens/N2d.jpg b/plans/names-codes/screens/N2d.jpg index 88b0006d99..6f975bca19 100644 Binary files a/plans/names-codes/screens/N2d.jpg and b/plans/names-codes/screens/N2d.jpg differ diff --git a/plans/names-codes/screens/N2e.jpg b/plans/names-codes/screens/N2e.jpg index e2d32deae4..c0bc465ade 100644 Binary files a/plans/names-codes/screens/N2e.jpg and b/plans/names-codes/screens/N2e.jpg differ diff --git a/plans/names-codes/screens/N3.jpg b/plans/names-codes/screens/N3.jpg index 0a6e1a59c3..09c4f97f18 100644 Binary files a/plans/names-codes/screens/N3.jpg and b/plans/names-codes/screens/N3.jpg differ diff --git a/plans/names-codes/screens/N3a.jpg b/plans/names-codes/screens/N3a.jpg index 572aeb8cc2..76a90dace8 100644 Binary files a/plans/names-codes/screens/N3a.jpg and b/plans/names-codes/screens/N3a.jpg differ diff --git a/plans/names-codes/screens/N3c.jpg b/plans/names-codes/screens/N3c.jpg index e29e0c0316..b301243809 100644 Binary files a/plans/names-codes/screens/N3c.jpg and b/plans/names-codes/screens/N3c.jpg differ diff --git a/plans/names-codes/screens/N3e.jpg b/plans/names-codes/screens/N3e.jpg index e4545e9bcc..446c7f602b 100644 Binary files a/plans/names-codes/screens/N3e.jpg and b/plans/names-codes/screens/N3e.jpg differ diff --git a/plans/names-codes/screens/N3f.jpg b/plans/names-codes/screens/N3f.jpg index 1c5fb42441..b53fd14ba0 100644 Binary files a/plans/names-codes/screens/N3f.jpg and b/plans/names-codes/screens/N3f.jpg differ diff --git a/plans/names-codes/screens/N3g.jpg b/plans/names-codes/screens/N3g.jpg index 5479b2b1ff..fdf893c063 100644 Binary files a/plans/names-codes/screens/N3g.jpg and b/plans/names-codes/screens/N3g.jpg differ diff --git a/plans/names-codes/screens/N3h.jpg b/plans/names-codes/screens/N3h.jpg index 84e0a5d994..947febebe0 100644 Binary files a/plans/names-codes/screens/N3h.jpg and b/plans/names-codes/screens/N3h.jpg differ diff --git a/plans/names-codes/screens/N4.jpg b/plans/names-codes/screens/N4.jpg index e2ad146f97..f54176ee60 100644 Binary files a/plans/names-codes/screens/N4.jpg and b/plans/names-codes/screens/N4.jpg differ diff --git a/plans/names-codes/screens/N4a.jpg b/plans/names-codes/screens/N4a.jpg index 7f3321a2f3..8b96f843e8 100644 Binary files a/plans/names-codes/screens/N4a.jpg and b/plans/names-codes/screens/N4a.jpg differ diff --git a/plans/names-codes/screens/N4c.jpg b/plans/names-codes/screens/N4c.jpg index d5780c41af..43d99bafc9 100644 Binary files a/plans/names-codes/screens/N4c.jpg and b/plans/names-codes/screens/N4c.jpg differ diff --git a/plans/names-codes/screens/N4d.jpg b/plans/names-codes/screens/N4d.jpg index 67a69864c1..d3a4c84954 100644 Binary files a/plans/names-codes/screens/N4d.jpg and b/plans/names-codes/screens/N4d.jpg differ diff --git a/plans/names-codes/screens/N5.jpg b/plans/names-codes/screens/N5.jpg index 3cc1d11e37..0b44e378fa 100644 Binary files a/plans/names-codes/screens/N5.jpg and b/plans/names-codes/screens/N5.jpg differ diff --git a/plans/names-codes/screens/N6.jpg b/plans/names-codes/screens/N6.jpg index 933379efe9..c1bbe89763 100644 Binary files a/plans/names-codes/screens/N6.jpg and b/plans/names-codes/screens/N6.jpg differ diff --git a/plans/names-codes/screens/N6a.jpg b/plans/names-codes/screens/N6a.jpg index 49516774e4..b33cb5c54c 100644 Binary files a/plans/names-codes/screens/N6a.jpg and b/plans/names-codes/screens/N6a.jpg differ diff --git a/plans/names-codes/screens/N6b.jpg b/plans/names-codes/screens/N6b.jpg index e5b1ba4f26..a3011831ea 100644 Binary files a/plans/names-codes/screens/N6b.jpg and b/plans/names-codes/screens/N6b.jpg differ diff --git a/plans/names-codes/screens/N7.jpg b/plans/names-codes/screens/N7.jpg index 8563618ce6..1b9d3402a3 100644 Binary files a/plans/names-codes/screens/N7.jpg and b/plans/names-codes/screens/N7.jpg differ diff --git a/plans/names-codes/screens/N7a.jpg b/plans/names-codes/screens/N7a.jpg index 251d1b8175..2ef50ee79e 100644 Binary files a/plans/names-codes/screens/N7a.jpg and b/plans/names-codes/screens/N7a.jpg differ diff --git a/plans/names-codes/screens/N8.jpg b/plans/names-codes/screens/N8.jpg index a1e8af4e86..cdf9737c17 100644 Binary files a/plans/names-codes/screens/N8.jpg and b/plans/names-codes/screens/N8.jpg differ diff --git a/plans/names-codes/screens/N8a.jpg b/plans/names-codes/screens/N8a.jpg index a3e153018c..06b999bd47 100644 Binary files a/plans/names-codes/screens/N8a.jpg and b/plans/names-codes/screens/N8a.jpg differ diff --git a/plans/names-codes/screens/N9.jpg b/plans/names-codes/screens/N9.jpg index 3d01eae340..59738782c4 100644 Binary files a/plans/names-codes/screens/N9.jpg and b/plans/names-codes/screens/N9.jpg differ