mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-30 19:46:17 +00:00
Backlog items 4-5 close. Both-variants-in-one-run A/B at 50k, repeated with relay order reversed: every delta flipped with the order (the second-running relay won queries and ingest latency in BOTH runs), so readers=8 / mmap_size=256MiB / temp_store=MEMORY / periodic PRAGMA optimize are all noise-level on container-class hardware. The config plumbing stays (hardware-dependent, operators should measure their own box); the example config now says so explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtDNpayEYvJH7QuPswND3A
156 lines
6.4 KiB
TOML
156 lines
6.4 KiB
TOML
# Example config for geode. Section layout mirrors
|
|
# nostr-rs-relay's config.toml so existing operators can port across.
|
|
#
|
|
# Run with:
|
|
# ./gradlew :geode:run --args="--config /etc/geode.toml"
|
|
#
|
|
# CLI flags override individual values: e.g. `--port 8888` wins over
|
|
# `[network].port`.
|
|
|
|
[info]
|
|
# The wss:// URL clients use to reach this relay (mandatory for NIP-42
|
|
# AUTH challenges). If not set, the relay synthesises one from the
|
|
# [network] section.
|
|
relay_url = "wss://relay.example.com/"
|
|
name = "Example Geode"
|
|
description = "A geode deployment."
|
|
contact = "admin@example.com"
|
|
# Operator pubkey (NIP-11). Optional.
|
|
# pubkey = "..."
|
|
# Override the supported NIPs advertised on the NIP-11 endpoint. If
|
|
# omitted, the relay advertises the NIPs it actually implements.
|
|
# supported_nips = [1, 9, 11, 40, 42, 45, 50, 62]
|
|
|
|
[network]
|
|
host = "0.0.0.0"
|
|
port = 7447
|
|
path = "/"
|
|
# Ktor CIO event-loop pool sizing. Leave commented-out for sensible
|
|
# per-CPU defaults (typical for <2k concurrent connections). Lift on
|
|
# big-VM deployments targeting 10k+ connections — over-threading at
|
|
# low connection counts hurts L1/L2 cache locality, so always
|
|
# benchmark before/after when tuning these.
|
|
#
|
|
# Operators targeting >1k concurrent WebSockets should also raise the
|
|
# OS file-descriptor limit: `ulimit -n 65536` (or higher) before
|
|
# launching, plus a matching `LimitNOFILE=` in any systemd unit. The
|
|
# default of 1024 on most distros caps the relay well below 1k FDs
|
|
# (one per WS plus DB and listening sockets).
|
|
# connection_group_size = 4
|
|
# worker_group_size = 16
|
|
# call_group_size = 64
|
|
|
|
[database]
|
|
# True keeps an in-memory SQLite db (events vanish on restart). Useful
|
|
# for tests; set false + `file = "..."` for persistent storage.
|
|
in_memory = false
|
|
file = "/var/lib/geode/events.db"
|
|
|
|
# NOTE: the four knobs below measured as pure noise in relayBench A/B
|
|
# runs on 4-core container hardware at 50k events (see
|
|
# geode/plans/2026-07-04-sqlite-knobs-ab.md) — their value is
|
|
# hardware-dependent, so benchmark on YOUR box before enabling.
|
|
#
|
|
# Reader-connection pool size (file-backed stores only). Default 4.
|
|
# readers = 4
|
|
|
|
# PRAGMA mmap_size in bytes — maps the db file into memory so reads
|
|
# skip the pread syscall. Off by default (SQLite default).
|
|
# mmap_size = 268435456
|
|
|
|
# PRAGMA temp_store = MEMORY — RAM instead of temp files for large
|
|
# sorts. Off by default.
|
|
# temp_store_memory = true
|
|
|
|
# Refresh query-planner statistics (PRAGMA optimize) every N seconds.
|
|
# Incremental and usually a no-op; keeps the planner from drifting
|
|
# onto the wrong index as the corpus grows. Off by default.
|
|
# optimize_interval_seconds = 3600
|
|
|
|
[options]
|
|
# Drop events whose Schnorr signature does not verify. Strongly
|
|
# recommended for any relay accepting traffic from real clients.
|
|
# Verify Schnorr signatures on every EVENT. Default: true. Disable
|
|
# only for trusted-input scenarios (test fixtures, mirror replays).
|
|
verify_signatures = true
|
|
|
|
# Run signature verification in parallel inside the IngestQueue
|
|
# (across all CPU cores) instead of serially on each connection's
|
|
# WebSocket pump. Default: true. Set false to fall back to the
|
|
# legacy in-policy verify path.
|
|
# parallel_verify = true
|
|
|
|
# NIP-50 full-text search. Default: true. When false no FTS index is
|
|
# built or maintained (inserts skip tokenization — a measurable share
|
|
# of ingest cost), NIP-11 stops advertising NIP-50, and REQ filters
|
|
# with a `search` term match nothing. CLI: --no-search.
|
|
# full_text_search = true
|
|
|
|
# Require clients to NIP-42 AUTH before REQ/EVENT/COUNT.
|
|
require_auth = false
|
|
|
|
# Advertise NIP-42 AUTH without requiring it: the relay sends the
|
|
# challenge and records clients that authenticate (so downstream
|
|
# policies can gate on identity), but REQ/EVENT/COUNT still work for
|
|
# clients that never AUTH. Ignored when require_auth = true.
|
|
# optional_auth = false
|
|
|
|
# Reject events whose `created_at` is more than this many seconds in
|
|
# the future. Enforced by RejectFutureEventsPolicy.
|
|
# reject_future_seconds = 1800
|
|
|
|
[authorization]
|
|
# Allow / deny lists. Allow is a permissive ceiling; deny still
|
|
# removes specific entries inside it. Enforced by Pubkey/KindAllowDenyPolicy.
|
|
# pubkey_whitelist = ["abcdef...64hex..."]
|
|
# pubkey_blacklist = []
|
|
# kind_whitelist = [0, 1, 3, 7, 1059, 30023]
|
|
# kind_blacklist = [4]
|
|
|
|
# Mirror upstream relays (strfry-router style, "down" direction): the
|
|
# relay dials each [[mirror]] url, subscribes to everything newer than
|
|
# now - backfill_seconds, and ingests the stream alongside client
|
|
# publishes. Reconnects and re-subscribes automatically.
|
|
#
|
|
# `trusted = true` is the relay-to-relay trust switch: events from that
|
|
# upstream skip Schnorr signature verification (the upstream already
|
|
# verified its own ingest; re-verifying burns ~8% of ingest CPU). The
|
|
# trusted identity is the URL *this* relay dialed — TLS-authenticated
|
|
# for wss:// — so an inbound client can never claim it. Default false:
|
|
# mirror-but-verify. Only trust relays you operate or whose ingest
|
|
# discipline you'd stake your own db on.
|
|
#
|
|
# `filter` (optional) scopes an upstream, as a NIP-01 filter JSON
|
|
# object — same idea as strfry-router's per-stream filter. It shapes
|
|
# the REQ sent upstream AND every delivered event is re-checked
|
|
# against it before ingest, so even a trusted upstream can only
|
|
# inject events inside the declared scope. `since`/`limit` inside it
|
|
# are ignored (backfill_seconds owns the time window). Omit to mirror
|
|
# everything; for several disjoint scopes, repeat [[mirror]] with the
|
|
# same url.
|
|
#
|
|
# [[mirror]]
|
|
# url = "wss://upstream.example.com/"
|
|
# trusted = true
|
|
# backfill_seconds = 3600
|
|
# filter = '{"kinds":[0,1,3,7],"#t":["nostr"]}'
|
|
|
|
[admin]
|
|
# NIP-86 relay management API. When `pubkeys` is non-empty, the relay
|
|
# accepts HTTP POST application/nostr+json+rpc on the same URL,
|
|
# authenticated with NIP-98 HTTP-Auth. Only events signed by one of
|
|
# the listed pubkeys can run admin RPCs (banpubkey / banevent /
|
|
# changerelayname / …). Empty (the default) disables the endpoint.
|
|
#
|
|
# NIP-98 binds admin tokens to the relay's HTTP URL, which is derived
|
|
# from [info].relay_url with the scheme swapped (ws -> http, wss ->
|
|
# https) per NIP-86. Make sure [info].relay_url is set to the
|
|
# canonical public URL when behind TLS termination or a reverse proxy.
|
|
# pubkeys = ["abcdef...64hex..."]
|
|
|
|
# Path for the JSON snapshot that persists NIP-86 admin state (ban
|
|
# lists + the live NIP-11 doc) across restarts. When unset, admin
|
|
# state is in-memory only and forgotten on every restart. Convention
|
|
# is to place this next to the SQLite event-store file.
|
|
# state_file = "/var/lib/geode/events.db.admin.json"
|