77 Commits

Author SHA1 Message Date
c87ce760e2 fix: paginate weinfo times and read min deposit from ranking_info
- weinfo.html now pages times with the API's zero-based page offset instead of block heights\n- bal.js renders page metadata and hides dead next links on the last page\n- index.html and terms.html read amount from /ranking_info/{chain}
2026-11-06 21:29:03 -04:00
33df29b7a8 Home: 'Add your Will Executor' link under the table; FAQ 6 lands on the panel
- Link beside the disclaimer scrolls to the Add / Renewal status panel
- ?mode=check scrolls to the panel; Terms FAQ 6 links to /?mode=check#we-panel
2026-11-06 19:38:59 +01:00
d0504dc930 Server page: no prev/next, 'Latest 100 rounds' badge
- The rounds endpoint ignores page/offset parameters, so the pager is hidden until the server supports paging
2026-11-06 19:33:28 +01:00
96ba0aafcc Server page: tighter table cells so the full hash fits
- Cell padding 14 -> 10 px
2026-11-06 19:31:29 +01:00
3883987e53 Server page: full block hash in the rounds table
- Hash no longer shortened; slightly smaller mono text so the row stays on one line
2026-11-06 19:31:05 +01:00
5bc502b0ea Server page restyled in the site's look
- Header with back button, server card with status badge, Listing card (expiry, days left, renewal window), Activity tiles, WeTime table in the home style
- Inline CSS only; qrcode.min.js dropped (unused); bal.js pager renders prev / page / next buttons
2026-11-06 19:29:36 +01:00
40d9b4ed80 Home: Renewal status tab with expiry card
- Check tab renamed Renewal status / Show renewal status
- Result card: expiry date, days left, renewal window open or opening date, position, balance, donations, server page link
- Terms FAQ 6 follows the new name
2026-11-06 19:22:24 +01:00
a13b93b8e0 Home panel: Add / Check tabs, side notes, narrower ranking panel
- One panel with two tabs (Add a server / Check a server), one button each, ?mode=check opens the Check tab
- Side column with 'What happens next' / 'What you get' steps; ranking panel gets a closing note and a narrower column
- Terms FAQ 6 points at the Check tab
2026-11-06 19:03:57 +01:00
f6dab5de5f Check server is read-only: list lookup and server data, no registration
- Looks the exact URL up in /datalist and shows expiry, balance and donations from /weinfodata
- No terms box, no POST /data: checking an unlisted URL creates nothing; clear message either way
2026-11-06 18:55:42 +01:00
6c58a80f26 Timeline: renewal paid in both windows; numbered FAQ; Check server button
- Terms timeline: 'Renewal paid' coin inside the year-1 and year-2 windows, caption says the window opens every year
- FAQ items numbered 1-13 (CSS counter, JSON-LD unchanged); FAQ 6 points at Check server
- Home form: 'Check server' button, same request as 'Add server'; hint text updated
2026-11-06 18:50:09 +01:00
4c57517a8a Terms: FAQ section; home: how to see a listed server's renewal date
- Section 5 FAQ with 13 questions answered from the Terms only; native details accordions, FAQPage JSON-LD
- Home form: 'Already listed?' hint, the exact URL shows status, balance and next renewal date
2026-11-06 18:45:02 +01:00
e39d56b756 Terms: animated listing timeline first, sections reordered, navigation buttons
- Animated SVG timeline of the listing lifecycle (pay, listed, renewal window every year, renewed / removed) opens the Terms page; plays when scrolled into view, Replay button, four-step strip kept on small screens
- Sections reordered Listing, Order, Contact, Changes and renumbered; cross-references updated, legal text unchanged
- Home hero: 'How to get listed' button to the Terms page; Terms: 'Back to the server list' button and footer link
2026-11-06 17:59:33 +01:00
6046f8fba9 Drop the old Testnet tab from the server list
- Chain buttons now show Bitcoin and Testnet4 only
- Old ?chain=testnet links fall back to Bitcoin
2026-11-03 20:49:20 +01:00
956e133b7c Show deposit progress after the invoice
- The invoice checks the server every 30 seconds and shows the amount
  detected, then the confirmation once the server is listed.
- The server page gets the same banner, with a 24-hour limit on checks.
- The invoice link is now "Follow your payment", so the invoice can be
  closed and the deposit followed from a fixed address.
2026-11-01 12:36:46 +01:00
aa723acbca Put the selected chain in the page address
- Opening ?chain=testnet or ?chain=testnet4 selects that list, so it can
  be shared as a link; an unknown value falls back to Bitcoin.
- The chain buttons update the address and Back/Forward follow it.
- Bitcoin stays the default and keeps the plain address.
2026-11-01 12:05:28 +01:00
43b2ca2b64 Update index.html
Added Master website footer link
2026-08-15 00:03:24 +00:00
f11d066ef4 Upload files to "/"
SEO optimize
2026-08-14 23:18:33 +00:00
5ea9460e00 Upload files to "/"
SEO optimize
2026-08-14 23:18:24 +00:00
efb60e8fe3 Update terms.html
SEO optimize
2026-08-14 23:17:21 +00:00
30e410c7a8 Update index.html
SEO Optimize
2026-08-14 23:16:41 +00:00
e395da105e Hide regtest tab when not on localhost 2026-07-22 12:43:47 -04:00
8c75e40edc Upload files to "/"
mobile changes
2026-07-21 22:48:50 +00:00
97617cecad Update index.html
added mobile view
2026-07-21 22:47:53 +00:00
3dcd8227fa Link Bitcoin After Life to homepage in page header 2026-07-20 14:56:48 -04:00
b50b9f5de9 Add disclaimer for Info, Fee, and Version columns 2026-07-20 14:53:56 -04:00
373580b356 Update index.html
Remove Block stat from header
2026-07-17 11:19:51 +00:00
a6ea23ff5f Update index.html
Fix random server order: use /datalist (ordered) instead of /data (unordered HashMap)
2026-07-17 11:05:00 +00:00
40db9f36ab Update index.html
Add data-freshness note above server list
2026-07-17 10:58:11 +00:00
8a9b1b21d1 original page 2026-07-17 10:43:25 +00:00
e71051d869 Added details of data update times
Added details of data update times
2026-07-17 10:10:46 +00:00
e4707b7fd7 Merge pull request 'Align index.html with yearly-listing terms' (#3) from donkey-ai/bal-welist-website:fix/index-yearly-model-consistency into main
Reviewed-on: bitcoinafterlife/bal-welist-website#3
2026-07-16 22:32:47 +00:00
Claude (draft, do not push as-is)
c297fb30db Match panel heights in Add-server / How-ranking-works grid
align-items:start made each .panel size to its own content, so the
two side-by-side cards in .add-grid ended up with different heights
whenever their text length differed. Switch to align-items:stretch
(+ flex column on the panels) so both cards always match height.
2026-07-17 00:32:27 +02:00
Claude (draft, do not push as-is)
7be9959241 Align index.html with yearly-listing terms
- Sidebar: replace vague 'Bid-based ranking' lead with the actual
  4-criteria ordering (excess donated, total paid, base fee, points)
- Sidebar: drop 'Early-adopter guarantee 12 months' (contradicts
  Terms 2.04 pro-rata conversion of legacy balances); add
  'Listing period 365 days' / 'Renewal window' facts instead
- Info block 03: rename 'Early Adopter Incentives' -> 'Listing &
  Renewal', remove the unconditional 12-month promise, describe the
  real pay -> listed -> renewal -> expiry lifecycle from Terms Sec. 2
- Rename 'Next deposit' -> 'Next renewal' (label + status text)
- Fix s-block stat: use max(last_block) across all servers instead of
  getFiltered()[0].last_block, since the list order no longer tracks
  block height
- Clarify 'Server added' -> 'Server verified... listed after 3
  confirmations', matching Terms 2.01 (listing starts at 3 confs,
  not at submission)

