A Nostr relay that is fully interoperable with the public Nostr network — any
standard client, any standard relay, plain wss — and, additionally, reachable
over I2P and Tor for other 1M5-aware relays and clients, using 1m5-core-java
for identity, routing, and transport rather than reimplementing any of it.
Phases 0-4 done: the plain NIP-01 listener (wss), 1m5-core-java embedded
(identity, I2P/Tor/Bitcoin all operator-toggleable), the 1M5 peer-gossip
channel (hand-entered peers; discovery is its own still-open design
question), and the NIP-86/NIP-98 admin API (ban/allow pubkeys, moderation,
relaystats) — 1m5-nostr-node-admin
is a browser SPA over that same API. Phase 5 (a wss fallback to known
1M5-aware peers when I2P/Tor are both down) is blocked on that same
peer-discovery question. See DESIGN.md for the architecture
and TODO.md for the full build order and what's actually
verified vs. still open.
mvn -q -DskipTests package && java -jar target/1m5-nostr-node-*.jar, or
build/run the Docker image: ./docker-build.sh (needs did-java and
1m5-core-java already mvn install-ed into your local ~/.m2 — neither
is published anywhere else yet, see that script's own comments), then
docker run --rm -p 8080:8080 -v "$(pwd)/data:/app/data" 1m5-nostr-node:latest.
All 1m5.*/nostr.relay.* config (application.yml) is overridable via
-e/--env-file (Spring Boot's relaxed env-var binding, e.g.
NOSTR_RELAY_URL for nostr.relay.url).
Production deployment (resolvingarchitecture.io/apps/nostr/node) is orchestrated from
ra/infra (ra/infra/deploy_all_script.sh), not from this repo alone — docker-compose.service.yml,
docker/1m5-nostr-node.Dockerfile, nginx-location.conf, and build_and_sync.sh here are this
repo's own half of that unified stack (ra/infra/DESIGN.md explains the split). See this repo's
own .env.prod.example for the production config this needs (NOSTR_RELAY_URL, 1M5_PASS,
1M5_I2P_ENABLED/1M5_TOR_ENABLED).
Nostr relay implementations already exist, in several languages, some fast,
some simple. A new one only earns its place if it does something none of the
existing ones do. Every relay implementation surveyed assumes plain
TCP/WebSocket and nothing else — none treat reachability over a censorship-
resistant transport as a first-class option. 1m5-core-java already has that:
a working I2P transport (i2p-java), a working Tor transport
(tor-client-java), a peer directory, and an escalation router that picks a
path based on how hostile the network currently is (ManCon). This project's
whole reason to exist is to put a real, spec-conformant Nostr relay behind
that machinery.
This is not a 1M5-only relay. A relay that only worked for 1M5 peers would
be a dialect, not a Nostr relay, and would defeat the point of building on an
interoperable protocol at all. 1m5-nostr-node listens on plain wss like
any other relay and speaks NIP-01 faithfully — that path has no 1M5 dependency
whatsoever. What 1M5 adds is a second, independent way for events to reach
this node: as ra.common.Envelope payloads, delivered peer-to-peer over I2P
or Tor to another 1M5-aware relay or client, for exactly the situations a
plain wss connection can't survive.
This isn't a one-sided integration — Nostr and 1M5 each get something real out of it.
To the 1M5 community:
- An existing, real user base and client ecosystem, for free. 1M5 doesn't have to bootstrap its own messaging protocol, apps, and users from zero — Nostr already has that (Damus, Amethyst, Primal, thousands of real users). This relay gives 1M5 a foothold in a network that already exists.
- A protocol that's already designed and debugged. Event model,
subscriptions, relay semantics — the hard-to-get-right surface area of "a
messaging protocol" — is inherited rather than invented.
did-java's identity work only had to add DID-specific event kinds on top. - A legible pitch to a security-conscious audience. "It's Nostr, but reachable when your country blocks Tor and I2P too" is a one-sentence story for exactly the people who already understand and value censorship resistance, because they're already using Nostr for it.
- A working answer to Nostr's own unsolved identity problem. Key
rotation has been a graveyard of stalled NIPs for years.
did-java's guardian-based rotation (kinds 30100-30103) is a real fix to a problem the Nostr community has publicly failed to solve on its own.
To the Nostr community:
- Real reachability under the exact threat model Nostr claims to resist. A relay's uptime for a censored user today depends entirely on clearnet DNS/IP staying unblocked — the easiest thing in the world for a state actor to block. A 1M5-aware relay keeps working over I2P/Tor when that fails, for the same clients, same protocol, no changes required on their end.
- Zero cost to adopt, because it's opt-in by construction. The plain listener has no 1M5 dependency — any existing client or relay interoperates with this one exactly as if it were ordinary Nostr. Nobody has to understand or buy into 1M5 to get value from a 1M5-aware relay being on the network.
- Relay implementation diversity. Nostr's resilience partly depends on relays not being a monoculture — one bug, one supply-chain compromise, one legal pressure point shouldn't take down every relay. A from-scratch JVM implementation is a genuinely independent codebase, valuable to the ecosystem on its own, separate from the 1M5 angle.
- A public good, not a walled garden. More relays that are individually harder to censor makes the whole network more robust for every Nostr user, not just the ones deliberately choosing 1M5-aware relays — redundancy compounds across a decentralized network.
Any Nostr client / relay --wss--> plain NIP-01 listener --\
>-- one event
Another 1M5-aware peer --I2P/Tor-> 1M5 peer-gossip channel-/ store
Both paths feed the same storage and the same NIP-01 subscription/broadcast
logic. Neither path knows the other exists. See DESIGN.md §"Two independent
delivery paths" for why this is two listeners and not one pluggable transport.
did-java— owns the Nostr event model: canonical serialization, id computation, BIP-340 signature verification, and the node's own identity key (NostrKeyRing). This repo depends on it for event verification and for signing anything the relay itself originates; it does not duplicate any of that.1m5-core-java— owns node identity, the peer directory, the escalation router, the I2P/Tor transports, and (optionally, gated by1m5.bitcoin.enabled) an on-chain Bitcoin wallet viaBitcoinService, used here only to surface an operator-support receive address in the NIP-11 document (DESIGN.md§5.4) — not a Lightning/zap capability. This repo embeds it as a library, the same way1m5-remnantand1m5-desktop-javado, through the narrowCoreClient/Msgboundary — neverra.common.Envelopeor the bus API directly.network.onemfive.core.business.NostrService(inside1m5-core-java) — the existing Nostr client capability: a 1M5 node publishing to and querying from the public relay set. That is a different role from running a relay, but the same node can do both — seeDESIGN.md§"Relationship toNostrService".
There is no separate "generic relay" repo alongside this one. An earlier plan
split that generic implementation into its own project (nostr-node-java)
with 1M5 wired in downstream, specifically to keep the generic relay
adoptable by non-1M5 users. That split added a repo and an SPI boundary with
no one on the other side of it, so it was dropped — this repo is the relay,
full stop, and stays fully interoperable on its own merits regardless of
whether anyone runs it with 1M5 enabled.
MIT.