| … | … | @@ -6,6 +6,84 @@ documented here. Dates in YYYY-MM-DD. |
| 6 | 6 | |
| 7 | 7 | The packages are versioned in lockstep. |
| 8 | 8 | |
| 9 | ## [0.25.0-rc.7] — 2026-07-28 | |
| 10 | ||
| 11 | **Security release. Supersedes rc.6 — deploy this.** | |
| 12 | ||
| 13 | ### Fixed — security | |
| 14 | ||
| 15 | - **Every default relay granted anonymous swarm peers the `authenticated-user` | |
| 16 | role.** Commit `9125f3c` ("default services to anonymous role") set | |
| 17 | `'anonymous'` in `relay-node/index.js` and `bare-relay.js` but not in | |
| 18 | `config/default.js` — and that is the file the CLI loads, merged last, so it | |
| 19 | won. Any relay started from the CLI, which is every packaged path, silently | |
| 20 | ran with the raised default. | |
| 21 | ||
| 22 | Effect: `protocol.js` handed any unauthenticated peer the `authenticated-user` | |
| 23 | role, and the service router then opened `arbitration.submit` — whose pending | |
| 24 | Map is uncapped — to the whole swarm. Verified before and after with | |
| 25 | `HOME=$(mktemp -d) loadConfig({})`: `"authenticated-user"` → `"anonymous"`. | |
| 26 | No test asserted the value in either file, which is why the revert went | |
| 27 | unnoticed; there is one now, and it asserts the *effective* value so it still | |
| 28 | fails if precedence between the two defaults changes again. | |
| 29 | ||
| 30 | - **`dashboard-admin-operator` was an unauthenticated fleet-root control plane.** | |
| 31 | It holds every relay's SSH key and its endpoints restart services, run | |
| 32 | `git pull && npm install`, rewrite `config.json` and edit `/etc/fstab` — while | |
| 33 | listening on `0.0.0.0` with zero authentication and wildcard CORS. Its own | |
| 34 | README says "do not expose port 3458". Now binds `127.0.0.1`, requires a | |
| 35 | shared secret (timing-safe) on every route including the SSE stream that | |
| 36 | leaked the inventory, drops wildcard CORS, and uses `execFile(argv)` instead | |
| 37 | of interpolating a caller-supplied `publicIp` into a shell command that the | |
| 38 | probe re-ran every 60s. Host and name are validated at the `POST /api/relay` | |
| 39 | boundary, which previously checked only truthiness. | |
| 40 | ||
| 41 | ### Fixed — fleet update path | |
| 42 | ||
| 43 | - **Runtime preflight before the service restart.** `npm ci` reporting success | |
| 44 | does not mean the relay can boot: utah-0.5gb's install exited 0 and the relay | |
| 45 | then crash-looped 188 times, because a memory-starved install left | |
| 46 | `node_modules` partially written and `require-addon` resolved the package root | |
| 47 | to `/`. The updater now executes the runtime after the dependency install and | |
| 48 | before the restart, and rolls back on failure instead of leaving a | |
| 49 | crash-looping relay. Deliberately a `require` rather than a version check — | |
| 50 | the fleet spans Node v18.19.1 to v22.23.1 and three relays share the exact | |
| 51 | version of the box that failed. | |
| 52 | - **The agent self-updates.** It runs from `/usr/local/bin` and nothing | |
| 53 | reinstalled it, so a bug in the update path could only be fixed by | |
| 54 | hand-visiting every box — which is why rc.6's `--force` rollback fix reached | |
| 55 | no relay. Installs atomically (temp + `bash -n` + rename) after the | |
| 56 | supply-chain gate and a proven-good dependency install. | |
| 57 | - **The watchdog no longer kills a relay for answering honestly.** It used | |
| 58 | `curl -f`, which discards the body on non-2xx, so a deliberate 503 | |
| 59 | `{"ok":false,"reason":"disk-critical"}` drain was indistinguishable from an | |
| 60 | event-loop hang — and since a restart frees no disk, the box looped SIGKILL | |
| 61 | every ~4 minutes. Any structured reply now proves the loop is turning. | |
| 62 | ||
| 63 | ### Added | |
| 64 | ||
| 65 | - `storageAdmission.failClosed()` is reported on `/health` as | |
| 66 | `reason: "storage-fail-closed"`. It previously set `_fatalReason` with no | |
| 67 | emit, log or metric while `canAcknowledge()` returned false — so every gateway | |
| 68 | drive read 404'd as "still replicating", actively misdirecting whoever was | |
| 69 | debugging it. | |
| 70 | - Capability docs publish `operator` when declared. `quorum-selector` keys on | |
| 71 | `r.operator || r.pubkey`, so an absent operator made every relay its own | |
| 72 | operator — five boxes in one datacenter satisfied a `minOperators` floor of | |
| 73 | five. Emitted only when set, so the default signable shape is unchanged. | |
| 74 | - `scripts/probe-fleet-services.mjs` reads `GET /api/v1/services` on every relay: | |
| 75 | what is *loaded*, not what is configured. First run found `sing-1` and | |
| 76 | `sing-2` on the same 1 GB tier running 3 and 9 services respectively, and | |
| 77 | `shard-store` on a 2 GB box. | |
| 78 | ||
| 79 | ### Docs | |
| 80 | ||
| 81 | Seven claims corrected, two of which *understated* what is built. Both headline | |
| 82 | packaging claims were inert: none of the `HIVERELAY_*` service or Tor variables | |
| 83 | have a reader on the Node runtime that Docker, systemd and Umbrel all launch — | |
| 84 | with all of them exported, `loadConfig()` still returns | |
| 85 | `enableServices: false, plugins: [], transports.tor: false`. | |
| 86 | ||
| 9 | 87 | ## [0.25.0-rc.6] — 2026-07-28 |
| 10 | 88 | |
| 11 | 89 | **Use this, not rc.5.** rc.5 cannot be installed by the fleet updater — see below. |
| … | … | @@ -14,7 +14,7 @@ and key-material fields at the schema boundary. |
| 14 | 14 | a one-page dashboard, first-run setup, wallet destination, service selection, |
| 15 | 15 | and persistent identity behind each platform's authenticated app proxy. |
| 16 | 16 | |
| 17 | **Open source (Apache 2.0)** | **[GitHub](https://github.com/bigdestiny2/P2P-Hiverelay)** | **[npm](https://www.npmjs.com/package/p2p-hiverelay)** | **Status: v0.25.0-rc.6** | |
| 17 | **Open source (Apache 2.0)** | **[GitHub](https://github.com/bigdestiny2/P2P-Hiverelay)** | **[npm](https://www.npmjs.com/package/p2p-hiverelay)** | **Status: v0.25.0-rc.7** | |
| 18 | 18 | |
| 19 | 19 | The four packages are versioned in lockstep: |
| 20 | 20 | |
| … | … | @@ -34,7 +34,7 @@ and StartOS metadata so the published release surfaces agree. |
| 34 | 34 | |
| 35 | 35 | | Surface | Current source state | Publication/evidence boundary | |
| 36 | 36 | |---|---|---| |
| 37 | | Core3 packages | Monorepo and package manifests are at `v0.25.0-rc.6`; the last release with a captured multi-arch GHCR digest is `v0.20.2` | A tag/release must run `npm run release:prepare` so versions, fleet channels, Umbrel, and StartOS metadata move together | | |
| 37 | | Core3 packages | Monorepo and package manifests are at `v0.25.0-rc.7`; the last release with a captured multi-arch GHCR digest is `v0.20.2` | A tag/release must run `npm run release:prepare` so versions, fleet channels, Umbrel, and StartOS metadata move together | | |
| 38 | 38 | | Blindspark on Umbrel | In-repo package uses app proxy, persistent `/data`, review-mode default, and guarded setup/add-wallet/service-manager smoke paths with visible in-flight state | Official App Store inclusion still needs the upstream `getumbrel/umbrel-apps` PR/review plus real-device `umbrel-runtime-review-evidence.json` | |
| 39 | 39 | | StartOS | Source can build and verify a digest-pinned `.s9pk` from a published GHCR release image and publish to a configured registry | Current `v0.20.2` package proof is blocked until `ghcr.io/bigdestiny2/p2p-hiverelay:0.20.2` resolves to a multi-arch digest; registry proof requires `startos-registry-evidence.json`; marketplace/community inclusion remains Start9 review-controlled | |
| 40 | 40 | | Raw fleet | Pull updater, channel metadata, health gate, rollback, and rollout verifier are in-repo | A release is live on the selected fleet only when `fleet-rollout-evidence.json` proves target SHA, package version, `/health.version`, and relay health | |
| … | … | @@ -1,12 +1,12 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiverelay-monorepo", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "p2p-hiverelay-monorepo", |
| 9 | "version": "0.25.0-rc.6", | |
| 9 | "version": "0.25.0-rc.7", | |
| 10 | 10 | "hasInstallScript": true, |
| 11 | 11 | "license": "Apache-2.0", |
| 12 | 12 | "workspaces": [ |
| … | … | @@ -8152,7 +8152,7 @@ |
| 8152 | 8152 | }, |
| 8153 | 8153 | "packages/client": { |
| 8154 | 8154 | "name": "p2p-hiverelay-client", |
| 8155 | "version": "0.25.0-rc.6", | |
| 8155 | "version": "0.25.0-rc.7", | |
| 8156 | 8156 | "license": "Apache-2.0", |
| 8157 | 8157 | "dependencies": { |
| 8158 | 8158 | "@noble/hashes": "^2.2.0", |
| … | … | @@ -8166,7 +8166,7 @@ |
| 8166 | 8166 | "corestore": "^7.11.0", |
| 8167 | 8167 | "hyperdrive": "^13.3.2", |
| 8168 | 8168 | "hyperswarm": "^4.17.0", |
| 8169 | "p2p-hiverelay": "^0.25.0-rc.6", | |
| 8169 | "p2p-hiverelay": "^0.25.0-rc.7", | |
| 8170 | 8170 | "sodium-universal": "^5.0.1" |
| 8171 | 8171 | }, |
| 8172 | 8172 | "engines": { |
| … | … | @@ -8206,7 +8206,7 @@ |
| 8206 | 8206 | }, |
| 8207 | 8207 | "packages/core": { |
| 8208 | 8208 | "name": "p2p-hiverelay", |
| 8209 | "version": "0.25.0-rc.6", | |
| 8209 | "version": "0.25.0-rc.7", | |
| 8210 | 8210 | "license": "Apache-2.0", |
| 8211 | 8211 | "dependencies": { |
| 8212 | 8212 | "@grpc/grpc-js": "^1.14.4", |
| … | … | @@ -8282,14 +8282,14 @@ |
| 8282 | 8282 | }, |
| 8283 | 8283 | "packages/services": { |
| 8284 | 8284 | "name": "p2p-hiveservices", |
| 8285 | "version": "0.25.0-rc.6", | |
| 8285 | "version": "0.25.0-rc.7", | |
| 8286 | 8286 | "license": "Apache-2.0", |
| 8287 | 8287 | "dependencies": { |
| 8288 | 8288 | "@noble/curves": "^2.2.0", |
| 8289 | 8289 | "@noble/hashes": "^2.2.0", |
| 8290 | 8290 | "@noble/secp256k1": "^3.1.0", |
| 8291 | 8291 | "b4a": "^1.8.1", |
| 8292 | "p2p-hiverelay": "^0.25.0-rc.6", | |
| 8292 | "p2p-hiverelay": "^0.25.0-rc.7", | |
| 8293 | 8293 | "sodium-universal": "^5.0.1" |
| 8294 | 8294 | }, |
| 8295 | 8295 | "engines": { |
| … | … | @@ -8329,7 +8329,7 @@ |
| 8329 | 8329 | }, |
| 8330 | 8330 | "packages/verifier": { |
| 8331 | 8331 | "name": "p2p-hiverelay-verifier", |
| 8332 | "version": "0.25.0-rc.6", | |
| 8332 | "version": "0.25.0-rc.7", | |
| 8333 | 8333 | "license": "Apache-2.0", |
| 8334 | 8334 | "dependencies": { |
| 8335 | 8335 | "b4a": "^1.8.1" |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiverelay-monorepo", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "description": "Monorepo root for HiveRelay (Core + Services + Client)", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "module", |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiverelay-client", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "description": "Unified client SDK for HiveRelay (Core + Services). Talks to either via capability flags advertised by relay nodes.", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "index.js", |
| … | … | @@ -61,7 +61,7 @@ |
| 61 | 61 | "corestore": "^7.11.0", |
| 62 | 62 | "hyperdrive": "^13.3.2", |
| 63 | 63 | "hyperswarm": "^4.17.0", |
| 64 | "p2p-hiverelay": "^0.25.0-rc.6", | |
| 64 | "p2p-hiverelay": "^0.25.0-rc.7", | |
| 65 | 65 | "sodium-universal": "^5.0.1" |
| 66 | 66 | }, |
| 67 | 67 | "engines": { |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiverelay", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "description": "HiveRelay Core — minimal always-on P2P availability infrastructure (seeding, circuit relay, proof-of-relay, gateway)", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "core/index.js", |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiveservices", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "description": "HiveRelay Services — application-layer services (AI inference, identity, schemas, SLAs, storage CRUD) that ride on top of HiveRelay Core", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "exports": { |
| … | … | @@ -20,7 +20,7 @@ |
| 20 | 20 | "@noble/hashes": "^2.2.0", |
| 21 | 21 | "@noble/secp256k1": "^3.1.0", |
| 22 | 22 | "b4a": "^1.8.1", |
| 23 | "p2p-hiverelay": "^0.25.0-rc.6", | |
| 23 | "p2p-hiverelay": "^0.25.0-rc.7", | |
| 24 | 24 | "sodium-universal": "^5.0.1" |
| 25 | 25 | }, |
| 26 | 26 | "engines": { |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "p2p-hiverelay-verifier", |
| 3 | "version": "0.25.0-rc.6", | |
| 3 | "version": "0.25.0-rc.7", | |
| 4 | 4 | "description": "Independent reference verifier for HiveRelay — reads raw hypercores from multiple relays and reports divergence. Ships separately from the main client SDK so cross-client verification is meaningful (see docs/THREAT-MODEL.md action item #3).", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "index.js", |
| … | … | @@ -167,7 +167,7 @@ Three paths, smallest to widest reach: |
| 167 | 167 | ## Status |
| 168 | 168 | |
| 169 | 169 | Package source targets multi-arch image tarballs (x86_64 + aarch64), |
| 170 | v0.25.0-rc.6, one-page dashboard via `HIVERELAY_UI_SIMPLE`, review-mode first boot, | |
| 170 | v0.25.0-rc.7, one-page dashboard via `HIVERELAY_UI_SIMPLE`, review-mode first boot, | |
| 171 | 171 | and a first-boot-only 10 GB storage cap. Current `v0.20.2` package verification |
| 172 | 172 | is externally gated on the published GHCR image/tag and digest; once that image |
| 173 | 173 | exists, `make verify IMAGE_DIGEST=sha256:<multi-arch-digest>` is the release |