Merge Gashboard ngit proposal with native access and durable chat

Reviewed proposal acd65f3c (pr/gashboard), exact head 4e167cbcf8. Preserve current catalogs including DATUM and normalize new app memory limit to supported 512m.

Validation: all six focused API security/persistence tests, API TypeScript check and strict release catalog drift pass. No real chat/member changes, miner shares, payout operations, node restart or publication performed by this integration.
This commit is contained in:
archipelago
2026-10-08 18:52:11 -04:00
101 changed files with 11232 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
# Gashboard on Archipelago
Install DATUM and configure its payout address, then install Gashboard. The app
connects to `http://datum:7152` on `archy-net`, so recreating DATUM does not require
copying a new container IP. The shared DATUM admin password is injected as a
platform secret and never sent to the browser. This PR depends on the DATUM app
package being merged and its build context being shipped.
## Sign in and invite miners
Use **Sign in with Nostr** inside Archipelago to choose a node identity through
the native signer. A standalone visitor can use a browser extension or remote
Nostr signer. No private key is entered into Gashboard.
All user identities offered by Archipelago's signer are dashboard owners through
`NODE_IDENTITY_PUBKEYS`; the appliance identity is excluded. Owners can open
**Access**, paste another miner's `npub`, and add them as a viewer. Share the
Gashboard URL on port 1337 over your intended node access route. Viewers can read
the entire fleet and join dashboard chat; they cannot edit membership or configure
DATUM. Access is independent of the miner's Stratum worker name. Signing with an
unlisted identity is rejected even if that person mines through DATUM.
Owners can remove a viewer in Access. Every authenticated request rechecks the
list, so an already-issued JWT stops working immediately. Removing a viewer does
not erase information or chat keys that their browser previously received.
Membership lives in `/var/lib/archipelago/gashboard/access.json`; preserve this
directory and the platform JWT secret across reinstall. Node owners are managed
in Archipelago identities, not in Gashboard. Restart Gashboard after changing
node identities to refresh owner access.
The app gate uses `auth: open` because invited miners have Gashboard membership,
not node administrator accounts. Gashboard still authenticates every data API.
The gate supplies HTTPS and iframe header handling. A node administrator can
enable the gate's extra login if only node users should reach the app.
## Source and native signer
`docker/gashboard` vendors the user-supplied Gashboard source at commit
`68b606b` from `/home/yaya/Projects/gashboard`, with Archipelago integration and
membership changes reviewed here. Its configured remote,
`https://git.tx1138.com/lfg2025/gashboard.git`, was unavailable over TLS during
preparation; upstream freshness has not been verified. Vendoring makes the build
independent of that server. The original application declares the MIT license.
The image bakes the canonical `neode-ui/public/nostr-provider.js`; the install
hook refreshes its persisted copy from the node. Refresh the baked copy when
updating the package. The server serves the provider uncached with
`data-app-id="gashboard"` and `data-no-nip98`, preserving Gashboard's own NIP-98
login. The service worker never caches the signer. Existing NIP-07 browser
providers take precedence over the node provider. The CSP permits the native
signer broker frame in standalone/companion launches.
`/healthz` checks that the dashboard API is serving. DATUM connection failures are
reported in the dashboard snapshot rather than disguised as a healthy mining
fleet. Contribution history persists under `/data`; live miner connections and
current hash rate recover through repeated DNS-based polling. Miner display names
come from their worker names, and history uses the full Stratum username so
multiple miners of the same model are not combined under a preset nickname.
Use a distinct worker name for each miner. Old Umbrel history should not be
copied blindly: its ledger used preset nicknames as identities.
Before release, validate HTTP/HTTPS iframe launch, companion launch, native
identity consent, invited-user login, revocation, DATUM address change/recovery,
and install/start/stop/reinstall/reboot on a dedicated Archipelago test node.
## Local validation (2026-10-06)
API access-control and worker-identity tests, TypeScript checks, production builds,
manifest validation and generated catalog drift checks pass. Both container images
build and run with read-only roots, cap-drop ALL and no-new-privileges on Docker.
Gashboard's digest-authenticated polling recovered after DATUM moved from
172.22.0.2 to 172.22.0.4, without restarting or reconfiguring Gashboard, and after
another DATUM restart with preserved settings.
The access/login flow passed Chromium 153, Firefox 155 and WebKit 26.6, each at
1440x900 and 390x844: native-provider NIP-98 through a simulated host frame,
invalid-key feedback, invitation, reload persistence, removal, and layout fit.
API tests also verify owner-only edits, rejected outsider login, persisted
membership, owner protection, corruption refusal, and revoked JWT/SSE access.
Browser scenarios and screenshots remain outside the repository in the shared
browser-check workspace. These browser tests simulate the app gate and signer
host; they do not establish real-node consent, HTTPS or Android acceptance.
The generated storefront entries are review candidates. No signed catalog,
registry image or release was published. Keep actual-node acceptance
separate from these local results.
## Operator-authorized node deployment (2026-10-06)
Installed alongside DATUM on archi-dev-box and yaya-server using rootless Podman,
read-only roots and the node-generated secrets. Both apps report healthy. Their
My Apps tiles show normalized icons, names and Launch controls. Chromium verified
Gashboard's embedded launch, native identity selection/signing, and owner Access
page on both nodes. Standalone native login and fresh DATUM polling passed too.
On yaya, Chromium 153, Firefox 155 and WebKit 26.6 passed owner login, Access-page
layout and reload persistence at 1440x900 and 390x844. These are Linux browser and
viewport checks, not Android companion or physical iPhone acceptance. Anonymous
requests to mining data and membership endpoints returned 401. No viewers were
added to the live allowlist during these read-only UI checks.
DATUM's normal package restart on yaya changed its address from 10.89.0.9 to
10.89.0.11; Gashboard was not restarted or reconfigured and its authenticated stats
API returned a successful poll less than five seconds old afterwards. Gashboard
also completed its own normal package restart. The previous Docker tests cover
invitation, revocation and membership persistence; full live-node membership,
HTTPS, companion, preserved-data reinstall and reboot acceptance remain separate.
Payouts are not configured and no real miner shares were submitted in this check.
These are node test deployments of review candidates, not catalog publication.
### Private chat persistence and invitations
Encrypted messages, reactions and per-recipient room-key wraps are saved atomically
in `/data/chat.json` (mode 0600), alongside the viewer list. The server never saves
the plaintext room key or decrypted messages. Back up the whole app data directory;
keep chat history and key wraps together. Invalid saved chat data stops startup
instead of silently discarding history.
An existing member with chat open shares the existing room key with newly invited
viewers. If no existing member is online, the new viewer sees a pending-key message;
an existing member must open chat, then the viewer can reopen the panel. A new
viewer or simultaneous first visitor cannot replace the established key. Existing
history becomes readable to invited viewers. Revoking membership blocks API access
but cannot erase a key or history already received by that viewer.
When upgrading from 0.2.0, export the authenticated `/api/chat` snapshot to
`/data/chat.json` before stopping the old container: that version holds chat only
in memory. Preserve the snapshot and data-directory backup through the upgrade.
+90
View File
@@ -0,0 +1,90 @@
app:
id: gashboard
name: Gashboard
version: 0.2.1
description: A playful mining dashboard for your DATUM fleet, with live hashrates, share history, lottery odds, chat and a Nostr viewer access list.
upstream:
kind: internal
container:
build:
context: /opt/archipelago/docker/gashboard
dockerfile: Dockerfile
tag: localhost/archipelago-gashboard:0.2.1
network: archy-net
data_uid: "1000:1000"
derived_env:
- key: NOSTR_OWNER_PUBKEYS
template: "{{NODE_IDENTITY_PUBKEYS}}"
generated_secrets:
- name: gashboard-jwt-secret
kind: hex32
secret_env:
- key: JWT_SECRET
secret_file: gashboard-jwt-secret
- key: DATUM_ADMIN_PASSWORD
secret_file: datum-admin-password
install_prerequisites: [datum]
dependencies:
- app_id: datum
- storage: 1Gi
resources:
cpu_limit: 1
memory_limit: 512m
disk_limit: 1Gi
security:
capabilities: []
readonly_root: true
no_new_privileges: true
network_policy: isolated
ports:
- host: 1337
container: 1337
protocol: tcp
bind: 127.0.0.1
auth: open
auth_rationale: Gashboard verifies NIP-98 signatures and an owner-managed Nostr access list before issuing sessions. Every data route rechecks membership; invited miners must not need a node administrator login.
volumes:
- type: bind
source: /var/lib/archipelago/gashboard
target: /data
options: [rw]
- type: tmpfs
target: /tmp
tmpfs_options: rw,noexec,nosuid,size=16m
environment:
- NODE_ENV=production
- PORT=1337
- DATUM_URL=http://datum:7152
- DATUM_ADMIN_USER=admin
- CONTRIBUTION_LEDGER_PATH=/data/contribution-ledger.json
- ACCESS_LIST_PATH=/data/access.json
- CHAT_STORE_PATH=/data/chat.json
- ARCHIPELAGO_PROVIDER_PATH=/data/nostr-provider.js
- MEMPOOL_API_URL=https://mempool.space/api
hooks:
post_install:
- copy_from_host:
src: web-ui/nostr-provider.js
dest: /data/nostr-provider.js
- exec: ["test", "-s", "/data/nostr-provider.js"]
health_check:
type: http
endpoint: http://localhost:1337
path: /healthz
interval: 30s
timeout: 5s
retries: 3
interfaces:
main:
name: Mining dashboard
type: ui
port: 1337
protocol: http
path: /
metadata:
icon: /assets/img/app-icons/gashboard.svg
category: bitcoin
tier: optional
repo: https://gitworkshop.dev/npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg/relay.ngit.dev/archy
launch:
open_in_new_tab: false