Note: result.donation (singular) confirmed correct against the
current API by backend — left untouched, no change needed here.
2026-07-17 00:08:06 +02:00
57347e87e7 Update listing terms and UI copy for yearly-fee model; fix API field reference 2026-07-16 17:33:33 -04:00
a8d1c5b214 Fix donations field: use singular donation from backend 2026-07-16 13:36:05 -04:00
0298ec737e docs: clarify payment affects list position 2026-07-16 12:58:04 -04:00
add0b2cddc add expiry date to weinfo page, fix last update population, move last update to first column 2026-07-14 08:28:19 -04:00
74fe167137 refactor: move fetch after terms accept, add donations/nextdep/unconfirmed balance, fix download blob 2026-07-14 08:01:03 -04:00
f9ffc383a0 fix: add missing statusMap definition and iv-balance reset 2026-07-11 20:27:54 -04:00
d223404a86 fix: remove deprecated iv-status2 element references, clean up status reset logic 2026-07-11 20:24:20 -04:00
b79ee2fdd8 add comprehensive status explanation documentation 2026-07-11 07:57:35 -04:00
131e5ffabf display status using description only (readme doesn't mention period) 2026-07-11 07:56:51 -04:00
ce66b0de3f fix: display status with description in weinfo page, add fallbacks for undefined values, handle empty times table 2026-07-11 07:49:27 -04:00
ff4e46858b add contact field to form and invoice, move submit button, add weinfo link in invoice, fix undefined fields in weinfo page 2026-07-11 06:35:05 -04:00
6c942e58c6 remove Amount and Status rows from invoice display 2026-07-10 11:50:14 -04:00
616c0d1285 add fullscreen QR overlay on click 2026-07-10 10:38:24 -04:00
03d41dd25d include amount in bitcoin URI for QR code and share link 2026-07-10 10:36:01 -04:00
adcd4cde94 fix qr code overflow: remove fixed height on container, pass size to QRCode lib, add max-width to canvas 2026-07-10 10:34:55 -04:00
8bdef5d18f add share invoice button with Web Share API / clipboard fallback 2026-07-10 10:32:52 -04:00
5536d8b14e rename 'Minimum amount' to 'Annual fee' in invoice 2026-07-10 10:30:05 -04:00
6cfa521adc rename Minimum amount -> Fee annuale 2026-07-10 10:29:15 -04:00
560a4d1cbe move note inside invoice div to prevent overlap with qrcode on mobile 2026-07-10 10:26:06 -04:00
221bfec74e add Amount field to invoice 2026-07-10 10:20:22 -04:00
c9749bfed1 change sort order: height desc, points desc, wins desc, fee asc, version desc 2026-07-10 10:16:43 -04:00
756ab98c41 remove Balance column from server list 2026-07-10 10:12:27 -04:00
098ca48e9d rewrite terms for yearly-listing model
author: donkey-ai
2026-07-10 10:09:47 -04:00
d97b405cae truncate long URLs in server list to 40 chars with ellipsis 2026-07-05 21:47:23 -04:00
b6e9f9c11f sort by balance desc, base_fee asc, points desc; update terms description 2026-07-05 21:29:26 -04:00
f4bcb27a7e show success status only when invoice appears, not during terms step 2026-07-05 21:11:25 -04:00
26fdefd294 show success message only after accepting terms 2026-07-05 21:09:51 -04:00
75a1191060 rewrite deposit threshold section in terms 2026-07-05 21:03:52 -04:00
f6c686cc40 remove acceptance clause from terms 2026-07-05 20:57:01 -04:00
ea66de0693 rename min_amount endpoint to min_deposit 2026-07-05 20:47:12 -04:00
2077b5b931 rename Listing threshold to Deposit threshold, add donation note in terms 2026-07-05 19:22:26 -04:00
e74259ef55 sort by balance then base_fee instead of points 2026-07-05 19:20:03 -04:00
f0d4104834 fetch min_amount from server instead of hardcoding 50000 2026-07-05 19:18:38 -04:00
dbc00b6012 add terms acceptance step before invoice, enter key submit, fix link display 2026-07-05 18:23:12 -04:00
3d2dbf1c2a use COMMIT_HASH_PLACEHOLDER for server-side hash injection, remove version.json and local hooks 2026-07-05 17:12:54 -04:00
c9d665f929 add commit hash link in footer via version.json and pre-commit hook 2026-07-05 16:39:03 -04:00
eda26684a0 remove online/offline status badge from server list 2026-07-05 16:02:09 -04:00
b1c27e84bd Nascondi messaggio di successo finché non viene aggiunto un server 2026-07-05 12:15:53 -04:00
31bdce61ca add server url row and live verification check in invoice 2026-07-03 15:58:31 -04:00
a66a1b6daa reset invoice fields on submit, show error outside invoice, show invoice only on success 2026-07-03 15:37:40 -04:00
5a3c1f2b9f show server url in add-success message 2026-07-03 14:40:44 -04:00
8d2d55fe40 fix 2026-06-30 12:33:31 -04:00
868292d7c1 fix ok-online 2026-06-30 12:28:09 -04:00
9454f8c5c1 Fix weLink using object key instead of numeric server id 2026-06-30 05:48:37 -04:00
8 changed files with 2444 additions and 628 deletions

99
MOBILE-CHANGES.md Normal file
View File

@@ -0,0 +1,99 @@
# Mobile readability fixes — `index.html`
Base: commit `3dcd822` (`bitcoinafterlife/bal-welist-website`, `main`).
**Scope:** header, filter bar and server list only. CSS-only — the JavaScript is
byte-for-byte unchanged. Desktop rendering is unaffected: every change sits
inside a `@media (max-width: ...)` block, except one line on `body` (item 7).
The "Add your Will Executor" panel, the invoice and the QR code were left
untouched on purpose, since payments are expected to be made from a desktop
browser.
---
## 1. Chain selector overflowed the viewport
**Before:** `.seg` was an `inline-flex` row of four buttons (Bitcoin, Testnet,
Testnet4, Regtest) with no wrapping — roughly 340px wide. On a 360–390px phone,
minus page padding, it pushed past the right edge and made the whole page scroll
horizontally.
**After:** below 600px the selector becomes a full-width 2×2 grid. Inner
dividers are recalculated with `:nth-child` so the borders stay correct in the
new arrangement.
## 2. Touch targets too small
Chain buttons were ~33px tall. Raised to `min-height: 44px` (the standard
minimum for a finger), applied to the chain buttons, the Tor toggle row and the
JSON link.
## 3. Nested scrolling in the server list
**Before:** `.mobile-list` carried `max-height: 350px; overflow: auto`, so on a
phone the list was a small window scrolling inside a page that also scrolls —
the two gestures fight each other.
**After:** the height cap is removed below 860px; the list flows with the page.
The desktop table (`.table-card`) keeps its 350px cap unchanged.
## 4. Long URLs broke out of the cards
Server URLs contain no spaces, so the browser cannot break them and they
overflowed the card. Added `overflow-wrap: anywhere`, plus `min-width: 0` on the
flex parent — without it a flex child defaults to `min-width: auto` and refuses
to shrink below its content width.
The JS truncation at 40 characters is unchanged.
## 5. Uneven card grid
The five fields (Score, Wins, Fee, Version, Height) sat in two fixed columns,
leaving Height alone on a half-empty row. Switched to
`repeat(auto-fit, minmax(84px, 1fr))`, which fits as many columns as the width
allows.
## 6. iOS auto-zoom on the search field
iOS Safari zooms the entire page when a focused input has a font size below
16px, and the user then has to pinch back out. The filter field is raised to
16px below 600px and made full width.
## 7. Text inflation in landscape
Added `-webkit-text-size-adjust: 100%` to `body`. iOS enlarges font sizes when a
phone is rotated to landscape, which distorted the header and the cards. This is
the only change outside a media query; it has no effect on desktop browsers.
## 8. Tighter spacing below 480px
New breakpoint reducing padding and type size where space is scarce:
| Element | Before | After |
| --- | --- | --- |
| `.wrap` side padding | 20px | 14px (+12px usable width per row) |
| `header.hero` padding | 48px 0 28px | 28px 0 20px |
| `.brand h1` | 24px | 20px |
| Logo mark | 46px | 40px |
| `.stat .n` | 26px | 22px |
| `.intro-card` padding | 28px 32px | 20px 18px |
| `.info-block` padding | 22px 24px | 18px 16px |
| `.srv` padding | 16px | 14px |
| Footer | space-between, wrapping | single column |
---
## Verification performed
- CSS braces balanced (148 open / 148 close).
- All 32 element IDs referenced by the JavaScript still present in the markup.
- Tag balance checked for `div`, `section`, `header`, `footer`, `main`, `table`,
`style`, `script`.
- Media queries present in order: 860px, 600px, 480px, `prefers-reduced-motion`.
## Not yet verified
Rendering was not tested on a physical device. Worth a quick check on one iPhone
and one Android phone before merging, in particular the 2×2 chain selector at
320px width (iPhone SE, the narrowest screen still in common use).

153
README.md
View File

@@ -1,2 +1,153 @@
welist html
# Server Status: Complete Explanation and Reference Guide
This page explains all the **Will Executor server health statuses** used throughout the BAL Web Interface. Each status type provides critical information about server operation, health checks, and potential issues for administrators monitoring the Bitcoin After Life network.
## Status Code Overview
| Status | Description | When It Occurs |
|--------|-------------|----------------|
| `OK` | Health check passed | Server is online, functioning normally |
| `TIMEOUT: <message>` | Connection timeout | Server response took too long |
| `CONNECTION: <message>` | Connection error | Network connectivity issues |
| `STATUS: <code>` | HTTP error (from server) | Server returned non-200 status |
| `WRONG REPLY: <message>` | Invalid JSON response | Server response couldn't be parsed |
| `REQUEST: <message>` | Request error | Client-side request failed |
| `BODY: <message>` | Body read error | Failed to read response body |
| `DECODE: <message>` | Decode error | Failed to decode response content |
| `KO` | Generic error | Unknown or unspecified error |
## Status Types Explained
### 🟢 Good Statuses
**`OK`** - Everything is functioning correctly:
- Server is online and responding
- Health check completed successfully
- Ready for new registration/processing
### 🟡 Warning/Moderate Issues
**`TIMEOUT: <message>`** - Connection timed out:
- Server took too than expected to respond
- Could indicate server overload or network issues
- Requires attention but server may still be functional
**`CONNECTION: <message>`** - Connection error:
- Network connectivity problems
- Server might be reachable but cannot communicate
- Potential infrastructure or configuration issue
**`STATUS: <code>`** - HTTP error:
- Server responded with an error code (4xx or 5xx)
- Could indicate misconfiguration, maintenance, or issues |
- Different error codes mean different things
### 🔴 Critical Issues
**`WRONG REPLY`** - Invalid JSON response:
- Server response format is incorrect
- May indicate server software bug or corruption
- Prevents proper data processing
**`REQUEST`** - Request error:
- Client request failed
- Network or protocol issue
- Server may be unreachable
**`BODY`** - Body read error:
- Failed to read response body after successful connection
- Could indicate server processing issues
- Data corruption or transmission problems
**`DECODE`** - Decode error:
- Failed to decode response content
- Encoding mismatch or corrupted data
- Prevents proper data interpretation
**`KO`** - Generic error:
- Unknown or unspecified error
- Fallback for all other unidentified issues
- Indicates something went wrong but exact cause unknown
## Example Status Messages
### Normal Operations
```
Status: OK
Meaning: Health check passed
Display: Server is healthy and ready
```
### Network/Time Issues
```
Status: TIMEOUT: Connection timed out after 30 seconds
Meaning: Server took too long to respond
Display: "Connection timeout (Connection timed out after 30 seconds)"
```
### HTTP Errors
```
Status: STATUS: 500
Meaning: Server returned HTTP 500 Internal Server Error
Display: "HTTP error (HTTP error)"
```
### Response Processing Issues
```
Status: WRONG REPLY: Invalid JSON
Meaning: Server response could not be parsed as JSON
Display: "Invalid JSON response (Invalid JSON)"
```
## Practical Usage Guide
### For Server Administrators
- **OK**: Check server resources and logs if needed
- **TIMEOUT/CONNECTION**: Check server network connectivity
- **STATUS**: Review server logs and restart if needed
- **WRONG REPLY/DECODE/REQUEST**: Investigate server software issues
- **KO**: Review server errors and restart
### For Users
- **OK**: Server is working correctly
- **TIMEOUT/CONNECTION**: Temporary issue, try again later
- **STATUS**: Server may be down for maintenance
- **Other errors**: Server issue, contact support if persistent
## Monitoring Tips
1. **Continuous Monitoring**: Regular checks of status codes help detect issues early
2. **Alert Setup**: Configure alerts for non-OK statuses
3. **Log Analysis**: Correlate status codes with server logs
4. **Capacity Planning**: Address persistent timeouts/connections with infrastructure improvements
## Status Processing Logic
### Status Code Processing
```javascript
const statusMap = {
'OK': 'Health check passed',
'TIMEOUT': 'Connection timeout',
'CONNECTION': 'Connection error',
'STATUS': 'HTTP error',
'WRONG REPLY': 'Invalid JSON response',
'REQUEST': 'Request error',
'BODY': 'Body read error',
'DECODE': 'Decode error',
'KO': 'Generic error'
};
// Extract base status from status message
const st = (data.status||'').split(':')[0];
// Display only the description (e.g., "Connection timeout" instead of "TIMEOUT: Connection timeout")
statusDisplay = statusMap[st] || '—';
```
## Related Documentation
For more details on server setup, monitoring, and troubleshooting, refer to:
- [Server Administration Guide](./administration)
- [Status Monitoring Setup](./monitoring-setup)
- [Error Resolution Handbook](./error-resolve)
## Additional Information
This status system provides a clear, human-readable way to understand server health at a glance, making it easier for administrators to quickly identify and respond to issues.

42
bal.js
View File

@@ -76,18 +76,40 @@ async function fetchDataAndPopulateTable(url,params,fnPopulateTable,downloadOpt,
function make_paginator(){
document.getElementById("chainth").innerHTML = chain;
const pag = document.getElementById("paginator");
pag.appendChild(document.createTextNode("_"));
if (page > 0) {
pag.appendChild(createlink("<","?page="+paginator.prev));
pag.innerHTML = "";
const currentPage = paginator && paginator.page != null
? paginator.page
: (Number(page) || 0);
const prevPage = paginator && paginator.prev != null
? paginator.prev
: Math.max(currentPage - 1, 0);
const nextPage = paginator && paginator.next != null
? paginator.next
: currentPage + 1;
const hasPrev = paginator && paginator.hasPrev != null
? paginator.hasPrev
: currentPage > 0;
const hasNext = paginator && paginator.hasNext != null
? paginator.hasNext
: true;
// prev (a plain, dimmed arrow on the first page), "page N", next
if (hasPrev) {
pag.appendChild(createlink("&lsaquo;","?page="+prevPage));
} else {
const off = document.createElement("span");
off.className = "pg-off"; off.innerHTML = "&lsaquo;";
pag.appendChild(off);
}
else{
pag.appendChild(document.createTextNode("<"));
const lbl = document.createElement("span");
lbl.className = "pg-label"; lbl.textContent = "page " + currentPage;
pag.appendChild(lbl);
if (hasNext) {
pag.appendChild(createlink("&rsaquo;","?page="+nextPage));
} else {
const off = document.createElement("span");
off.className = "pg-off"; off.innerHTML = "&rsaquo;";
pag.appendChild(off);
}
pag.appendChild(document.createTextNode("_"));
pag.appendChild(document.createTextNode(page));
pag.appendChild(document.createTextNode("_"));
pag.appendChild(createlink(">","?page="+paginator.next));
}
}

1569
index.html

File diff suppressed because it is too large Load Diff

4
robots.txt Normal file
View File

@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://welist.bitcoin-after.life/sitemap.xml

5
sitemap.xml Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://welist.bitcoin-after.life/</loc><lastmod>2026-08-14</lastmod><priority>1.0</priority></url>
<url><loc>https://welist.bitcoin-after.life/terms.html</loc><lastmod>2026-08-14</lastmod><priority>0.4</priority></url>
</urlset>

740
terms.html Normal file
View File

@@ -0,0 +1,740 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BAL Server List — Terms &amp; Conditions</title>
<meta name="description" content="Terms and conditions for listing a Will-Executor server on the Bitcoin After Life WeList directory.">
<link rel="canonical" href="https://welist.bitcoin-after.life/terms.html">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Bitcoin After Life">
<meta property="og:title" content="Terms & Conditions — Bitcoin After Life WeList">
<meta property="og:description" content="Terms and conditions for listing a Will-Executor server on the Bitcoin After Life WeList directory.">
<meta property="og:url" content="https://welist.bitcoin-after.life/terms.html">
<meta property="og:image" content="https://bitcoin-after.life/pitura/og-default.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@BitcoinAfterLif">
<meta name="twitter:title" content="Terms & Conditions — Bitcoin After Life WeList">
<meta name="twitter:description" content="Terms and conditions for listing a Will-Executor server on the Bitcoin After Life WeList directory.">
<meta name="twitter:image" content="https://bitcoin-after.life/pitura/og-default.png">
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<!--
Sections 2-4 rewritten on 2026-07 for the yearly-listing model
(flat yearly fee, renewal from 6 months before expiry). The list
is ordered by total excess donated, then total amount paid, then
base fee, then WeTime points as a tie-breaker (see Section 3),
matching the server-side get_list() ordering. Section 1, header,
footer and the dynamic min-deposit script are unchanged from the
previous version.
2026-11-06: the page opens with an animated SVG timeline (.lt) of the
listing lifecycle, played when it scrolls into view; the four-step
.lifecycle strip is kept for small screens only. Sections reordered
(Listing, Order, Contact, Changes) and renumbered; the "see Section N"
references follow. The yearly fee in the SVG (#thr-min-lt) is updated by
the same script as #thr-min.
-->
<style>
:root{
--maxw:1120px;
--radius:10px;
--bg:#eef1f4;
--panel:#ffffff;
--ink:#1a2027;
--muted:#5b6672;
--line:#dfe4ea;
--line-strong:#cbd2da;
--accent:#2d6cdf;
--accent-soft:#e6eefc;
--ok:#1f9d57;
--ok-soft:#e3f4ea;
--off:#9aa4b0;
--off-soft:#eceff3;
--rank:#94a0ad;
--display:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--body:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
--shadow:0 1px 2px rgba(20,30,50,.05);
}
*{box-sizing:border-box}
html,body{margin:0}
body{
background:var(--bg);
color:var(--ink);
font-family:var(--body);
line-height:1.5;
-webkit-font-smoothing:antialiased;
}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 20px}
header.hero{padding:48px 0 28px;border-bottom:1px solid var(--line)}
.hero .wrap{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:16px}
.brand .mark{
width:46px;height:46px;border-radius:12px;flex:none;
display:grid;place-items:center;
background:#fff;color:var(--ink);
border:1px solid var(--line-strong);box-shadow:var(--shadow);overflow:hidden;
}
.brand .mark .brand-logo{width:34px;height:34px;display:block}
.brand h1{
font-family:var(--display);font-weight:600;
font-size:30px;line-height:1.05;margin:0;letter-spacing:-.01em;
}
.brand .sub{color:var(--muted);font-size:14px;margin-top:2px}
/* Way back to the server list (added 2026-11-06: the page had no obvious exit). */
.back-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 16px;border-radius:10px;
border:1px solid var(--line-strong);background:var(--panel);color:var(--accent);
font-weight:600;font-size:14px;text-decoration:none;box-shadow:var(--shadow)}
.back-btn:hover{border-color:var(--accent)}
.back-btn svg{width:18px;height:18px;flex:none}
.mono{font-family:var(--mono);font-size:13px}
.info-section{margin:48px 0}
.info-section h2{font-family:var(--display);font-size:22px;margin:0 0 8px}
.info-section .sub{color:var(--muted);font-size:15px;line-height:1.6;margin:0 0 28px;max-width:760px}
.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.info-block{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px 24px;box-shadow:var(--shadow)}
.info-block .ic{font-family:var(--mono);font-size:12px;color:var(--accent);letter-spacing:.1em;display:block;margin-bottom:10px}
.info-block h3{font-size:15px;margin:0 0 8px;letter-spacing:.01em}
.info-block p{font-size:14px;color:var(--muted);margin:0;line-height:1.6}
/* Blocks with more than one paragraph need breathing room between them */
.info-block p + p{margin-top:8px}
/* --- Added for the yearly-listing model (2026-07) ---
Listing lifecycle: a small horizontal timeline that encodes the real
sequence pay -> listed -> renewal window -> expiry. Spans both grid
columns so the four steps read left to right. */
.lifecycle{grid-column:1 / -1;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;
background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
padding:22px 24px;box-shadow:var(--shadow)}
.step{position:relative;padding-top:14px;border-top:3px solid var(--line-strong)}
.step:first-child{border-top-color:var(--accent)}
.step::before{
content:"";position:absolute;top:-7px;left:0;width:11px;height:11px;border-radius:50%;
background:var(--panel);border:3px solid var(--line-strong);
}
.step:first-child::before{border-color:var(--accent)}
.step h3{font-size:14px;margin:0 0 6px;letter-spacing:.01em}
.step p{font-size:13px;color:var(--muted);margin:0;line-height:1.55}
/* --- Added 2026-11-06: animated timeline of the listing lifecycle (top of the page).
Inline SVG + CSS keyframes, runs once and ends on the complete drawing;
prefers-reduced-motion shows the complete drawing at once. On small
screens the SVG text would be too small, so the four-step .lifecycle
strip is shown instead (see the 860px media query). */
/* ---- Listing timeline (SVG + CSS animation) ---- */
.lt{position:relative;grid-column:1 / -1;background:var(--panel);border:1px solid var(--line);
border-radius:var(--radius);padding:18px 20px 6px;box-shadow:var(--shadow)}
.lt svg{display:block;width:100%;height:auto;font-family:var(--body)}
.lt .replay{position:absolute;right:14px;top:14px;font:12px var(--body);color:var(--accent);background:var(--accent-soft);
border:1px solid #cfe0fb;border-radius:999px;padding:4px 10px;cursor:pointer}
.lt text{fill:var(--ink);font-size:13px}
.lt .small{font-size:12px;fill:var(--muted)}
.lt .mono{font-family:var(--mono);font-size:12px}
.lt .lbl-ok{fill:var(--ok);font-weight:600}
.lt .lbl-acc{fill:var(--accent);font-weight:600}
.lt .lbl-off{fill:var(--off);font-weight:600}
/* every animated piece starts hidden/empty and ends in its final state; the
animation runs once (fill-mode both) so the diagram is complete when it stops */
.lt .bar-y1{transform-origin:120px 0}
.lt .bar-y2{transform-origin:520px 0}
.lt .dot{transform:translateX(400px)}
/* .play is added by the script when the timeline scrolls into view (and by
Replay); the keyframes start from the empty state, so until then the
complete drawing is shown */
.lt.play .pill{animation:fade .85s ease both}
.lt.play .bar-y1{animation:grow 5.5s linear both}
.lt.play .bar-y2{animation:grow 4s linear both}
.lt.play .dot{animation:travel 5.5s linear both}
.lt.play .win{animation:fade 1s ease both}
.lt.play .out{animation:fade 1s ease both}
.lt.play .p1{animation-delay:.4s}
.lt.play .p2{animation-delay:1.8s}
.lt.play .p3{animation-delay:3.4s}
.lt.play .bar-y1,.lt.play .dot{animation-delay:4s}
.lt.play .win{animation-delay:6.8s}
.lt.play .out{animation-delay:9.6s}
.lt.play .bar-y2{animation-delay:10.5s}
.lt.play .pay{animation:fade .5s ease both}
.lt.play .pay1{animation-delay:7.8s}
.lt.play .pay2{animation-delay:13.4s}
@keyframes fade{from{opacity:0}to{opacity:1}}
@keyframes grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes travel{from{transform:translateX(0)}to{transform:translateX(400px)}}
@media (prefers-reduced-motion:reduce){
.lt.play .pill,.lt.play .win,.lt.play .out,.lt.play .pay,.lt.play .bar-y1,.lt.play .bar-y2,.lt.play .dot{animation:none}
}
.lifecycle{display:none}
/* --- FAQ (2026-11-06): native accordions in the page's card style --- */
.faq{display:grid;gap:10px;counter-reset:faq}
.faq details{counter-increment:faq}
.faq summary::before{content:counter(faq);flex:none;width:26px;height:26px;border-radius:50%;background:var(--accent);color:#fff;
font-family:var(--mono);font-size:12px;display:inline-flex;align-items:center;justify-content:center}
.faq summary span{flex:1}
.faq details{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:0 20px;box-shadow:var(--shadow)}
.faq summary{cursor:pointer;padding:16px 0;font-weight:600;font-size:15px;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:16px}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";flex:none;width:24px;height:24px;border-radius:50%;background:var(--accent-soft);color:var(--accent);
display:inline-flex;align-items:center;justify-content:center;font-size:18px;line-height:1}
.faq details[open] summary::after{content:"\2212"}
.faq details[open] summary{border-bottom:1px solid var(--line)}
.faq p{margin:14px 0 18px;color:var(--muted);font-size:14px;line-height:1.6}
footer{border-top:1px solid var(--line);margin-top:48px;padding:28px 0;color:var(--muted);font-size:13px}
footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px}
footer a{color:var(--accent);text-decoration:none}
@media (max-width:860px){
.info-grid{grid-template-columns:1fr}
.brand h1{font-size:24px}
/* On small screens the four-step strip replaces the SVG timeline and stacks vertically */
.lt-section{display:none}
.lifecycle{display:grid;grid-template-columns:1fr}
.step{border-top:none;border-left:3px solid var(--line-strong);padding:0 0 4px 16px}
.step:first-child{border-left-color:var(--accent)}
.step::before{top:2px;left:-7px}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How much does it cost to be listed?",
"acceptedAnswer": {
"@type": "Answer",
"text": "50,000 sats for 365 days, paid one year at a time. The exact amount in satoshi is shown in the payment request. The fee can be reviewed once a year, around 17 July; time you have already bought stays valid under the conditions in force when you paid."
}
},
{
"@type": "Question",
"name": "When does my server appear in the list?",
"acceptedAnswer": {
"@type": "Answer",
"text": "As soon as your payment reaches 3 confirmations on the blockchain, typically around 30 minutes. Your 365 days start at that moment."
}
},
{
"@type": "Question",
"name": "I paid less than the yearly fee. What happens?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A payment below the yearly fee is treated as a donation: it does not add listing time."
}
},
{
"@type": "Question",
"name": "When and how do I renew?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Any time from 6 months before your expiry date, by paying the yearly fee for the exact same server URL as published in the list. The new year is added on top of the time you have left, so renewing early never costs you days."
}
},
{
"@type": "Question",
"name": "What happens if I do not renew?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Your server is removed from the list at expiry. Users who already added it to their plugin keep it, because the plugin's server list is add-only. Being listed matters for reaching new users."
}
},
{
"@type": "Question",
"name": "Where do I see my expiry date?",
"acceptedAnswer": {
"@type": "Answer",
"text": "On the home page, open the “Renewal status” tab, enter the exact URL of your server as it appears in the list and press “Show renewal status”. Nothing is charged: you see the expiry date, the days left, whether the renewal window is open (or when it opens), the position in the list, the balance and the donations, with a link to the server page. The date is not shown in the main table, so you need the exact URL. Knowing it also tells you when your renewal window opens (6 months before). Since the price per day is fixed and public, the expiry date shows how much a server has paid: this is intentional transparency."
}
},
{
"@type": "Question",
"name": "How is the order of the list decided?",
"acceptedAnswer": {
"@type": "Answer",
"text": "By four criteria, in this order: total excess donated (higher first), total amount paid (higher first), base fee charged (lower first) and, as a tie-breaker, WeTime points (higher first). Excess donated never decays, so paying more than the exact yearly fee is the fastest way to move up."
}
},
{
"@type": "Question",
"name": "What are WeTime points?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Points earned in WeTime rounds: each executor periodically submits its block hash, and the faster the response within the same block, the more points. They are not transferable, have no monetary value and only break ties in this list. They measure presence and reactivity, not how a server will handle inheritance transactions."
}
},
{
"@type": "Question",
"name": "Can I change my server URL?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Write to welist@bitcoin-after.life. Any change to a listing requires proof that you control both the domain and the server; we may ask for a message signed with the private key of the UTXO used for the payment."
}
},
{
"@type": "Question",
"name": "Can I get a refund?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Once a payment is confirmed on the blockchain and the server is listed, no refunds are issued. Double-check that your server URL is correct and reachable before paying."
}
},
{
"@type": "Question",
"name": "Is there a limit to the number of servers in the list?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. There is no fixed number of slots: every server with remaining listing time is shown."
}
},
{
"@type": "Question",
"name": "I had a balance from before the yearly model. What happened to it?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Balances deposited under the previous rules were converted into listing time at the same yearly rate (for example, 5,000 sats became 36.5 days). Those countdowns started on 17 July 2026; to stay listed beyond that time, renew as usual."
}
},
{
"@type": "Question",
"name": "Do I need to be in this list to run a Will Executor?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. This list is a convenience service: the BAL protocol does not depend on it. Users can add any Will Executor manually in the plugin, import custom lists or use other list services, and anyone is free to run one."
}
}
]
}
</script>
</head>
<body>
<header class="hero">
<div class="wrap">
<div class="brand">
<div class="mark"><svg class="brand-logo" xmlns="http://www.w3.org/2000/svg" viewBox="-116.00 -110.00 2262.00 2262.00" fill="currentColor" role="img" aria-label="Bitcoin After Life logo">
<g transform="translate(0.000000,2072.000000) scale(0.100000,-0.100000)">
<path d="M12278 20443 c-84 -177 -102 -216 -175 -378 -204 -457 -370 -897
-471 -1255 -48 -168 -112 -437 -133 -557 -5 -29 -14 -79 -19 -110 -13 -74 -35
-246 -50 -388 -15 -139 -15 -617 -1 -738 32 -266 64 -444 111 -621 142 -538
391 -1000 724 -1344 430 -444 818 -601 1781 -721 211 -27 254 -32 325 -42 257
-35 414 -68 560 -117 161 -53 255 -116 411 -271 127 -127 209 -242 291 -406
203 -404 379 -882 497 -1350 59 -237 69 -284 111 -522 86 -494 120 -1121 85
-1583 -42 -559 -115 -952 -277 -1485 -135 -447 -343 -924 -588 -1350 -534
-927 -1278 -1709 -2180 -2292 -255 -164 -666 -386 -965 -521 -513 -230 -1203
-416 -1845 -497 -393 -49 -1085 -48 -1550 4 -87 10 -349 47 -385 55 -11 3 -49
10 -85 16 -97 17 -335 69 -420 92 -41 11 -97 25 -125 33 -280 73 -667 214
-991 360 -264 120 -387 186 -709 378 -772 461 -1542 1195 -2040 1942 -449 676
-742 1312 -954 2075 -59 215 -128 538 -151 718 -48 358 -60 475 -68 670 -9
199 -11 205 -67 200 -22 -2 -31 -9 -36 -28 -22 -80 -1 -836 30 -1075 57 -441
166 -902 309 -1315 64 -185 77 -218 130 -350 57 -142 237 -533 284 -619 465
-836 991 -1472 1733 -2096 423 -356 1121 -781 1645 -1002 448 -189 902 -335
1290 -414 30 -6 75 -15 100 -20 411 -87 986 -139 1523 -139 211 0 572 26 762
55 580 88 992 191 1485 372 1215 446 2304 1240 3095 2259 773 994 1256 2155
1414 3399 38 295 51 523 51 872 0 367 -22 684 -71 1001 -35 232 -85 484 -139
697 -50 197 -64 329 -56 499 22 431 161 834 372 1078 84 98 186 164 297 193
80 21 259 28 402 17 551 -45 591 -46 737 -33 217 19 373 61 527 143 179 94
296 184 451 344 159 165 269 318 373 521 154 302 233 573 278 958 17 141 17
643 0 790 -35 315 -92 651 -161 945 -48 207 -56 234 -66 237 -7 2 -37 -42 -69
-98 -70 -127 -118 -191 -214 -292 -128 -134 -287 -252 -476 -353 -206 -111
-371 -179 -790 -324 -602 -209 -831 -318 -1105 -523 -336 -251 -562 -642 -657
-1134 -14 -69 -17 -142 -17 -333 0 -234 9 -334 43 -520 5 -25 17 -94 27 -154
10 -59 23 -110 30 -113 10 -4 47 31 146 137 62 67 223 210 309 276 136 103
240 168 429 269 482 257 820 473 1152 737 88 70 162 128 165 128 25 0 -134
-153 -285 -273 -170 -136 -577 -398 -902 -582 -174 -99 -346 -209 -465 -299
-434 -327 -701 -703 -790 -1111 -23 -104 -23 -105 -32 -105 -5 0 -37 62 -72
138 -404 875 -960 1625 -1691 2277 -198 177 -711 687 -855 849 -277 313 -473
600 -614 897 -83 175 -81 211 6 71 364 -591 1009 -1283 1868 -2004 225 -188
424 -384 546 -538 152 -190 330 -452 420 -618 19 -35 40 -61 47 -60 27 6 215
466 297 728 104 331 138 510 157 822 15 261 3 458 -43 708 -24 129 -75 323
-108 415 -97 260 -142 352 -261 527 -292 433 -693 770 -1505 1263 -367 223
-501 306 -655 408 -480 319 -809 626 -1041 972 -133 197 -204 358 -291 658 -6
20 -15 37 -20 37 -5 0 -45 -75 -90 -167z"/>
<path d="M9065 18949 c-485 -27 -851 -72 -1295 -159 -164 -32 -200 -41 -400
-89 -110 -27 -170 -43 -395 -106 -103 -28 -436 -140 -600 -200 -961 -357
-1867 -883 -2670 -1549 -509 -423 -1078 -1018 -1453 -1518 -438 -585 -771
-1138 -1028 -1705 -651 -1437 -945 -3111 -814 -4638 17 -189 30 -324 41 -400
16 -123 40 -288 49 -345 5 -30 14 -82 19 -115 30 -193 172 -818 210 -925 5
-14 14 -43 19 -65 16 -59 150 -472 171 -530 11 -27 36 -95 57 -150 466 -1248
1246 -2444 2201 -3375 381 -372 670 -615 1128 -947 130 -94 429 -289 605 -395
676 -405 1362 -707 2135 -940 250 -75 647 -172 895 -219 387 -72 712 -115
1160 -151 207 -17 1118 -17 1295 0 349 33 547 57 775 93 924 148 1774 418
2625 835 1269 621 2400 1547 3266 2674 721 937 1258 2012 1565 3130 23 84 89
344 112 445 32 136 87 419 102 522 49 336 66 469 85 678 65 712 33 1528 -92
2275 -35 210 -39 230 -73 390 -65 307 -174 714 -250 930 -5 14 -27 79 -50 145
-82 239 -80 236 -137 214 -15 -6 -145 -51 -288 -100 -143 -49 -265 -95 -273
-102 -11 -11 -10 -21 2 -58 37 -111 103 -363 161 -614 27 -118 81 -400 99
-520 34 -223 43 -293 61 -470 35 -351 39 -442 40 -910 0 -459 -4 -563 -36
-855 -36 -340 -72 -558 -170 -1025 -119 -574 -428 -1418 -726 -1985 -503 -957
-1136 -1765 -1918 -2450 -710 -622 -1518 -1119 -2395 -1472 -240 -97 -668
-241 -885 -298 -151 -40 -300 -77 -400 -99 -55 -12 -118 -26 -140 -31 -22 -5
-69 -14 -105 -20 -36 -6 -85 -15 -110 -20 -78 -15 -270 -45 -385 -59 -871
-111 -1743 -81 -2635 89 -58 12 -123 25 -145 30 -22 5 -69 16 -105 24 -574
130 -1129 321 -1665 570 -55 26 -140 66 -190 89 -476 220 -1007 560 -1503 960
-426 343 -912 834 -1259 1270 -384 483 -710 999 -979 1552 -281 577 -472 1120
-619 1760 -9 39 -20 88 -25 110 -12 50 -39 184 -50 250 -30 172 -39 229 -50
305 -6 47 -15 114 -20 150 -86 621 -85 1395 1 2065 23 176 30 225 60 398 27
154 35 194 58 300 6 26 15 66 21 90 216 962 583 1840 1113 2662 341 529 676
946 1101 1371 731 731 1579 1325 2451 1719 164 74 208 93 450 192 293 120 723
258 1065 341 204 50 248 60 385 87 210 40 226 43 305 55 41 6 102 16 135 21
584 91 1350 114 1983 58 91 -8 136 -9 143 -2 6 6 15 41 19 79 10 75 25 189 40
299 26 194 32 257 23 266 -11 11 -198 41 -438 69 -422 51 -1053 72 -1460 49z"/>
<path d="M7765 12995 c-76 -31 -139 -96 -160 -168 -10 -34 -15 -94 -15 -188 0
-101 -3 -139 -13 -148 -9 -6 -84 -12 -203 -13 l-189 -3 0 -445 0 -445 195 -3
c136 -2 196 -6 201 -14 4 -7 8 -321 9 -698 0 -541 -2 -689 -12 -703 -11 -15
-37 -17 -203 -19 l-190 -3 0 -445 0 -445 190 -3 c165 -2 192 -4 203 -19 10
-14 12 -164 12 -703 -1 -377 -4 -691 -8 -697 -6 -9 -62 -13 -202 -15 l-195 -3
-3 -447 -2 -448 195 0 c194 0 196 0 204 -22 4 -13 9 -93 10 -178 2 -139 4
-160 25 -206 13 -28 39 -63 58 -77 67 -52 101 -57 357 -57 288 0 322 7 393 80
57 59 68 95 68 220 0 52 3 125 7 163 l6 67 128 0 127 0 4 -197 c3 -226 11
-255 81 -301 l40 -27 311 -3 311 -2 52 30 c36 21 61 45 80 77 27 47 28 53 33
233 l5 185 510 6 c516 6 541 8 719 49 503 114 963 516 1145 1000 77 206 111
385 111 589 -1 471 -225 925 -608 1235 -40 32 -72 62 -72 66 0 5 27 48 59 97
361 541 374 1222 33 1771 -228 366 -624 645 -1035 726 -154 31 -227 36 -530
36 -240 0 -317 3 -325 13 -6 7 -12 89 -14 199 l-3 186 -30 44 c-17 24 -51 54
-75 68 -45 25 -47 25 -328 28 -317 4 -353 -2 -410 -61 -50 -52 -56 -77 -62
-282 l-5 -190 -125 0 -125 0 -5 180 c-6 209 -18 249 -93 302 -70 50 -92 53
-346 53 -227 0 -238 -1 -296 -25z m2550 -1448 c88 -39 158 -140 193 -274 14
-58 17 -116 17 -378 -1 -334 -8 -409 -52 -533 -30 -86 -88 -156 -159 -192
l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
878 -3 915 -20z m291 -2341 c246 -114 416 -573 334 -901 -50 -200 -200 -414
-341 -486 -37 -19 -67 -19 -1066 -17 l-1028 3 -3 700 c-1 385 0 706 3 713 3 9
218 12 1027 12 l1023 0 51 -24z"/>
</g>
</svg></div>
<div>
<h1>Terms &amp; Conditions</h1>
<div class="sub">Bitcoin After Life · Will Executor List</div>
</div>
</div>
<a class="back-btn" href="/"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 17l-5-5 5-5"/><path d="M6 12h6"/><path d="M15 6h4M15 12h4M15 18h4"/></svg>Back to the server list</a>
</div>
</header>
<main class="wrap">
<!-- The listing lifecycle first: the animated timeline (desktop) with a
one-line intro. On small screens this section is hidden and the
four-step strip inside Section 1 takes its place. The animation
starts when the timeline scrolls into view (see the script). -->
<section class="info-section lt-section">
<h2>How a listing works</h2>
<p class="sub">From the payment to the renewal, in fifteen seconds. The details are in the sections below.</p>
<div class="lt" id="lt">
<button class="replay" type="button">&#8635; Replay</button>
<svg viewBox="0 0 1000 318" role="img" aria-labelledby="lt-title lt-desc">
<title id="lt-title">Listing timeline</title>
<desc id="lt-desc">Pay the yearly fee; after 3 confirmations the server is listed for 365 days. From 6 months before expiry you can renew: a new year is added on top. If you do not renew, the server is removed at expiry.</desc>
<defs>
<pattern id="hatch" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="8" height="8" fill="#e6eefc"/><line x1="0" y1="0" x2="0" y2="8" stroke="#2d6cdf" stroke-opacity=".35" stroke-width="2"/>
</pattern>
</defs>
<!-- steps 1-3: pay, confirmations, listed -->
<g class="pill p1">
<rect x="20" y="22" width="180" height="40" rx="20" fill="#fff" stroke="#dfe4ea"/>
<circle cx="42" cy="42" r="11" fill="#f7931a"/><text x="42" y="46.5" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">&#8383;</text>
<text x="62" y="38" font-weight="600">Pay the yearly fee</text>
<text x="62" y="54" class="small mono"><tspan id="thr-min-lt">50,000</tspan> sats / 365 days</text>
</g>
<g class="pill p2">
<path d="M202 42 h26" stroke="#cbd2da" stroke-width="2"/>
<polygon points="226,37 234,42 226,47" fill="#cbd2da"/>
<rect x="238" y="22" width="186" height="40" rx="20" fill="#fff" stroke="#dfe4ea"/>
<circle cx="260" cy="42" r="11" fill="none" stroke="#2d6cdf" stroke-width="2"/><path d="M260 35 v7 l5 3" stroke="#2d6cdf" stroke-width="2" fill="none" stroke-linecap="round"/>
<text x="280" y="38" font-weight="600">3 confirmations</text>
<text x="280" y="54" class="small">about 30 minutes</text>
</g>
<g class="pill p3">
<path d="M426 42 h26" stroke="#cbd2da" stroke-width="2"/>
<polygon points="450,37 458,42 450,47" fill="#cbd2da"/>
<rect x="462" y="22" width="190" height="40" rx="20" fill="#e3f4ea" stroke="#bfe3cd"/>
<circle cx="484" cy="42" r="11" fill="#1f9d57"/><path d="M478 42 l4 4 l8 -8" stroke="#fff" stroke-width="2.4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<text x="504" y="38" font-weight="600" fill="#1f9d57">Listed</text>
<text x="504" y="54" class="small">your 365 days start now</text>
</g>
<!-- axis: 24 months, 0 at x=120, 12 at x=520, 24 at x=920 -->
<line x1="120" y1="136" x2="940" y2="136" stroke="#cbd2da" stroke-width="2"/>
<polygon points="938,131 948,136 938,141" fill="#cbd2da"/>
<g class="small" text-anchor="middle">
<line x1="120" y1="130" x2="120" y2="142" stroke="#9aa4b0"/><text x="120" y="182">day 0</text>
<line x1="320" y1="130" x2="320" y2="142" stroke="#9aa4b0"/><text x="320" y="182">6 months</text>
<line x1="520" y1="130" x2="520" y2="142" stroke="#9aa4b0"/><text x="520" y="182" font-weight="600" fill="#1a2027">12 months &middot; expiry</text>
<line x1="720" y1="130" x2="720" y2="142" stroke="#9aa4b0"/><text x="720" y="182">18 months</text>
<line x1="920" y1="130" x2="920" y2="142" stroke="#9aa4b0"/><text x="920" y="182">24 months</text>
</g>
<!-- year 1: listed (fills from day 0 to month 12) -->
<rect class="bar-y1" x="120" y="124" width="400" height="24" rx="6" fill="#1f9d57" fill-opacity=".85"/>
<text x="220" y="140" text-anchor="middle" font-size="13" font-weight="600" fill="#fff" style="pointer-events:none">Listed &middot; year 1</text>
<circle class="dot" cx="120" cy="136" r="7" fill="#fff" stroke="#1f9d57" stroke-width="3"/>
<!-- renewal window: months 6-12, glued under the bar -->
<g class="win">
<!-- the window marked on the bar itself, and two guides down to its caption -->
<rect x="320" y="124" width="200" height="24" rx="6" fill="none" stroke="#2d6cdf" stroke-width="2" stroke-dasharray="5 4"/>
<rect x="320" y="150" width="200" height="16" fill="url(#hatch)" stroke="#2d6cdf" stroke-opacity=".5" rx="3"/>
<g class="pay pay1"><text x="420" y="114" text-anchor="middle" class="small" fill="#1a2027" font-weight="600">Renewal paid</text><circle cx="420" cy="136" r="9" fill="#f7931a" stroke="#fff" stroke-width="2"/><text x="420" y="140" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">&#8383;</text></g>
<text x="320" y="204" class="lbl-acc">Renewal window</text>
<text x="320" y="220" class="small">from 6 months before expiry: pay the yearly fee again</text>
<text x="320" y="235" class="small">for the same URL &mdash; the new year is added on top.</text>
<text x="320" y="250" class="small">Renewing early never costs you days.</text>
<text x="320" y="265" class="small">The same window opens every year.</text>
</g>
<!-- the two outcomes at month 12 -->
<g class="out">
<!-- renewed: the bar continues -->
<rect class="bar-y2" x="520" y="124" width="400" height="24" rx="6" fill="#1f9d57" fill-opacity=".55"/>
<text x="620" y="140" text-anchor="middle" font-size="13" font-weight="600" fill="#fff">Renewed &middot; year 2</text>
<!-- the renewal window repeats every year: months 18-24 of year 2 -->
<rect x="720" y="124" width="200" height="24" rx="6" fill="none" stroke="#2d6cdf" stroke-width="2" stroke-dasharray="5 4"/>
<rect x="720" y="150" width="200" height="16" fill="url(#hatch)" stroke="#2d6cdf" stroke-opacity=".5" rx="3"/>
<g class="pay pay2"><text x="820" y="114" text-anchor="middle" class="small" fill="#1a2027" font-weight="600">Renewal paid</text><circle cx="820" cy="136" r="9" fill="#f7931a" stroke="#fff" stroke-width="2"/><text x="820" y="140" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">&#8383;</text></g>
<path d="M520 98 v18" stroke="#1f9d57" stroke-width="2"/>
<polygon points="515,116 520,124 525,116" fill="#1f9d57"/>
<text x="660" y="78" class="lbl-ok">Renewed in time</text>
<text x="660" y="93" class="small">+365 days added on top of the time left</text>
<!-- not renewed: removed at expiry -->
<path d="M524 156 h116 v58 h12" stroke="#9aa4b0" stroke-width="2" fill="none"/>
<polygon points="648,209 656,214 648,219" fill="#9aa4b0"/>
<rect x="660" y="200" width="292" height="28" rx="14" fill="#eceff3" stroke="#dfe4ea"/>
<line x1="676" y1="208" x2="688" y2="220" stroke="#9aa4b0" stroke-width="2.4" stroke-linecap="round"/>
<line x1="688" y1="208" x2="676" y2="220" stroke="#9aa4b0" stroke-width="2.4" stroke-linecap="round"/>
<text x="698" y="218" class="lbl-off">Not renewed &rarr; removed at expiry</text>
<text x="660" y="248" class="small">Users who already added your server keep it:</text>
<text x="660" y="263" class="small">the plugin list is add-only.</text>
</g>
<!-- note: the paid total never decays -->
<g>
<rect x="20" y="282" width="960" height="1" fill="#dfe4ea"/>
<text x="20" y="304" class="small"><tspan font-weight="600" fill="#1a2027">Your paid total never decays:</tspan> it is kept across renewals and sets your position in the list (Section 2). Payments below the yearly fee are donations and add no time.</text>
</g>
</svg>
</div>
</section>
<section class="info-section">
<h2>1. Listing &amp; Renewal</h2>
<p class="sub">A listing works like a hosting plan: a flat yearly fee buys your server one year of presence in the list. Your paid amount accumulates and never decays — it is preserved across renewals and also determines your position in the list (see Section&nbsp;2). Payments are non-refundable (see Section&nbsp;3) — double-check your server URL before paying.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">01 · PAYMENT</span>
<h3>One Year at a Time</h3>
<p>The listing fee is <span id="thr-min">50,000</span> sats for 365 days, paid one year at a time. The exact amount in satoshi is shown in the payment request. Your server appears in the list once the payment reaches 3 confirmations on the blockchain (typically around 30 minutes), and your listing period starts at that moment.</p>
<p>Payments below the yearly fee will be considered donations and do not add listing time.</p>
</div>
<div class="info-block">
<span class="ic">02 · RENEWAL</span>
<h3>Renew From 6 Months Before Expiry</h3>
<p>You can renew for one additional year at any time starting 6 months before your expiry date, by paying the yearly fee for the exact same server URL as published in the list. The new year is added on top of your remaining time — renewing early never costs you days.</p>
<p>If the listing is not renewed, the server is removed from the list at expiry.</p>
</div>
<div class="info-block">
<span class="ic">03 · CAPACITY</span>
<h3>No Cap on Listings</h3>
<p>There is no fixed number of slots: every server with remaining listing time is shown. Paying buys presence in the list; the total amount paid also affects your position in it (see Section&nbsp;2).</p>
<p>Removal from the list does not affect users who already have your server in their plugin: the plugin's server list is add-only. Being listed matters for reaching new users.</p>
</div>
<div class="info-block">
<span class="ic">04 · TRANSITION</span>
<h3>Deposits Made Under the Previous Rules</h3>
<p>Balances deposited under the previous, balance-based rules are converted into listing time pro-rata at the same yearly rate (e.g. a 5,000-sat balance becomes 36.5 days of listing). Countdowns for converted balances start on 17 July 2026. To stay listed beyond the converted time, renew as described above.</p>
</div>
<div class="lifecycle">
<div class="step">
<h3>Pay</h3>
<p>Send the yearly fee shown in the payment request.</p>
</div>
<div class="step">
<h3>Listed</h3>
<p>After 3 confirmations your server appears; 365 days start.</p>
</div>
<div class="step">
<h3>Renewal window</h3>
<p>From 6 months before expiry you can add another year.</p>
</div>
<div class="step">
<h3>Expiry</h3>
<p>If not renewed, the server is removed from the list.</p>
</div>
</div>
</div>
</section>
<section class="info-section">
<h2>2. List Order &amp; Server Page</h2>
<p class="sub">The list is ordered by four criteria: first by total excess donated (higher first), then by total amount paid in balance (higher first), then by the base fee charged (lower first), and finally by WeTime points (higher first) as a tie-breaker.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">ORDER</span>
<h3>How Position Is Determined</h3>
<p>Servers are sorted by four criteria, in order: first by the total excess donated (higher first), then by the total paid amount (higher first), then by the base fee they charge (lower first), and finally by WeTime points (higher first) to break any remaining ties.</p>
<p>Because excess donated comes first and never decays, over-paying or tipping more — on top of any payment — directly and permanently moves a server up the list. Paying more than the exact yearly fee is the fastest way to improve position.</p>
</div>
<div class="info-block">
<span class="ic">POINTS</span>
<h3>WeTime Points</h3>
<p>Points are earned by participating in WeTime rounds. Each executor periodically submits its block hash; the faster the response within the same block, the more points awarded. The win count tracks how many times an executor was the fastest responder.</p>
<p>Points are not transferable, not redeemable and have no monetary value: within this list they act only as a tie-breaker. Points measure presence and reactivity — they do not certify how a server will handle inheritance transactions.</p>
</div>
<div class="info-block">
<span class="ic">DETAILS</span>
<h3>Server Detail Page</h3>
<p>Clicking a server in the list opens its detail page, showing the server URL, the listing expiry date, and its WeTime activity. The expiry date is shown only there, not in the main list. Since the price per day is fixed and public, the expiry date necessarily reflects how much a server has paid: this is intentional transparency.</p>
</div>
</div>
</section>
<section class="info-section">
<h2>3. Contact &amp; Support</h2>
<p class="sub">For URL changes, refunds, or any issues related to your Will Executor listing, contact us at <a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">VERIFICATION</span>
<h3>Proof of Ownership</h3>
<p>Any request to modify a listing (including URL changes) requires proof that you control both the domain and the server. We reserve the right to request a message signed with the private key of the UTXO used for payment before processing any changes or refunds.</p>
</div>
<div class="info-block">
<span class="ic">POLICY</span>
<h3>No Refunds After Payment</h3>
<p>Once a payment is confirmed on the blockchain and the server is listed, no refunds will be issued. Always verify that your server URL is correct and reachable before submitting payment.</p>
</div>
</div>
</section>
<section class="info-section">
<h2>4. Changes to These Terms</h2>
<p class="sub">We reserve the right to update these terms at any time. Material changes will be announced on this page with their effective date.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">REVIEW</span>
<h3>Annual Review</h3>
<p>Prices and rules are expected to be reviewed once a year, around 17 July, reflecting the growth of the protocol and market demand. Listing time already purchased remains valid under the conditions in force when it was bought; updated rules apply to new payments and renewals.</p>
</div>
<div class="info-block">
<span class="ic">SCOPE</span>
<h3>A Convenience Service</h3>
<p>This list is a convenience service operated at our discretion. The BAL protocol does not depend on it: users can add any Will Executor manually in the plugin, import custom lists, or use alternative list services. Anyone is free to run one.</p>
</div>
</div>
</section>
<!-- FAQ (added 2026-11-06): the questions a reader asks first, answered only
with what the sections above already say. Native <details>, no script;
the same questions are in the FAQPage JSON-LD in the head. -->
<section class="info-section" id="faq">
<h2>5. Frequently Asked Questions</h2>
<p class="sub">Short answers to the questions we get most often. The sections above remain the reference.</p>
<div class="faq">
<details open>
<summary><span>How much does it cost to be listed?</span></summary>
<p>50,000 sats for 365 days, paid one year at a time. The exact amount in satoshi is shown in the payment request. The fee can be reviewed once a year, around 17 July; time you have already bought stays valid under the conditions in force when you paid.</p>
</details>
<details>
<summary><span>When does my server appear in the list?</span></summary>
<p>As soon as your payment reaches 3 confirmations on the blockchain, typically around 30 minutes. Your 365 days start at that moment.</p>
</details>
<details>
<summary><span>I paid less than the yearly fee. What happens?</span></summary>
<p>A payment below the yearly fee is treated as a donation: it does not add listing time.</p>
</details>
<details>
<summary><span>When and how do I renew?</span></summary>
<p>Any time from 6 months before your expiry date, by paying the yearly fee for the exact same server URL as published in the list. The new year is added on top of the time you have left, so renewing early never costs you days.</p>
</details>
<details>
<summary><span>What happens if I do not renew?</span></summary>
<p>Your server is removed from the list at expiry. Users who already added it to their plugin keep it, because the plugin's server list is add-only. Being listed matters for reaching new users.</p>
</details>
<details>
<summary><span>Where do I see my expiry date?</span></summary>
<p>On the home page, open the “Renewal status” tab, enter the exact URL of your server as it appears in the list and press “Show renewal status”. Nothing is charged: you see the expiry date, the days left, whether the renewal window is open (or when it opens), the position in the list, the balance and the donations, with a link to the server page. The date is not shown in the main table, so you need the exact URL. Knowing it also tells you when your renewal window opens (6 months before). Since the price per day is fixed and public, the expiry date shows how much a server has paid: this is intentional transparency. <a href="/?mode=check#we-panel">Open the Renewal status tab &rarr;</a></p>
</details>
<details>
<summary><span>How is the order of the list decided?</span></summary>
<p>By four criteria, in this order: total excess donated (higher first), total amount paid (higher first), base fee charged (lower first) and, as a tie-breaker, WeTime points (higher first). Excess donated never decays, so paying more than the exact yearly fee is the fastest way to move up.</p>
</details>
<details>
<summary><span>What are WeTime points?</span></summary>
<p>Points earned in WeTime rounds: each executor periodically submits its block hash, and the faster the response within the same block, the more points. They are not transferable, have no monetary value and only break ties in this list. They measure presence and reactivity, not how a server will handle inheritance transactions.</p>
</details>
<details>
<summary><span>Can I change my server URL?</span></summary>
<p>Write to welist@bitcoin-after.life. Any change to a listing requires proof that you control both the domain and the server; we may ask for a message signed with the private key of the UTXO used for the payment.</p>
</details>
<details>
<summary><span>Can I get a refund?</span></summary>
<p>No. Once a payment is confirmed on the blockchain and the server is listed, no refunds are issued. Double-check that your server URL is correct and reachable before paying.</p>
</details>
<details>
<summary><span>Is there a limit to the number of servers in the list?</span></summary>
<p>No. There is no fixed number of slots: every server with remaining listing time is shown.</p>
</details>
<details>
<summary><span>I had a balance from before the yearly model. What happened to it?</span></summary>
<p>Balances deposited under the previous rules were converted into listing time at the same yearly rate (for example, 5,000 sats became 36.5 days). Those countdowns started on 17 July 2026; to stay listed beyond that time, renew as usual.</p>
</details>
<details>
<summary><span>Do I need to be in this list to run a Will Executor?</span></summary>
<p>No. This list is a convenience service: the BAL protocol does not depend on it. Users can add any Will Executor manually in the plugin, import custom lists or use other list services, and anyone is free to run one.</p>
</details>
</div>
</section>
</main>
<footer>
<div class="wrap">
<span>Bitcoin After Life — Will Executor List</span>
<a href="/">&larr; Back to the server list</a>
<a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>
</div>
</footer>
<script>
(function () {
// Listing timeline: play once when it scrolls into view; Replay restarts it.
var lt = document.getElementById('lt');
if (!lt || !('IntersectionObserver' in window)) { if (lt) lt.classList.add('play'); return; }
var play = function () { lt.classList.remove('play'); void lt.offsetWidth; lt.classList.add('play'); };
var io = new IntersectionObserver(function (entries) {
if (entries.some(function (e) { return e.isIntersecting; })) { play(); io.disconnect(); }
}, { threshold: 0.5 });
io.observe(lt);
lt.querySelector('.replay').addEventListener('click', play);
})();
(async function() {
// Unchanged from the previous version: fetches the current yearly fee
// from the server so the number on this page always matches reality.
// The #thr-min span is inside Section 1 (01 · PAYMENT).
const chain = location.hash ? location.hash.slice(1) : 'bitcoin';
try {
const r = await fetch('/ranking_info/' + chain);
const data = await r.json();
const val = parseInt(data.amount, 10);
if (!isNaN(val)) {
const txt = Number(val).toLocaleString('en-US');
document.getElementById('thr-min').textContent = txt;
const svgFee = document.getElementById('thr-min-lt');
if (svgFee) svgFee.textContent = txt;
}
} catch (err) {}
})();
</script>
</body>
</html>

