feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
// Marketplace page: list the gateway's active offers and complete a purchase.
//
// A listing is the seller's partially signed transaction (see
// Argus/src/lib/market-tx.js). Buying = verify it locally, append our own
// funding inputs and the outputs that give us the certificate and update
// the registry, sign our inputs, broadcast. The seller's signature only
// stays valid while their certificate UTXO is unspent, so the gateway prunes
// stale offers and the buyer's broadcast is the final arbiter.
feat(portal): the name's settings show what its subdomains serve, and let you change it
The per-name tab was a DNS record table: eight record types, of which the
stack reads exactly one, and then only as a last resort when a name carries
no content record. Everything else was signed, stored and read by nobody —
furniture that looked like configuration. Meanwhile the thing a name owner
actually has, a folder tree that already answers on matching subdomains, was
not visible anywhere and could not be changed.
The tab now leads with what answers today: the apex, every folder in the
name's storage next to the subdomain it serves on, and which of those a rule
overrides. Below it, the rules — point a subdomain at a different folder,
redirect it, or switch it off.
Classic DNS keeps its table, behind a disclosure that says plainly what
those records do today and what they are waiting for. Nobody loses a record
they published; they just stop being the first thing a name owner is asked
to think about. The table gains a Host column, so records can belong to a
subdomain rather than only the apex.
Both halves are one signed manifest and one publish button — they always
were one file, and two buttons for it only ever invited a half-saved state.
Labels and values are separated in the generated rows so the words
translate and the owner's own folder and URL do not.
The signer accepted v1 manifests only, which would have rejected every
publish from this screen.
2026-09-28 01:21:47 +02:00
import * as BNS from "https://silentmode.st/js/bns-register.js?v=20260928hosts" ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
const API = "https://silentmode.st" ;
const $ = ( id ) => document . getElementById ( id ) ;
const esc = ( s ) => String ( s ? ? "" ) . replace ( /[&<>"']/g , ( c ) => ( { "&" : "&" , "<" : "<" , ">" : ">" , '"' : """ , "'" : "'" } [ c ] ) ) ;
const fmtInt = ( n ) => String ( n ) . replace ( /\B(?=(\d{3})+(?!\d))/g , "," ) ;
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
const bch = ( sats ) => ( window . siriusPricing ? . formatBch || ( ( s ) => ( Number ( s ) / 1e8 ) . toFixed ( 8 ) . replace ( /0+$/ , "" ) . replace ( /\.$/ , "" ) + " BCH" ) ) ( sats ) ;
const bits = ( sats ) => ( window . siriusPricing ? . formatBits || ( ( s ) => ( Number ( s ) / 100 ) . toLocaleString ( "en-US" ) + " bits" ) ) ( sats ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
const usd = ( sats ) => { const r = window . siriusPricing ? . CHIPNET _SATS _PER _USD || 250000 ; const v = Number ( sats ) / r ; return ( window . siriusPricing ? . formatUsd || ( ( n ) => "$" + n . toFixed ( 2 ) ) ) ( Math . round ( v * 100 ) / 100 ) ; } ;
2026-09-20 19:20:30 +02:00
const fmtUsdC = ( cents ) => ( window . siriusPricing ? . formatUsd || ( ( n ) => "$" + n . toFixed ( 2 ) ) ) ( Math . round ( Number ( cents ) ) / 100 ) ;
const satsForCents = ( cents ) => Math . round ( Number ( cents ) / 100 * ( window . siriusPricing ? . CHIPNET _SATS _PER _USD || 400000 ) ) ;
const clampSats = ( sats , l ) => Math . min ( Number ( l . ceil _sats ) , Math . max ( Number ( l . floor _sats ) , sats ) ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
const shortAddr = ( a ) => ( a ? a . replace ( /^[^:]+:/ , "" ) . slice ( 0 , 8 ) + "…" + a . slice ( - 5 ) : "—" ) ;
bns: one network table instead of chipnet constants copied into fifteen places
Every client hard-coded the chipnet beacons, address prefix and electrum
servers on its own: resolver, registrar config, wallets, gateway, indexer,
mirror scripts, the browser bundle and the mobile Java. A mainnet launch would
have meant finding all of them and hoping none was missed.
The table now lives in resolver-web.js, the one file every client already
shares, so it stays a single-file drop-in. BNS_NETWORK selects the record;
unset means chipnet, so nothing changes today: the live index resolves the
same 60 names and 20 TLDs, the 67 offline tests pass, and the dashboard,
market and studio load the same values through BNS.NETWORK.
The mainnet record carries the verified public servers, the prefix and its
own Sia bucket, but its beacons, start height and operator address are
deliberately null: requireBeacons() refuses to scan until they are pinned in
the order ROADMAP-MAINNET.md §6 requires. Bns.java reads a generated
BnsNetwork.java so the phone cannot drift from the desktop clients.
NETWORK-CONFIG.md records what reads the table and what a launch still pins.
2026-09-27 21:18:37 +02:00
window . siriusNetwork = BNS . NETWORK ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
let listings = [ ] ;
let wallet = null ;
let selected = null ;
async function restoreWallet ( ) {
if ( window . siriusWallet ) return window . siriusWallet ;
const S = window . siriusSession ;
if ( ! S ? . restore ) return null ;
try {
const s = await S . restore ( ) ;
if ( ! s ? . mnemonic ) return null ;
bns: one network table instead of chipnet constants copied into fifteen places
Every client hard-coded the chipnet beacons, address prefix and electrum
servers on its own: resolver, registrar config, wallets, gateway, indexer,
mirror scripts, the browser bundle and the mobile Java. A mainnet launch would
have meant finding all of them and hoping none was missed.
The table now lives in resolver-web.js, the one file every client already
shares, so it stays a single-file drop-in. BNS_NETWORK selects the record;
unset means chipnet, so nothing changes today: the live index resolves the
same 60 names and 20 TLDs, the 67 offline tests pass, and the dashboard,
market and studio load the same values through BNS.NETWORK.
The mainnet record carries the verified public servers, the prefix and its
own Sia bucket, but its beacons, start height and operator address are
deliberately null: requireBeacons() refuses to scan until they are pinned in
the order ROADMAP-MAINNET.md §6 requires. Bns.java reads a generated
BnsNetwork.java so the phone cannot drift from the desktop clients.
NETWORK-CONFIG.md records what reads the table and what a launch still pins.
2026-09-27 21:18:37 +02:00
const w = await BNS . BuiltInWallet . fromMnemonic ( s . mnemonic , BNS . NETWORK . prefix , s . accountPath || undefined ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
window . siriusWallet = w ;
return w ;
} catch { return null ; }
}
async function load ( ) {
const status = $ ( "status" ) ;
try {
const r = await fetch ( ` ${ API } /api/market ` , { cache : "no-store" } ) ;
if ( ! r . ok ) throw new Error ( "API " + r . status ) ;
const j = await r . json ( ) ;
listings = ( j . listings || [ ] ) . map ( ( l ) => ( { ... l , label : l . name . split ( "." ) [ 0 ] , tld : l . tld || l . name . split ( "." ) . pop ( ) } ) ) ;
render ( ) ;
} catch ( e ) { status . className = "status err" ; status . textContent = "Could not load listings: " + ( e . message || e ) ; }
}
function render ( ) {
const q = ( $ ( "q" ) . value || "" ) . toLowerCase ( ) . trim ( ) ;
const sort = $ ( "sort" ) . value ;
const tldSel = $ ( "tld" ) ;
const tlds = [ ... new Set ( listings . map ( ( l ) => l . tld ) ) ] . sort ( ) ;
const keep = tldSel . value ;
tldSel . innerHTML = ` <option value="">All TLDs</option> ` + tlds . map ( ( t ) => ` <option value=" ${ esc ( t ) } " ${ t === keep ? " selected" : "" } >. ${ esc ( t ) } </option> ` ) . join ( "" ) ;
let rows = listings . filter ( ( l ) => ( ! q || l . name . includes ( q ) ) && ( ! tldSel . value || l . tld === tldSel . value ) ) ;
2026-09-20 19:20:30 +02:00
const priceOf = ( l ) => ( l . kind === "usd" ? clampSats ( satsForCents ( l . target _cents ) , l ) : Number ( l . price _sats ) ) ;
rows . sort ( ( a , b ) => sort === "name" ? a . name . localeCompare ( b . name ) : sort === "new" ? Date . parse ( b . created _at ) - Date . parse ( a . created _at ) : priceOf ( a ) - priceOf ( b ) ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
$ ( "count" ) . textContent = ` ${ rows . length } of ${ listings . length } ` ;
const status = $ ( "status" ) ;
2026-09-20 00:58:25 +02:00
document . querySelector ( ".toolbar" ) . hidden = ! listings . length ;
if ( ! listings . length ) {
status . hidden = true ;
$ ( "list" ) . innerHTML = ` <div class="empty big">
< div class = "ico" > 🛒 < / d i v >
< h3 > Nothing is for sale right now < / h 3 >
< p > Names go on sale when their owners list them from the dashboard . Have one to sell , or want a fresh one instead ? < / p >
< div class = "cta" > < a class = "btn acid" href = "./portal.html#domains" > Sell a name from your dashboard → < /a><a class="btn ghost" href="./ # search - input " > Register a new name < / a > < / d i v >
< / d i v > ` ;
return ;
}
status . hidden = false ;
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
status . className = "status" ; status . textContent = ` ${ listings . length } name ${ listings . length === 1 ? "" : "s" } for sale. Prices are set by the sellers; you pay the price plus about 13 bits of chain dust and fee. ` ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
$ ( "list" ) . innerHTML = rows . length ? rows . map ( ( l ) => `
< div class = "row" >
< div class = "n" > $ { esc ( l . label ) } . < span class = "tld" > $ { esc ( l . tld ) } < / s p a n > $ { w a l l e t & & l . s e l l e r = = = w a l l e t . a d d r e s s ? ' < s p a n c l a s s = " b a d g e " > y o u r s < / s p a n > ' : " " } < / d i v >
2026-09-20 19:20:30 +02:00
< div class = "price" > $ { l . kind === "usd"
? ` <b> ${ esc ( fmtUsdC ( l . target _cents ) ) } </b><span>fixed in USD · today ≈ ${ esc ( bch ( satsForCents ( l . target _cents ) ) ) } </span> `
: ` <b> ${ esc ( bch ( l . price _sats ) ) } </b><span>≈ ${ esc ( usd ( l . price _sats ) ) } </span> ` } < / d i v >
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
< div > < button class = "btn acid small" data - buy = "${esc(l.name)}" $ { wallet && l . seller === wallet . address ? "disabled" : "" } > Buy → < / b u t t o n > < / d i v >
< div class = "meta" > < span > seller $ { esc ( shortAddr ( l . seller ) ) } < / s p a n > < s p a n > · < / s p a n > < s p a n > l i s t e d $ { e s c ( n e w D a t e ( l . c r e a t e d _ a t ) . t o L o c a l e D a t e S t r i n g ( ) ) } < / s p a n > < s p a n > · < / s p a n > < a h r e f = " $ { A P I } / b n s / $ { e n c o d e U R I C o m p o n e n t ( l . n a m e ) } / " t a r g e t = " _ b l a n k " r e l = " n o o p e n e r " > v i e w s i t e → < / a > < / d i v >
< / d i v > ` ) . j o i n ( " " ) : ` < d i v c l a s s = " e m p t y " > N o t h i n g m a t c h e s t h a t f i l t e r . < / d i v > ` ;
}
[ "q" , "sort" , "tld" ] . forEach ( ( id ) => $ ( id ) . addEventListener ( "input" , render ) ) ;
document . addEventListener ( "click" , ( e ) => { const b = e . target . closest ( "[data-buy]" ) ; if ( b ) openBuy ( b . dataset . buy ) ; } ) ;
// ---------- buy ----------
function setMsg ( t , cls = "" ) { $ ( "buy-msg" ) . textContent = t ; $ ( "buy-msg" ) . className = "msg " + cls ; }
function push ( t ) { const log = $ ( "buy-steps" ) ; log . className = "steps on" ; const d = document . createElement ( "div" ) ; d . textContent = t ; log . appendChild ( d ) ; log . scrollTop = 1e6 ; }
function openBuy ( name ) {
selected = listings . find ( ( l ) => l . name === name ) ; if ( ! selected ) return ;
$ ( "buy-name" ) . textContent = name ;
2026-09-20 19:20:30 +02:00
const isUsd = selected . kind === "usd" ;
const price = BigInt ( isUsd ? clampSats ( satsForCents ( selected . target _cents ) , selected ) : selected . price _sats ) ;
const check = isUsd ? BNS . verifyUsdListing ( selected ) : BNS . verifyListing ( selected , { category : selected . category } ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
$ ( "buy-kv" ) . innerHTML = [
2026-09-20 19:20:30 +02:00
isUsd ? [ "Price" , ` ${ esc ( fmtUsdC ( selected . target _cents ) ) } fixed · about ${ esc ( bch ( price ) ) } at today's rate (the exact amount is set by the oracle quote when you buy, between ${ esc ( bch ( selected . floor _sats ) ) } and ${ esc ( bch ( selected . ceil _sats ) ) } ) ` ]
: [ "Price" , ` ${ esc ( bch ( price ) ) } (≈ ${ esc ( usd ( price ) ) } ) ` ] ,
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
[ "Seller" , esc ( selected . seller ) ] ,
[ "You receive" , "the name's certificate, to your token address" ] ,
2026-09-20 19:20:30 +02:00
[ "Offer check" , check . ok ? ( isUsd ? "contract terms verified" : "seller signature valid" ) : "INVALID: " + esc ( check . error ) ] ,
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
[ "Extra" , "≈ 16 bits dust + fee" ] ,
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
] . map ( ( [ k , v ] ) => ` <div class="k"> ${ k } </div><div class="v"> ${ v } </div> ` ) . join ( "" ) ;
$ ( "buy-steps" ) . className = "steps" ; $ ( "buy-steps" ) . innerHTML = "" ; setMsg ( "" ) ;
const canBuy = ! ! wallet && check . ok && wallet . address !== selected . seller ;
$ ( "buy-go" ) . disabled = ! canBuy ; $ ( "buy-go" ) . textContent = "Buy — sign & broadcast" ;
$ ( "buy-signin" ) . hidden = ! ! wallet ;
if ( ! wallet ) setMsg ( "Sign in on the dashboard first — the purchase is signed with your wallet in this browser." ) ;
$ ( "buy" ) . classList . add ( "open" ) ;
}
const closeBuy = ( ) => $ ( "buy" ) . classList . remove ( "open" ) ;
$ ( "buy-close" ) . addEventListener ( "click" , closeBuy ) ; $ ( "buy-cancel" ) . addEventListener ( "click" , closeBuy ) ;
$ ( "buy" ) . addEventListener ( "click" , ( e ) => { if ( e . target === $ ( "buy" ) ) closeBuy ( ) ; } ) ;
$ ( "buy-go" ) . addEventListener ( "click" , async ( ) => {
if ( ! selected || ! wallet ) return ;
const btn = $ ( "buy-go" ) ; btn . disabled = true ; btn . textContent = "Buying…" ; setMsg ( "" ) ;
let el = null ;
try {
bns: one network table instead of chipnet constants copied into fifteen places
Every client hard-coded the chipnet beacons, address prefix and electrum
servers on its own: resolver, registrar config, wallets, gateway, indexer,
mirror scripts, the browser bundle and the mobile Java. A mainnet launch would
have meant finding all of them and hoping none was missed.
The table now lives in resolver-web.js, the one file every client already
shares, so it stays a single-file drop-in. BNS_NETWORK selects the record;
unset means chipnet, so nothing changes today: the live index resolves the
same 60 names and 20 TLDs, the 67 offline tests pass, and the dashboard,
market and studio load the same values through BNS.NETWORK.
The mainnet record carries the verified public servers, the prefix and its
own Sia bucket, but its beacons, start height and operator address are
deliberately null: requireBeacons() refuses to scan until they are pinned in
the order ROADMAP-MAINNET.md §6 requires. Bns.java reads a generated
BnsNetwork.java so the phone cannot drift from the desktop clients.
NETWORK-CONFIG.md records what reads the table and what a launch still pins.
2026-09-27 21:18:37 +02:00
el = await BNS . connect ( ) ; push ( ` connected to ${ BNS . NETWORK . label } ` ) ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
const utxos = await BNS . getUtxosForAddresses ( el , wallet . watchedAddresses ) ;
push ( ` wallet has ${ utxos . filter ( ( u ) => ! u . token ) . length } spendable coin(s) ` ) ;
2026-09-20 19:20:30 +02:00
let built , signed ;
if ( selected . kind === "usd" ) {
const q = await ( await fetch ( ` ${ API } /api/price/oracle?txid= ${ encodeURIComponent ( selected . outpoint . txid ) } ` , { cache : "no-store" } ) ) . json ( ) ;
if ( ! q . message ) throw new Error ( q . error || "no oracle quote" ) ;
push ( ` oracle quote: 1 BCH = $ ${ ( q . price _cents / 100 ) . toFixed ( 2 ) } (median of ${ q . median _of } exchanges) ` ) ;
built = BNS . buildUsdBuyTx ( { listing : selected , quote : q , buyerAddress : wallet . address , buyerTokenAddress : wallet . tokenAddress , utxos , records : selected . records || { } , addressToLockingBytecode : BNS . addressToLockingBytecode } ) ;
push ( ` you pay ${ bch ( built . priceSats ) } · fee ${ bits ( built . fee ) } ` ) ;
signed = BNS . signInputs ( built . transaction , built . sourceOutputs , ( h ) => wallet . keyFor ( h ) , { skip : [ built . covenantInputIndex ] } ) ;
} else {
built = BNS . completeSale ( { listing : selected , buyerAddress : wallet . address , buyerTokenAddress : wallet . tokenAddress , utxos , records : selected . records || { } , addressToLockingBytecode : BNS . addressToLockingBytecode } ) ;
push ( ` built sale · price ${ bch ( built . costs . priceSats ) } · fee ${ bits ( built . costs . chainFeeSats ) } ` ) ;
signed = BNS . signInputs ( built . transaction , built . sourceOutputs , ( h ) => wallet . keyFor ( h ) , { skip : [ built . sellerInputIndex ] } ) ;
}
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
push ( "signed your inputs (the seller's signature is already in place)" ) ;
const txid = await BNS . broadcast ( el , signed . hex ) ;
push ( "broadcast: " + txid ) ;
setMsg ( ` Done. ${ selected . name } is yours — it appears in your dashboard within a minute. ` , "ok" ) ;
btn . textContent = "Bought ✓" ;
setTimeout ( load , 3000 ) ;
} catch ( e ) {
const raw = String ( e . message || e ) ;
setMsg ( /not enough funds/i . test ( raw ) ? ` Not enough coins: ${ raw } . Fund ${ wallet . address } first. ` : "Failed: " + raw , "err" ) ;
push ( "error: " + raw ) ; btn . disabled = false ; btn . textContent = "Buy — sign & broadcast" ;
} finally { try { el ? . close ? . ( ) ; } catch { } }
} ) ;
( async function boot ( ) {
wallet = await restoreWallet ( ) ;
await load ( ) ;
} ) ( ) ;
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
window . addEventListener ( "sirius:price" , ( ) => { if ( listings . length ) render ( ) ; } ) ;