Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
b57fdda
Design the chain pages instead of printing their API responses
Aug 29, 2026
ded4ae8
Say in public that this is a three-chain explorer
Aug 29, 2026
fd490f6
Give the paged lists a way to reach page two, and print times people …
Aug 29, 2026
81e7820
Photograph the five Universe pages nobody had photographed, and fix w…
Aug 29, 2026
0b0db85
One evidence vocabulary, not two
Aug 29, 2026
be8efc3
Draw the trust boundary, and stop sending contributors to the wrong p…
Aug 29, 2026
a170df2
Correct the search privacy claim, which stopped being true
Aug 29, 2026
1487ed5
Stop calling Dogecoin pages Bitcoin, in the tab and in the shared link
Aug 29, 2026
747613f
Put the metadata fix in the file that is not thrown away
Aug 29, 2026
f1b369f
Readable is not the same as current, and the README should not blur them
Aug 29, 2026
683dd12
Say how many columns the table left out
Aug 29, 2026
ea023b6
Stop pointing people at an issue tracker that is switched off
Aug 29, 2026
8ce3db8
Refuse a matrix run whose build moved underneath it
Aug 29, 2026
15f60ba
Read the real API, and fix the three things the fixtures could not show
Aug 29, 2026
a7e5aef
Say none, rather than showing nothing
Aug 29, 2026
b615b49
Stop telling a Dogecoin page it is about Bitcoin
Aug 29, 2026
7d845f7
Make the fixture addresses look like addresses
Aug 29, 2026
2af6b84
Write down the shape that cost this release four defects
Aug 29, 2026
71ed486
A contrast probe that threw and one that found nothing both printed zero
Aug 29, 2026
0b4bc09
Correct the count, and name the two that were in the gates
Aug 29, 2026
c8dad30
Merge remote-tracking branch 'origin/develop' into design/universe-ex…
bitcoinuniverseadmin Aug 29, 2026
6c0b857
Merge remote-tracking branch 'origin/develop' into design/universe-ex…
bitcoinuniverseadmin Aug 29, 2026
ecb4d02
Say which part is missing, not only that something is
bitcoinuniverseadmin Aug 29, 2026
84ed833
Open the chain switcher, which nothing had ever opened
bitcoinuniverseadmin Aug 29, 2026
4fbda57
A gate that read nothing said what a gate that found nothing says
bitcoinuniverseadmin Aug 29, 2026
4944817
The brand mark is not a status light, and a filled badge needs ink
bitcoinuniverseadmin Aug 29, 2026
381a92f
Read a Zcash block, which production printed field by field
bitcoinuniverseadmin Aug 29, 2026
ae8e7f1
Stop a superseded commit from holding the runner its replacement wait…
bitcoinuniverseadmin Aug 29, 2026
0f6b2eb
Name the security channel, which the documentation never did
bitcoinuniverseadmin Aug 29, 2026
8e19d7f
Retake the overview screenshot the fixture corrections made stale
bitcoinuniverseadmin Aug 29, 2026
37e3ec8
Stop the failure fixtures photographing a disagreement production can…
bitcoinuniverseadmin Aug 29, 2026
bb75e74
Read a Zcash address, which had a balance and showed none
bitcoinuniverseadmin Aug 29, 2026
a9b8b77
Photograph a Zcash block that carries something
bitcoinuniverseadmin Aug 29, 2026
9aeae84
Stop a three minute job holding a runner the fifty minute one waits for
bitcoinuniverseadmin Aug 29, 2026
25f1392
Run the five chain failure states, which nothing had ever run
bitcoinuniverseadmin Aug 29, 2026
d685ab1
Stop counting, and name the two gates that read the build
Aug 29, 2026
628c029
My own table claimed it held nothing back while dropping the ledger
Aug 29, 2026
c13f8e5
The restart test asserted an empty port it never owned
Aug 29, 2026
cef3aff
Design the chain pages instead of printing their API responses
bitcoinuniverseadmin Aug 29, 2026
dcbf208
Production smoke had no idea the chain domain existed
Aug 29, 2026
62b5075
Write down what the matrix actually costs, not what it cost once
Aug 29, 2026
257702b
Merge pull request #19 from bitcoinuniverseio/qa/record-the-measured-…
bitcoinuniverseadmin Aug 29, 2026
bea93c1
Merge pull request #18 from bitcoinuniverseio/qa/chain-smoke-coverage
bitcoinuniverseadmin Aug 29, 2026
c0531b9
The overview said address history was offered and could not be read
bitcoinuniverseadmin Aug 29, 2026
0210d5c
Nothing had ever looked at what the origin renders
bitcoinuniverseadmin Aug 29, 2026
2b81f36
Three components publish a commit and the document named one
bitcoinuniverseadmin Aug 29, 2026
9218ae6
A label stretched to the far side of its cell is not bound to its value
bitcoinuniverseadmin Aug 29, 2026
834bc4d
The chain pages were absent from the forced colours and zoom pass
bitcoinuniverseadmin Aug 29, 2026
57392dd
Retake the overview screenshot the coverage panel made stale
bitcoinuniverseadmin Aug 29, 2026
fad6088
Nothing wrote down what a release was supposed to be
bitcoinuniverseadmin Aug 29, 2026
2afacf2
Merge pull request #22 from bitcoinuniverseio/fix/chain-dashboard-rel…
bitcoinuniverseadmin Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions .github/workflows/universe-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ permissions:

concurrency:
group: universe-ci-${{ github.ref }}
cancel-in-progress: false
# A superseded commit on a pull request proves nothing about the commit that
# replaced it, and while its run continues it holds one of the six ultra
# runners the replacement is queued for. Pushing three times to a branch
# under review used to mean three full matrix runs in a queue, and only the
# last one was worth waiting for.
#
# Pushes to develop and main are never cancelled. Every commit on those is
# validated on its own, because promotion reads per-commit check results and
# the release artifact is built from main.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
docs:
name: Documentation gates
runs-on: [self-hosted, linux-ultra]
# Any runner in the fleet. This job is a checkout, a Node install and a set
# of Node scripts: no browser, no Rust, no Docker. Pinned to `linux-ultra`
# it competed for the six runners that are the only ones able to run the
# visual matrix, while eight runners carrying `universe-ci` sat idle, and a
# three minute job held a slot the fifty minute one was queued for.
runs-on: [self-hosted, universe-ci]
timeout-minutes: 10
steps:
- name: Checkout
Expand Down Expand Up @@ -57,6 +71,9 @@ jobs:
- name: Gateway routing
run: node --test scripts/universe/gateway.test.mjs

- name: The component identity check can still fail
run: node --test scripts/universe/release-manifest.test.mjs

- name: The gateway bridges an upstream restart rather than failing
run: node --test scripts/universe/gateway-restart.test.mjs

Expand Down Expand Up @@ -160,6 +177,14 @@ jobs:
working-directory: scripts/universe/visual-qa
run: node --test progress-gate.test.mjs

# The public chain page gate, checked against the states it exists for.
# It runs against the live origin rather than this build, so nothing in
# this job exercises it, and a gate that has only ever passed has not
# shown it can fail. Every case it covers shipped past a green suite.
- name: The public chain page gate fires on the states that shipped
working-directory: scripts/universe/visual-qa
run: node --test chain-page-audit.test.mjs

# A fixed port here once measured another job's build: two runners share
# a host, the second gateway crashed on the taken port behind "&", the
# probe happily reached the first job's server, and the run died halfway
Expand Down Expand Up @@ -192,8 +217,30 @@ jobs:
# finishes show up. The deep pass takes the routes this suite holds to a
# terminal state through every failure state, every theme and the widths
# that change the layout, which is where a page that lies about why it is
# empty shows up. Measured at about two and a half minutes and sixteen
# minutes respectively.
# empty shows up.
#
# Measured on run 33254326169, 2026-08-29, on universe-linux-ultra-02:
#
# broad pass 64 screenshots 5m50s
# deep pass 504 screenshots 47m10s
# chain failures 48 screenshots 3m27s
# forced colours and zoom 30s
# whole job 59m57s of a 100 minute cap
#
# This comment said two and a half minutes and sixteen. The deep pass has
# since grown to roughly three times that, it is now 78 percent of the
# job, and the job sits at 60 of its 100 minutes. Nothing is failing. The
# point of writing the real numbers down is that the next person to add a
# route, a state or a theme to the deep pass is the one who meets the
# cap, and without this they meet it as a timeout forty-seven minutes
# into a run rather than as a number they could have added up first.
#
# The deep pass is routes multiplied by states multiplied by themes
# multiplied by widths. Adding one route to it currently costs about
# seven minutes. If it needs to grow again, split it the way it was split
# from the broad pass in the first place, rather than raising the cap:
# a gate that cannot finish protects nothing, which is the reason the
# split exists.
- name: Every route renders and finishes, at a phone and a desktop width
working-directory: scripts/universe/visual-qa
run: >-
Expand All @@ -206,6 +253,25 @@ jobs:
node capture.mjs --base=http://127.0.0.1:$GATEWAY_PORT --out=./artifacts
--routes=graphs,mining,protocols,home,blocks,tx,address --viewports=320,768,1440