View File

@@ -1,133 +1,367 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-<|begin▁of▁sentence|>8" />
<meta name="viewport" content="width=device-w, initial-scale=1.0" />
<title>Bal Server List</title>
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<script src="/qrcode.min.js"></script>
<script src="/purify.min.js"></script>
<script src="/bal.js"></script>
</head>
<style>
table {
border-collapse: collapse;
width: 100%;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
span{
margin-left:10px;
}
</style>
<body>
<div align="center">
<a href="/"><img alt="BAL LOGO" src="/Logo_nero.png"></a>
<h2 id="wename">&nbsp;</h2>
<h3 id="weinfo">&nbsp;</h3>
<h4 id="wechain">&nbsp;</h4>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Will Executor — Server page</title>
<meta name="robots" content="noindex">
<link rel="icon" href="/Logo_nero.ico" type="image/x-icon">
<script src="/purify.min.js"></script>
<script src="/bal.js"></script>
<!--
Server detail page, restyled 2026-11-06 in the look of index.html (same colour
variables and type). Deliberately light: inline CSS only, no new script
(qrcode.min.js was loaded and never used, so it is gone). The data flow is
unchanged: fetchDataAndPopulateTable (bal.js) reads /weinfodata/<chain>/<id>
with ?page/?limit and draws the prev/next links into #paginator; the payment
banner polls the same endpoint until the deposit is credited.
-->
<style>
:root{
--maxw:1120px;--radius:10px;
--bg:#eef1f4;--panel:#ffffff;--ink:#1a2027;--muted:#5b6672;
--line:#dfe4ea;--line-strong:#cbd2da;
--accent:#2d6cdf;--accent-soft:#e6eefc;
--ok:#1f9d57;--ok-soft:#e3f4ea;--off:#9aa4b0;--off-soft:#eceff3;
--display:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--body:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
--shadow:0 1px 2px rgba(20,30,50,.05);
}
*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--bg);color:var(--ink);font-family:var(--body);font-size:15px;line-height:1.5}
a{color:var(--accent)}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 20px}
.mono{font-family:var(--mono)}
header.hero{padding:28px 0 22px;border-bottom:1px solid var(--line);background:var(--panel)}
.hero .wrap{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:14px;text-decoration:none;color:inherit}
.brand .mark{width:44px;height:44px;border-radius:12px;border:1px solid var(--line);display:flex;align-items:center;justify-content:center;background:#fff}
.brand .mark img{width:30px;height:30px;display:block}
.brand h1{font-family:var(--display);font-size:18px;font-weight:600;margin:0;letter-spacing:-.01em}
.brand .sub{color:var(--muted);font-size:13px}
.back-btn{display:inline-flex;align-items:center;gap:8px;padding:9px 14px;border-radius:10px;border:1px solid var(--line-strong);
background:var(--panel);color:var(--accent);font-weight:600;font-size:14px;text-decoration:none;box-shadow:var(--shadow)}
.back-btn:hover{border-color:var(--accent)}
.back-btn svg{width:18px;height:18px}
main{padding:28px 0 40px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:22px 24px}
.server .url{font-family:var(--mono);font-size:22px;font-weight:600;word-break:break-all;margin:0 0 6px}
.server .info{color:var(--muted);margin:0 0 14px}
.badges{display:flex;flex-wrap:wrap;gap:8px}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-family:var(--mono);padding:4px 10px;border-radius:999px;
background:var(--off-soft);color:var(--muted);border:1px solid var(--line)}
.badge.ok{background:var(--ok-soft);color:var(--ok);border-color:#bfe3cd}
.badge.ok::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--ok)}
.badge.bad{background:#fdecec;color:#b42318;border-color:#f5c2c0}
.badge.bad::before{content:"";width:8px;height:8px;border-radius:50%;background:#b42318}
#wepayment{display:none;margin:16px 0 0;padding:10px 14px;border-radius:8px;font-weight:600;font-size:14px}
.grid2{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:20px;margin-top:20px}
.sec-title{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent);font-weight:600;margin:0 0 12px}
.expiry .big{font-family:var(--display);font-size:32px;font-weight:600;line-height:1.1;margin:0}
.expiry .days{color:var(--muted);font-size:13px;margin:2px 0 14px}
.kv{display:grid;grid-template-columns:1fr 1fr;gap:10px 18px;font-size:13px}
.kv div{display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--line);padding-top:8px}
.kv span{color:var(--muted)}
.kv b{font-family:var(--mono);font-weight:600}
.open{color:var(--ok)}
.tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.tile{border:1px solid var(--line);border-radius:8px;padding:12px 14px;background:var(--bg)}
.tile .n{font-family:var(--display);font-size:22px;font-weight:600;line-height:1.1}
.tile .l{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-top:4px}
.tile.wide{grid-column:1 / -1;display:flex;justify-content:space-between;align-items:center;gap:12px}
.tile.wide .n{font-size:13px;font-family:var(--mono)}
.rounds{margin-top:20px;padding:0;overflow:hidden}
.rounds .head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:16px 24px;border-bottom:1px solid var(--line)}
.rounds .head h2{font-family:var(--display);font-size:16px;margin:0}
.rounds .head .note{color:var(--muted);font-size:13px}
.pager{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:13px;color:var(--muted)}
#paginator a{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border:1px solid var(--line-strong);
border-radius:8px;background:var(--panel);color:var(--ink);text-decoration:none;font-weight:600}
#paginator a:hover{border-color:var(--accent);color:var(--accent)}
.pg-off{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border:1px solid var(--line);border-radius:8px;color:var(--off)}
.pg-label{padding:0 4px}
.table-wrap{overflow-x:auto}
table{border-collapse:collapse;width:100%;font-size:13px}
th{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);text-align:left;
padding:10px 10px;border-bottom:1px solid var(--line);background:#fafbfc;white-space:nowrap}
td{padding:10px 10px;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top}
tr:last-child td{border-bottom:0}
td.num,th.num{text-align:right;font-family:var(--mono)}
td.hash{font-family:var(--mono);color:var(--muted);font-size:12px;letter-spacing:-.01em}
.pos-1{color:var(--ok);font-weight:600}
.foot{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;padding:14px 24px;border-top:1px solid var(--line);font-size:13px;color:var(--muted)}
.btn-ghost{font:inherit;font-size:13px;text-decoration:none;padding:8px 12px;border-radius:8px;border:1px solid var(--line-strong);
background:var(--panel);color:var(--ink);display:inline-flex;gap:7px;align-items:center}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
footer{border-top:1px solid var(--line);margin-top:32px;padding:24px 0;color:var(--muted);font-size:13px}
footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px}
footer a{color:var(--accent);text-decoration:none}
@media (max-width:760px){
.grid2{grid-template-columns:1fr}
.tiles{grid-template-columns:repeat(2,1fr)}
.server .url{font-size:18px}
.expiry .big{font-size:28px}
}
</style>
</head>
<body>
<header class="hero">
<div class="wrap">
<a class="brand" href="/">
<span class="mark"><img alt="Bitcoin After Life" src="/Logo_nero.png"></span>
<span><h1>Will Executor — Server List</h1><span class="sub">Bitcoin After Life · server page</span></span>
</a>
<a class="back-btn" href="/"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 17l-5-5 5-5"/><path d="M6 12h6"/><path d="M15 6h4M15 12h4M15 18h4"/></svg>Back to the server list</a>
</div>
<div style="float:left;border:1;margin-right:50px;">
<div><span>Version:</span><span id="weversion">&nbsp;</span></div>
<div><span>Fees:</span><span id="wefees">&nbsp;</span></div>
<div><span>Tld:</span><span id="wetld">&nbsp;</span></div>
</header>
<main class="wrap">
<section class="card server">
<p class="url" id="wename">&nbsp;</p>
<p class="info" id="weinfo">&nbsp;</p>
<div class="badges">
<span class="badge" id="wechain">&nbsp;</span>
<span class="badge"><span>v</span><span id="weversion">&nbsp;</span></span>
<span class="badge" id="westatus-badge"><span id="westatus">&nbsp;</span></span>
</div>
<div id="wepayment"></div>
</section>
<div class="grid2">
<section class="card expiry">
<p class="sec-title">Listing</p>
<p class="big" id="weexpiry">&nbsp;</p>
<p class="days" id="wedays">&nbsp;</p>
<div class="kv">
<div><span>Renewal window</span><b id="wewindow">—</b></div>
<div><span>Balance</span><b id="webalance">—</b></div>
<div><span>Donations</span><b id="wedonations">—</b></div>
<div><span>Domain</span><b id="wetld">—</b></div>
</div>
</section>
<section class="card">
<p class="sec-title">Activity</p>
<div class="tiles">
<div class="tile"><div class="n" id="wepoints">—</div><div class="l">Points</div></div>
<div class="tile"><div class="n" id="wewin">—</div><div class="l">Wins</div></div>
<div class="tile"><div class="n" id="wefees">—</div><div class="l">Fee (sats)</div></div>
<div class="tile"><div class="n" id="weheight">—</div><div class="l">Height</div></div>
<div class="tile wide"><div class="l" style="margin:0">Last update</div><div class="n" id="weupdate">—</div></div>
</div>
</section>
</div>
<div style="float:left;border:1;margin-right:50px;">
<div><span>Height:</span><span id="weheight">&nbsp;</span></div>
<div><span>Points:</span><span id="wepoints">&nbsp;</span></div>
<div><span>Win:</span><span id="wewin">&nbsp;</span></div>
<section class="card rounds">
<div class="head">
<div><h2>WeTime rounds</h2><div class="note">Each round: the block hash sent by this server and how fast it was. Chain: <span class="mono" id="chainth"></span>. The full list is in the download below.</div></div>
<!-- The API supports page offsets; bal.js draws the prev/next links here. -->
<div class="pager" id="paginator"></div>
</div>
<div class="table-wrap">
<table id="wetimes">
<thead>
<tr>
<th class="num">Height</th>
<th>Block hash</th>
<th>Time</th>
<th class="num">Delay</th>
<th class="num">Position</th>
<th class="num">Participants</th>
<th class="num">Points</th>
</tr>
</thead>
<tbody><!-- rows from the script --></tbody>
</table>
</div>
<div class="foot">
<span>Points reward fast answers within the same block; they only break ties in the list.</span>
<a class="btn-ghost" href="" id="download">&#8595; Download JSON</a>
</div>
</section>
</main>
<footer>
<div class="wrap">
<span>Bitcoin After Life — Will Executor List</span>
<a href="/">&larr; Server list</a>
<a href="/terms.html">Terms</a>
<a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>
</div>
<div style="border:1;margin-right:50px;">
<div><span>Balance:</span><span id="webalance">&nbsp;</span></div>
<div><span>Status:</span><span id="westatus">&nbsp;</span></div>
<div><span>Last Update:</span><span id="weupdate">&nbsp;</span></div>
</div>
<div>
<table id="wetimes">
<thead>
<tr>
<th colspan=5><span id="chainth"><!-- --></span>: <span id="paginator"></span></th>
</tr>
<tr>
<th>height</th>
<th>block hash</th>
<th>time</th>
<th>diff</th>
<th>position</th>
<th>participants</th>
<th>points</th>
</tr>
</thead>
<tbody>
<!-- -->
</tbody>
</table>
</div>
<a href="" id="download">Download</a>
<script type="text/javascript">
</footer>
<script type="text/javascript">
// Payment banner: this page has a fixed address, so whoever paid can close the
// invoice and follow the deposit here. It asks the server data every 30 seconds
// until the server is listed. The status only moves forward, because
// unconfirmed_balance may go back to 0 before the deposit is credited.
const PAYMENT_POLL_MS = 30000;
const PAYMENT_POLL_MAX_MS = 24 * 60 * 60 * 1000;
const paymentStarted = Date.now();
let paymentTimer = null, paymentSeen = false, paymentAmount = 0, firstExpiry;
const fmt = n => (n != null ? Number(n).toLocaleString('en-US') : '—');
const pad = n => String(n).padStart(2, '0');
function fmtTime(d) {
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' +
pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
}
// Expiry card: the date big, the days left, the renewal window (from 6 months
// before expiry, Terms Section 1).
function showExpiry(expiryRaw) {
const expiry = (expiryRaw || '').slice(0, 10);
document.getElementById('weexpiry').textContent = expiry ? 'Expires ' + expiry : 'Not listed';
const daysEl = document.getElementById('wedays');
const winEl = document.getElementById('wewindow');
if (!expiry) { daysEl.textContent = 'No listing time: see the Terms to get listed.'; winEl.textContent = '—'; winEl.className = ''; return; }
const exp = new Date(expiry + 'T00:00:00Z');
const win = new Date(exp); win.setUTCMonth(win.getUTCMonth() - 6);
const days = Math.ceil((exp - Date.now()) / 86400000);
daysEl.textContent = days > 0 ? days + ' days of listing left' : 'Listing expired';
const open = Date.now() >= win.getTime() && days > 0;
winEl.textContent = open ? 'open now' : 'opens on ' + win.toISOString().slice(0, 10);
winEl.className = open ? 'open' : '';
}
function showPayment(text, ok) {
const box = document.getElementById('wepayment');
box.textContent = text;
box.style.display = 'block';
box.style.color = ok ? '#14703a' : '#7a5200';
box.style.background = ok ? '#e6f6ec' : '#fdf3dc';
}
function stopPayment() {
if (paymentTimer) clearInterval(paymentTimer);
paymentTimer = null;
}
async function pollPayment() {
if (Date.now() - paymentStarted > PAYMENT_POLL_MAX_MS) return stopPayment();
const path = window.location.pathname.split('/').filter(p => p !== '');
let data;
try {
const response = await fetch('/weinfodata/' + path[1] + '/' + path[2]);
if (!response.ok) return;
data = await response.json();
} catch (err) {
return; // temporary network problem: try again at the next round
}
if (!data) return;
const expiry = data.expiry_date || null;
if (firstExpiry === undefined) firstExpiry = expiry;
const unconfirmed = Number(data.unconfirmed_balance) > 0;
const active = expiry && new Date(expiry.replace(' ', 'T') + 'Z') > new Date();
const bal = data.balance != null ? fmt(data.balance) : null;
const unc = data.unconfirmed_balance != null ? fmt(data.unconfirmed_balance) : null;
document.getElementById('webalance').textContent = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
showExpiry(expiry);
if (expiry && expiry !== firstExpiry) {
showPayment('✓ Payment confirmed: this server is now listed.', true);
stopPayment();
} else if (unconfirmed || paymentSeen) {
paymentSeen = true;
if (unconfirmed) paymentAmount = Number(data.unconfirmed_balance);
showPayment('Payment detected: ' + paymentAmount.toLocaleString('en-US') + ' sats, waiting for 3 confirmations (about 30 minutes). This page updates by itself.', false);
} else if (active) {
stopPayment(); // already listed and nothing incoming: no banner, nothing to follow
} else {
showPayment('Waiting for payment. This page updates by itself.', false);
}
}
document.addEventListener('DOMContentLoaded', () => {
paymentTimer = setInterval(pollPayment, PAYMENT_POLL_MS);
pollPayment();
});
function getWeInfo(){
const path = window.location.pathname.split('/').filter(p => p !== '');
const we_id = path[2];
const chain = path[1];
const cdate=new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
fetchDataAndPopulateTable('/weinfodata/'+chain+"/"+we_id, new URLSearchParams(),(data) => {
document.getElementById('wename').innerHTML = DOMPurify.sanitize(data.url);
document.getElementById('wechain').innerHTML = DOMPurify.sanitize(data.chain);
document.getElementById('weinfo').innerHTML = DOMPurify.sanitize(data.info);
document.getElementById('wefees').innerHTML = data.base_fee;
document.getElementById('weheight').innerHTML = data.last_block;
document.getElementById('wepoints').innerHTML = data.points;
document.getElementById('wewin').innerHTML = data.count_win;
document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld);
document.getElementById('webalance').innerHTML = data.balance;
document.getElementById('weversion').innerHTML = data.version;;
document.getElementById('westatus').innerHTML = DOMPurify.sanitize(data.status);
document.getElementById('weupdate').innerHTML = data.last_update;
document.getElementById('wename').textContent = data.url || '—';
document.title = (data.url || 'Server') + ' — Will Executor';
document.getElementById('wechain').textContent = data.chain || chain;
document.getElementById('weinfo').textContent = data.info || '';
document.getElementById('wefees').textContent = fmt(data.base_fee);
document.getElementById('weheight').textContent = fmt(data.last_block);
document.getElementById('wepoints').textContent = fmt(data.points);
document.getElementById('wewin').textContent = fmt(data.count_win);
document.getElementById('wetld').textContent = data.tld || '—';
const bal = data.balance != null ? fmt(data.balance) : null;
const unc = data.unconfirmed_balance != null ? fmt(data.unconfirmed_balance) : null;
document.getElementById('webalance').textContent = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
document.getElementById('wedonations').textContent = fmt(data.donation);
document.getElementById('weversion').textContent = data.version || '—';
document.getElementById('weupdate').textContent = data.last_update || '—';
showExpiry(data.expiry_date);
const statusMap = {
'OK': 'Health check passed',
'TIMEOUT': 'Connection timeout',
'CONNECTION': 'Connection error',
'STATUS': 'HTTP error',
'WRONG REPLY': 'Invalid JSON response',
'REQUEST': 'Request error',
'BODY': 'Body read error',
'DECODE': 'Decode error',
'KO': 'Generic error'
};
const st = (data.status||'').split(':')[0];
document.getElementById('westatus').textContent = statusMap[st] || (data.status ? 'Unknown status' : '—');
document.getElementById('westatus-badge').className = 'badge ' + (st === 'OK' ? 'ok' : (st ? 'bad' : ''));
const tableBody = document.querySelector('#wetimes tbody');
tableBody.innerHTML ='';
Object.entries(data.times).forEach(([key,wetime]) => {
const row = document.createElement('tr');
const d = new Date(wetime.time/1000);
row.innerHTML = `
<td>${wetime.blockHeight}</td>
<td>${DOMPurify.sanitize(wetime.blockHash)}</td>
<td>${d}</td>
<td>${wetime.diff/1000000} s</td>
<td>${wetime.position+1}</td>
<td>${wetime.participants}</td>
<td>${wetime.points}</td>
`;
tableBody.appendChild(row);
})
var prev=0;
var next=0;
if (data.times.length > 0){
prev = data.times[0].blockHeight+data.times.length;
next = data.times[data.times.length-1].blockHeight;
if (prev < 0 ){
prev = 0;
}
if (data.times && data.times.length) {
Object.entries(data.times).forEach(([key,wetime]) => {
const row = document.createElement('tr');
const d = new Date(wetime.time/1000);
const hash = DOMPurify.sanitize(wetime.blockHash||'');
const pos = wetime.position != null ? wetime.position + 1 : null;
row.innerHTML = `
<td class="num">${wetime.blockHeight != null ? fmt(wetime.blockHeight) : '—'}</td>
<td class="hash">${hash || '—'}</td>
<td>${fmtTime(d)}</td>
<td class="num">${wetime.diff != null ? (wetime.diff/1000000).toFixed(3) + ' s' : '—'}</td>
<td class="num ${pos === 1 ? 'pos-1' : ''}">${pos != null ? '#' + pos : '—'}</td>
<td class="num">${wetime.participants != null ? wetime.participants : '—'}</td>
<td class="num">${wetime.points != null ? wetime.points : '—'}</td>
`;
tableBody.appendChild(row);
});
} else {
tableBody.innerHTML = '<tr><td colspan="7" style="padding:24px;text-align:center;color:var(--muted)">No rounds yet.</td></tr>';
}
const paginator = {prev:prev,next:next};
return paginator;
const urlParams = new URLSearchParams(window.location.search);
const currentPage = Math.max(parseInt(urlParams.get('page') || '0', 10) || 0, 0);
const pageSize = Math.min(Number(limit) || 100, 100);
const hasNext = !!data.times && data.times.length >= pageSize;
return {
page: currentPage,
prev: Math.max(currentPage - 1, 0),
next: currentPage + 1,
hasPrev: currentPage > 0,
hasNext: hasNext,
};
},{elementName:"download",fileName:"weinfo_"+cdate+".json",linkText:"download"})
}
}
getWeInfo();
</script>
</body>
</body>
</html>