A self-contained package that turns an ordinary machine (an old laptop is enough) into a censorship-resistant Blurt RPC node, reachable over Tor (.onion v3) and I2P (.b32.i2p) — with no public IP, no port-forward, no DNS, no registrar, and no hosting company.
  • Shell 92.3%
  • Python 7.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-16 11:44:21 -07:00
config hidden-rpc v1.0.1 2026-09-12 11:44:34 -07:00
desktop hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
docs hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
extras/hardening hidden-rpc v0.2.0 2026-08-28 18:18:47 -07:00
installer hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
supervisor hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
.gitignore hidden-rpc v0.1.0 — one-command private Blurt RPC node (Tor + I2P) 2026-08-13 17:52:24 -07:00
LICENSE hidden-rpc v0.1.0 — one-command private Blurt RPC node (Tor + I2P) 2026-08-13 17:52:24 -07:00
README.md hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
RELEASE-NOTES.md hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00
release.sh hidden-rpc v0.1.5 2026-08-13 22:24:02 -07:00
VERSION hidden-rpc v1.1.0 2026-09-16 11:44:21 -07:00

hidden-rpc

A self-contained package that turns an ordinary machine (an old laptop is enough) into a censorship-resistant Blurt RPC node, reachable over Tor (.onion v3) and I2P (.b32.i2p) — with no public IP, no port-forward, no DNS, no registrar, and no hosting company.

It bundles a full blurtd + a hidden-service transport layer + grandma-friendly operator tooling, so any Blurt RPC admin can stand up a node on hardware they already own.

  • Home: git.agorise.net/agorise/hidden-rpc (Forgejo)
  • License: AGPL-3.0-or-later
  • Status: Phase 0 (scaffold). See docs/ROADMAP.md.

What this is — and what it is NOT

It is

A packaging/transport/tooling layer that wraps existing upstreams. It does not fork or re-implement blurtd (or, later, Nexus) — it consumes them as upstream dependencies and adds the value on top: self-authenticating hidden-service reachability, JSON-RPC batch support, and one-command operator setup that stays calm on flaky home hardware.

Wrap, don't fork. If we ever find ourselves patching blurtd internals, we've taken a wrong turn. Fixes to the chain node go upstream; this repo owns the layer around it.