# The chain failure states have never run here. They are scoped to the
# chain routes, so the pass above never reaches them, and they were
# reviewed by hand instead: a chain that cannot be reached, one that is
# behind, one whose node is caught up while its protocol side is not, one
# that withholds readiness without saying why, and an object that simply
# is not there. Those five are the states the whole status vocabulary
# exists for, and the one set nothing measured.
#
# Two themes and two widths rather than three and three, because the cost
# has to fit beside the pass above and this is the part of the matrix
# that catches wording and state, not layout.
- name: A chain that cannot answer says so, in every failure state
working-directory: scripts/universe/visual-qa
run: >-
node capture.mjs --base=http://127.0.0.1:$GATEWAY_PORT --out=./artifacts-chain-states
--routes=dogecoin,dogecoin-drc20,zcash,zcash-block
--states=chain-authority-down,chain-behind,chain-not-ready-protocols,chain-not-ready-unexplained,chain-object-missing
--themes=default,dark --viewports=375,1440

- name: Forced colours and 200 percent zoom
working-directory: scripts/universe/visual-qa
run: node modes-check.mjs --base=http://127.0.0.1:$GATEWAY_PORT
Expand All @@ -218,4 +284,5 @@ jobs:
path: |
scripts/universe/visual-qa/artifacts
scripts/universe/visual-qa/artifacts-broad
scripts/universe/visual-qa/artifacts-chain-states
retention-days: 30
34 changes: 33 additions & 1 deletion .github/workflows/universe-production-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ jobs:
smoke:
name: Live origin answers with real content
runs-on: [self-hosted, linux-ultra]
timeout-minutes: 10
# Ten was for a job that only read the API. It now installs the browser
# harness and loads two pages in a real browser, which is the only way to
# see what the origin renders.
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
Expand All @@ -42,3 +45,32 @@ jobs:

- name: Every advertised feature has routes, data, and a matching release
run: node scripts/universe/synthetic-check.mjs "${{ github.event.inputs.origin || 'https://explorer.bitcoinuniverse.io' }}"

# The step above reads the API. It cannot see what the origin renders, and
# for as long as it could not, production served a chain dashboard whose
# status line ran six facts together behind a raw snapshot identifier,
# never said why Dogecoin was degraded, and published "Release
# development" to the public, while every check here passed. The API was
# answering correctly the whole time; the origin was serving an older
# build than anything measured it.
- name: Install the visual QA harness
working-directory: scripts/universe/visual-qa
run: npm ci

- name: The gate can still tell a bad page from a good one
working-directory: scripts/universe/visual-qa
run: node --test chain-page-audit.test.mjs

- name: The public chain pages say what the chain documents say
working-directory: scripts/universe/visual-qa
run: >-
node chain-page-smoke.mjs
--origin="${{ github.event.inputs.origin || 'https://explorer.bitcoinuniverse.io' }}"

- name: Keep what the chain pages looked like
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: universe-chain-page-smoke
path: scripts/universe/visual-qa/artifacts-chain-smoke
retention-days: 30
13 changes: 12 additions & 1 deletion .github/workflows/universe-release-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ jobs:
- name: No em dash in what the build actually ships
run: node scripts/universe/check-text.mjs frontend/dist

- name: The component identity check can still fail
run: node --test scripts/universe/release-manifest.test.mjs

# release.sh reads the frontend from frontend/build, the backend from
# backend/dist, and the gateway from scripts/universe. node_modules and
# the compiled gbt module are not packed: install hard-links them from the
Expand Down Expand Up @@ -102,11 +105,19 @@ jobs:
mkdir -p "$stage/production"
cp -a production/linux "$stage/production/linux"
test -f "$stage/production/linux/universe-explorer-gateway.socket"
# What this release is, written down once and carried with it. The
# frontend, the explorer backend and the gateway in this artifact are
# one commit, and stating so is what makes an origin serving two of
# them from different releases a detectable fault rather than an open
# question. release.sh refuses an install whose manifest names a
# different commit, and the cutover holds the live origin to it.
node scripts/universe/release-manifest.mjs emit --commit='${{ steps.sha.outputs.sha }}' --out="$stage/RELEASE-MANIFEST.json"
test -f "$stage/RELEASE-MANIFEST.json"
test -f "$stage/backend/dist/index.js"
test -f "$stage/frontend/build/index.html"
test -f "$stage/scripts/universe/gateway.mjs"
out="$PWD/mempool-$sha.tar.gz"
tar -czf "$out" -C "$stage" backend frontend scripts production
tar -czf "$out" -C "$stage" backend frontend scripts production RELEASE-MANIFEST.json
sha256sum "$out" | tee "$out.sha256"
printf 'name=mempool-%s\n' "$sha" >> "$GITHUB_OUTPUT"

