Cargo workspace plus a Vite frontend, following ~/git/architecture/generic.md. Every block header carries its author's wormhole reward preimage in a `pow_` PreRuntime digest, so authorship for the whole network is derivable from headers alone — no indexer, no registration, no way for a miner to be left out. That decoding, the hashrate maths and the telemetry name attribution live in blackbeard-core with no I/O at all, so the parts that are easy to get subtly wrong are exercised by unit tests rather than only against a live chain. The browser holds one WebSocket: snapshot on subscribe, deltas thereafter. The head stream is itself a push (chain_subscribeNewHeads), so a block reaches the page the moment the node imports it. Messages are serialised once per broadcast, and leaderboards are recomputed only for windows a socket is actually watching. No RxJS — useSyncExternalStore is React's own contract for this. Verified against the live Planck testnet: 12/12 headers decoded, telemetry names attributed (quanpool-planck, baba-gorchitsa, …), warm start restoring 84 blocks and 5 held names across a restart. Three findings worth recording, all in CLAUDE.md: - substrate-telemetry sends its JSON in *binary* frames. A text-only client connects, subscribes, reports healthy and receives nothing at all — and a Python probe hides it, because json.loads accepts bytes. - Difficulty is a little-endian U512; decoding it big-endian gives a number wrong by ~10^150 that still renders fine. - Planck's real block interval is ~13-15s against a 6s target with enormous variance, so a measured interval needs 20 tip samples before it is publishable. Deploy assets, the Gitea Actions workflow and script/infra-setup.sh are included; port 25864 is registered in architecture/port-allocations.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSDYiibCtELsrjQq6KXnoi
40 lines
1.6 KiB
SQL
40 lines
1.6 KiB
SQL
-- Idempotent role and database creation for blackbeard.observer.
|
|
--
|
|
-- Run once, by an operator, on the Postgres PRIMARY only (magrathea) —
|
|
-- replication carries roles and databases to the standby. Applied by
|
|
-- script/infra-setup.sh --database.
|
|
--
|
|
-- No password is set on the role, deliberately and permanently. Authentication
|
|
-- is mTLS: the app host's certificate CN maps to this role through a
|
|
-- pg_ident.conf drop-in, which infra-setup.sh installs on BOTH servers. A
|
|
-- standby missing that mapping locks the app out on failover
|
|
-- (architecture/generic.md §5).
|
|
|
|
do $$
|
|
begin
|
|
if not exists (select from pg_roles where rolname = 'blackbeard_rw') then
|
|
create role blackbeard_rw with login;
|
|
end if;
|
|
-- A read-only role for ad-hoc queries and any future reporting, so nothing
|
|
-- has to borrow the writer's credentials to look at the data.
|
|
if not exists (select from pg_roles where rolname = 'blackbeard_ro') then
|
|
create role blackbeard_ro with login;
|
|
end if;
|
|
end
|
|
$$;
|
|
|
|
-- `create database` cannot run inside a transaction block or a DO block, so the
|
|
-- caller guards it: infra-setup.sh checks first and skips if present.
|
|
|
|
\connect blackbeard
|
|
|
|
-- The writer owns the schema so sqlx migrations can create tables.
|
|
alter schema public owner to blackbeard_rw;
|
|
grant usage on schema public to blackbeard_ro;
|
|
|
|
-- Applies to tables the migrations have not created yet, so a new migration
|
|
-- does not need this file re-run.
|
|
alter default privileges for role blackbeard_rw in schema public
|
|
grant select on tables to blackbeard_ro;
|
|
grant select on all tables in schema public to blackbeard_ro;
|