It is NOT

  • Not a witness / block-production tool. p2p block gossip over Tor is latency-sensitive and chain-wide — out of scope. (A witness may also expose their RPC as .onion with this package; that's fine. Block production over Tor is not.)
  • Not a naming/addressing system. No vanity .i2p addressbook, no .loki/LNS. RPC endpoints are machine-to-machine — nobody types them — so memorability buys nothing while re-introducing a naming-trust dependency. We support only raw .onion v3 + .b32.i2p, both of which are self-authenticating (the address is the key hash) and therefore strictly stronger than clearnet + TLS + DNS.
  • Not a trust layer. This package makes a trustworthy-transport node. Whether a given node's answers are true is a separate problem solved by the reader (see below).

The trust caveat (load-bearing — read this)

Tor/I2P hide where you read, not whether what you read is true.

Reaching a node over a hidden service tells you nothing about the honesty of its data. Cross- checking and quorum — racing the same call at several independent nodes and requiring them to agree before trusting a head block or balance — is the reader's responsibility. In the Agorise stack that reader is Morphit, which implements race + quorum as a parallel track in its own repo.

Nothing in this package's docs or output should ever imply that reaching a node means trusting its data. If you catch such an implication, it's a bug.

Local blurtd is still the gold standard. The strongest reader runs its own blurtd and reads localhost. This package's hidden-RPC tier is the fallback for readers too small to hold the chain, or for onion-only / locked-down environments.


Relationship to Morphit

hidden-rpc is a separate project from Morphit, with a different audience (all Blurt RPC admins, not only Morphit operators) and an independent release cadence.

  • Morphit consumes these nodes. It reads the chain through them and publishes the on-chain directory (morphit_rpc_v1, signed by @morphit) that lists them.
  • Morphit does not own this software. This repo does not know about the marketplace; it just exposes a chain node over a hidden service and emits its address in a form Morphit's registration tooling can pick up.
  • The morphit_rpc_v1 op is defined and broadcast in the Morphit repo, not here.

Why "outbound-only" is the whole point

Hidden services dial out. There is no inbound connection to accept, so there is no public IP to obtain, no port to forward, no registrar to satisfy, and no CGNAT/ISP-NAT problem. A laptop behind a typical home router — or behind carrier-grade NAT — can serve RPC to the world without any cooperation from the ISP.


Flaky by design — but never stuck

Laptops sleep; home internet flaps. Per-node uptime is not the goal — the union of nodes being reachable is. We do not engineer per-node high-availability. We engineer for trivial add/remove of nodes and clean "just doesn't answer" failure. (Morphit's race + failover handles the consumer side.)

There is an important difference between flaky and stuck, though, and v0.3.0 is about the second. A node that drops out for ten minutes and comes back is fine. A node that goes dark and stays dark until somebody walks over to it is a node that has left the fleet. So each node supervises itself end to end — chain daemon, container runtime, Tor, I2P, clock, disk, power — repairs whatever broke, verifies that the repair actually worked, and if it did not, keeps trying on a backoff for as long as the machine has power. See docs/SELF-HEALING.md.


Repository layout

hidden-rpc/
├── README.md            ← you are here
├── LICENSE              ← AGPL-3.0-or-later
├── VERSION              ← current release version (used by release.sh)
├── RELEASE-NOTES.md     ← notes for the current release
├── release.sh           ← maintainer: one-command publish to Forgejo
├── config/
│   ├── config.ini              ← ready-to-use: upstream config, webserver bound to loopback
│   └── rpc-config.upstream.ini ← Blurt-published config, verbatim (provenance)
├── installer/
│   ├── setup.sh     ← one-command auto-installer (Phase 2; first run = Phase 1 proof)
│   └── README.md               ← installer usage
├── desktop/             ← tray icon + menu entries (no terminal needed)
├── supervisor/          ← the self-healer that keeps a node reachable
│   ├── transport-supervisor.sh ← the loop: node + Tor + I2P + host, with a repair ladder
│   ├── heal.sh                 ← the repairs themselves; every one verified afterwards
│   ├── transport-probe.sh      ← end-to-end probes (fork-free on the idle path)
│   ├── hidden-rpc-status       ← everything about this node, in one screen
│   ├── hidden-rpc-nodectl      ← stop/start it safely (maintenance mode)
│   └── tests/                  ← fsm-sim, heal-sim, installer-lint, tray-classify
└── docs/
    ├── ROADMAP.md       ← build order, phases 06
    ├── DECISIONS.md     ← settled + open decisions log
    ├── SELF-HEALING.md  ← what breaks a node, what fixes it, and how to test that
    ├── LOGGING.md        ← what is recorded, where, and for how long (14 days)
    ├── AUDIT.md          ← the v1.0.0 code, security and resource audit
    ├── FOOTPRINT.md     ← every resource lever, measured, with the trade-offs
    ├── GAPS.md          ← gap analysis of the upstream setup instructions
    └── MANUAL-SETUP.md  ← Phase 1: the one-laptop proof, reproducible by hand

Current status & next step

Released and running (v0.3.0). The one-command installer (installer/setup.sh) is the canonical setup — by default it trustlessly bootstraps the chain from a signed on-chain pointer (chain_snapshot_v1 by @morphit): it fetches the block_log (IPFS, mirror fallback), verifies its SHA-256, places Blurt's genesis file, and replays every block. It binds the RPC to loopback, stands up the Tor and I2P hidden services, and prints the node's two addresses. It's idempotent, self-cleaning, and survives reboots — a reboot is now a two-minute restart, not an overnight replay. A --trusted-state opt-in sets up fast from a presynced state snapshot (you trust its publisher).

Once set up, the node looks after itself. Check on one with hidden-rpc-status, repair one on demand with --doctor, and stop one safely with hidden-rpc-nodectl stop.

./installer/setup.sh

For how it works under the hood (and the manual equivalent), see docs/MANUAL-SETUP.md. The reasoning and the resolved unknowns are in docs/DECISIONS.md; the roadmap and what's left (Nexus, Morphit handoff) are in docs/ROADMAP.md.