Nostrfi
/Docs
Browse documentation
Owner: NostrfiLast reviewed: 2026-08-08

Development operation

Current runtime

The source expects Go 1.25 with toolchain 1.25.12. Running the relay starts one HTTP server, listening at the configured server.listen_addr (default :8080), and opens the DuckDB file at the configured storage.db_path (default db/relay.db).

go run ./cmd/relay

A Dockerfile and docker-compose.yml exist, and CI publishes versioned images to ghcr.io/nostrfi/relay on every build (GitVersion-driven semver, sha-<shortsha>, and latest). A documented, exercised TLS-terminating reverse-proxy overlay (docker-compose.tls.yml, using Caddy) and a rollback recipe against those published tags also exist — see the source repository's README.md "Deployment" section. TLS is not implemented in the application itself; that boundary is deliberate, not a gap — see the trust and data page.

Configuration

config.yaml contains the NIP-11 relay information object (name, description, operator public key, contact, advertised software URL, supported NIPs, and advertised relay limitations), plus operational settings: resource limits, NIP-42 auth binding, WebSocket origin policy, retention, server listener and shutdown timeout, and the database path. See the source repository's README.md "Configuration" section for the full reference and current defaults — every setting is configurable, with defaults matching what used to be hardcoded.

If the file is absent, code defaults supply basic identity and NIP values. LOG_LEVEL accepts debug, info, warn, or error; the default is info.

The limitation fields in config.yaml are included in the NIP-11 response but are not applied to connections or messages unless the corresponding enforcement exists. Enforcement now covers message size, subscription and filter counts, content length, tag count, timestamp bounds, proof-of-work, and auth requirements — operators should still confirm against the source README.md rather than assume a field is active.

Storage lifecycle

Schema changes are applied by a versioned migration runner, recorded in a schema_migrations table; a migration failure aborts startup rather than serving traffic against a partially-migrated schema. A background worker deletes events whose NIP-40 expiration has passed (and their tag rows) on a configurable interval, then runs DuckDB's checkpoint and vacuum. -backup and -restore flags on the relay binary export and import the database using DuckDB's native tooling, with a row-count manifest verified at restore time.

Deletion and replacement on the existing delete/replace code paths still do not remove associated tag rows, which can create orphaned index data over time — this remains open and is tracked separately from the storage-lifecycle work above.

Logging and observability

Logs are structured JSON on standard output. /healthz reflects process health only; /readyz verifies the database is reachable. /metrics exposes Prometheus-format metrics: connections, messages by type, rejections by reason, subscriptions, query latency, events stored, and save failures, with cardinality-bounded labels. Negentropy reconciliation logging is at Debug and no longer includes the raw payload value. There is still no distributed tracing.

Production-readiness checklist

  • Define public, private, or product-specific relay policy and ownership.
  • Make listener, public relay URL, data path, and shutdown settings explicit.
  • Put authenticated TLS termination in front of the service (via a documented, exercised reverse-proxy overlay — the relay itself still never terminates TLS directly).
  • Enforce message, connection, subscription, filter, event, and time limits.
  • Add connection and publication rate limits plus resource backpressure.
  • Restrict and test the WebSocket origin policy.
  • Add liveness, readiness, metrics, dashboards, and alerting (an initial, explicitly provisional dashboard/alert set — a formal service-level objective has not been approved yet). Distributed tracing remains open.
  • Establish schema migrations, backup, restore, retention, and purge jobs.
  • Define moderation, abuse response, legal requests, and operator access.
  • Load-test expected event volume, database growth, queries, and fan-out.
  • Threat-model untrusted JSON, large events, connection floods, and metadata.
  • Add release packaging, dependency scanning, and a deployment runbook (CI runs govulncheck on every build; a finding fails the build).