Expand Down
86 changes: 84 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,87 @@
# Contributing to Universe Explorer

This repository is Bitcoin Universe's fork of the Mempool Open Source Project,
released under the AGPL. Contributions to it are contributions to this fork.
The upstream project's own contribution terms are preserved further down, and
they govern work you submit to that project rather than to this one.

## Where work happens

`develop` is the working branch. Open a pull request against it. `main` is the
released branch and is protected: it only ever moves through a promotion from
`develop`.

The issue tracker is turned off, so a pull request is the only durable place to
raise something. That is deliberate: a change, its reasoning, its review, and
its checks stay in one record. Open one even for a report you cannot fix
yourself, with an empty diff if you have nothing to change yet.

## Scope conventions

Universe changes live in `frontend/src/app/universe/`, `scripts/universe/`, and
`docs/`, and reach the inherited application through a small number of
documented integration points. Keeping them there is what makes upstream
security fixes easy to take. `UPSTREAM.md` records every subsystem this fork
modifies and the known conflict points, and
`docs/operations/UPSTREAM-SYNC.md` is the synchronization procedure.

## What the checks hold you to

Run these before opening a pull request. They are the same ones
`.github/workflows/universe-ci.yml` runs.

```bash
cd frontend && npm ci && npm run lint && npm run test && npm run build:universe
cd backend && npm ci && npm run lint && npm run test:ci
node scripts/universe/generate-protocol-coverage.mjs --check
node scripts/universe/check-text.mjs
node scripts/universe/check-colors.mjs
node scripts/universe/check-palettes.mjs
node scripts/universe/check-fills.mjs
node scripts/universe/check-branding.mjs
node scripts/universe/check-origins.mjs
node --test scripts/universe/gateway.test.mjs
```

Four of them are easy to trip by accident, so they are enforced rather than
remembered:

- **No em dash.** Use a colon, a comma, or two sentences. The word "canonical"
is also out of the vocabulary here.
- **No raw interface colour.** Everything comes from the tokens in
`frontend/src/styles/_universe-tokens.scss`, including in style bindings.
Every strong fill declares the ink that goes on it.
- **No third-party data origin.** Not in the source, not in the built bundle,
not as a fallback. `docs/data/ASSET-EVIDENCE.md` explains why.
- **No obsolete upstream product mark** outside the allowlist recorded in
`docs/legal/TRADEMARK-AUDIT.md`.

## If you add or change a page

Add it to the visual matrix in `scripts/universe/visual-qa/`. A route with no
fixture is a route no screenshot, contrast probe or unfinished-page check ever
looks at, and every defect this product has shipped visually was on a surface
in exactly that position. `docs/product/DESIGN-SYSTEM.md` has the account of
what that has cost, at the end.

## Writing

`docs/product/DESIGN-SYSTEM.md` has the rules the copy is held to. The short
version: say what is true and no more, distinguish "there is none" from "we
could not tell", keep exact figures exact, and explain a term at the moment it
matters rather than in a glossary.

## Reporting a vulnerability

Through GitHub's private vulnerability reporting for this repository:
[open a report](https://github.com/bitcoinuniverseio/mempool/security/advisories/new).
Not in a pull request, and not in a commit message, because both are public as
soon as they are written. [SECURITY.md](SECURITY.md) has what to include and
what is in scope, and `docs/security/THREAT-MODEL.md` records the trust
boundaries this deployment assumes.

---

# Contributing to The Mempool Open Source Project

Thank you for contributing to The Mempool Open Source Project managed by Mempool Space K.K. (“Mempool”).
Expand Down Expand Up @@ -47,5 +131,3 @@ Should You wish to submit work that is not Your original creation, You may submi
### 8. Notifications

You agree to notify Mempool of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.

EOF
Loading
Loading