Files
amethyst/geode
Claude 1794ed5249 fix: make the sync benchmark opt-in; stop advertising the QUIC preview path
Two unrelated things that both amount to not paying for something nobody
asked for.

**`MirrorSyncThroughputTest` is a benchmark, so it now opts in.** It
preloaded a million events and pulled them over a real WebSocket on every
ordinary test run: 4,584 s of `:geode:test`'s 4,636 s — 98.9% of the
module's test time for one test that asserts nothing about correctness and
reported `skipped` at the end anyway. Every other benchmark in the module
is already gated this way (`perf.LoadBenchmark`). It now bails before
building anything, and enables on `-DrunLoadBenchmark=true` OR on any of
its own sizing properties, so every invocation its kdoc documents still
runs it — naming a size is itself the opt-in. Measured after: the test
takes 5 ms, the module takes 64.8 s, and `-DsyncN=2000` still prints a
throughput number.

**The agent text stream QUIC path is kept but no longer advertised, and
nothing starts it.** Nothing in the deployed network publishes those
previews. So:

- `SUPPORTED_COMPONENTS` drops `0x8006` and the leaf capabilities drop
  `0xF2D1`/`0xF2D2`/`0xF2D4`. A capability is a standing promise to every
  peer that reads our KeyPackage, and one for a path nobody exercises
  costs something and buys nothing. The captured reference KeyPackage in
  our own conformance vector does not advertise `0x8006` either.
- The Android chat screen no longer builds a stream watcher and dials the
  brokers a kind:1200 advertises. That was a UDP connection attempt to a
  third-party endpoint on every feed change, on behalf of a feature with
  nothing to show — a service we start, not a capability we hold.

The implementation stays and stays tested: `:marmotQuic`, the codecs,
`amy marmot stream`, the direct path, the certificate pinning and the
interop tests are all untouched. The module README records the posture and
the exact way back.

Three tests asserted the old advertisement and were reworked rather than
deleted. The role-enforcement gate is still covered — the tests now build
leaves that explicitly carry the roles, which is the better shape anyway,
since a test that exercised the gate through OUR default was really
asserting the default and stopped testing the gate the moment it changed.
A new test pins the new default: our KeyPackage carries no role and is
therefore refused by a group requiring one. That refusal is the deliberate
cost, so it is asserted rather than discovered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kCuA6tc4JQzHPCDd39GHq
2026-09-09 18:29:36 +00:00
..

geode

A standalone Nostr relay for the JVM, built on Quartz's relay-server code (Ktor CIO). It speaks the core relay protocol plus NIP-11 (info doc), NIP-42 (AUTH), NIP-45 (COUNT), NIP-50 (full-text search), NIP-77 (Negentropy sync), and NIP-86 (relay management), stores events in SQLite (or a filesystem backend), and can mirror upstream relays strfry-router style.

geode depends only on :quartz — no Android, no Compose. amy serve (the Amethyst CLI) embeds the same engine in-process.

Install

Pick the channel that matches how you deploy. For a real relay, the Docker image or the .deb/.rpm + systemd are the two production paths; Homebrew and the portable tarball are handy for local testing.

Images are published to GHCR on every release:

# Ephemeral — in-memory store, events vanish on restart:
docker run --rm -p 7447:7447 ghcr.io/vitorpamplona/geode:latest

# Persistent + configured:
docker run -d --name geode -p 7447:7447 \
  -v $PWD/geode.toml:/etc/geode/geode.toml:ro \
  -v geode-data:/var/lib/geode \
  ghcr.io/vitorpamplona/geode:latest --config /etc/geode/geode.toml

with in_memory = false and file = "/var/lib/geode/events.db" in your geode.toml. Pin a version (:1.14.0) instead of :latest for reproducible deploys. To build the image yourself, from the repo root:

docker build -f geode/Dockerfile -t geode:local .

Debian/Ubuntu (.deb) and Fedora/RHEL (.rpm)

Download geode-<version>-linux-x64.deb (or .rpm) from the release page and install it — a minimal JRE is bundled, so no system Java is required. It installs to /opt/geode/ with the launcher at /opt/geode/bin/geode.

sudo dpkg -i geode-1.14.0-linux-x64.deb    # or: sudo rpm -i geode-1.14.0-linux-x64.rpm

To run it as a managed service, wire up the shipped systemd unit (/opt/geode/share/geode/geode.service) — the unit file's header comment has the copy-paste steps (create the geode user, drop your config in /etc/geode/geode.toml, systemctl enable --now geode).

Homebrew

brew install vitorpamplona/tap/geode-relay    # from the tap, once published

The formula depends on openjdk and installs the no-JRE jar bundle. Reference formula: packaging/homebrew/geode-relay.rb.

Portable tarball (any Linux/macOS, no system Java)

Download geode-<version>-<os>-<arch>.tar.gz, unpack, and run:

tar xzf geode-1.14.0-linux-x64.tar.gz
./geode/bin/geode --config geode/share/geode/config.example.toml

The tarball bundles a jlink'd JRE plus share/geode/config.example.toml and share/geode/geode.service.

From source

./gradlew :geode:run --args="--config /etc/geode.toml"

Configure

geode runs with zero config (binds 0.0.0.0:7447, in-memory SQLite). For anything real, copy config.example.toml and edit it — the sections mirror nostr-rs-relay's config.toml so existing configs port almost verbatim. Precedence is CLI flags > TOML > built-in defaults.

geode --config /etc/geode/geode.toml
geode --help          # full flag list
geode --version

Key sections: [info] (NIP-11 doc), [network] (bind + thread pools), [database] (SQLite path/tuning), [options] (AUTH / verify / search), [authorization] (allow/deny lists), [[mirror]] (upstream mirroring), and [admin] (NIP-86 management). See the example file for every knob.

Verbs

geode [relay] [flags]        serve the relay (default)
geode import [flags] [FILE…] bulk-load NDJSON events into the store
geode export [flags]         dump the store as NDJSON to stdout

import/export are geode's strfry import / strfry export — one JSON event per line, for seeding, migrating, or backing up a relay. Both stream, so a multi-million-event corpus round-trips in roughly constant memory.

Release process

geode ships on the same tag-driven pipeline as the rest of Amethyst: pushing a v* tag runs .github/workflows/create-release.yml, whose build-geode job produces the tarball + .deb/.rpm + Homebrew jvm bundle, docker-geode builds and pushes the GHCR image, and bump-homebrew-geode-formula.yml syncs the reference formula. Design notes: plans/2026-07-24-geode-release.md.