You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Silberengel f76e047472 thinned out unused and inefficient/broken stuff 3 weeks ago
config thinned out unused and inefficient/broken stuff 3 weeks ago
docker update setup script and Dockerfile 3 weeks ago
lib thinned out unused and inefficient/broken stuff 3 weeks ago
priv thinned out unused and inefficient/broken stuff 3 weeks ago
rel/overlays/bin Configure for local dev with Docker Compose 2 months ago
test thinned out unused and inefficient/broken stuff 3 weeks ago
.dockerignore Organize for release builds and dev deployment 2 months ago
.env.example thinned out unused and inefficient/broken stuff 3 weeks ago
.formatter.exs thinned out unused and inefficient/broken stuff 3 weeks ago
.gitignore PR corrections 3 weeks ago
AGENTS.md more bug-fixes 3 weeks ago
CLAUDE.md Symlink CLAUDE.md to AGENTS.md 2 months ago
LICENSE.md Initial commit 3 months ago
README.md thinned out unused and inefficient/broken stuff 3 weeks ago
compose.yaml thinned out unused and inefficient/broken stuff 3 weeks ago
mix.exs thinned out unused and inefficient/broken stuff 3 weeks ago
mix.lock thinned out unused and inefficient/broken stuff 3 weeks ago
prd-1-index-relay.md Produce Swagger schema for REST API 2 months ago
setup.sh more PR corrections 3 weeks ago

README.md

Mercury Index-Relay logo

GitCitadel Mercury Index-Relay

A Nostr index relay built with Phoenix 1.8 / Elixir. Stores and serves Nostr events via a REST API, with filter-based querying per NIP-01.

Supported NIPs: NIP-11 (relay info), NIP-70 (protected events)

Getting Started

Prerequisites

  • Docker (for the database)
  • Elixir ~> 1.15

Automated setup

Run the setup script — on Debian/Ubuntu it installs Erlang/Elixir from Team RabbitMQ’s apt repositories (Elixir install guide); on Fedora/RHEL it uses asdf. It starts the Apache AGE database container and runs mix setup:

chmod +x setup.sh
./setup.sh

After setup, database credentials are written to .env (including REQUIRE_DB=true for integration tests). Source it before running mix tasks that need the DB or asdf’s mix in a new terminal:

source "$HOME/.asdf/asdf.sh"   # if `mix` is not found
source .env

Manual setup

Start the Apache AGE Docker container:

docker run \
  -d \
  --name gc_age_db \
  -p 5455:5432 \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=gc_index_relay_dev \
  apache/age:release_PG17_1.6.0

Set database environment variables, then install dependencies and run migrations:

export POSTGRES_HOST=localhost
export POSTGRES_PORT=5455
export POSTGRES_USER=postgres
export POSTGRES_PASSWORD=postgres
export POSTGRES_DB=gc_index_relay_dev
export REQUIRE_DB=true

mix setup

Docker Compose (dev)

Postgres is published on localhost:5455, same as the manual docker run and setup.sh flow. The stack works without a .env file (compose supplies defaults for DB names, the app DB role, and a dev-only SECRET_KEY_BASE). Override with .env if you want different passwords.

docker compose -f compose.yaml up -d

Use POSTGRES_PORT=5455 and POSTGRES_HOST=localhost in .env when running mix on the host against this database (compose services talk to Postgres via the hostname postgres on the Docker network, not localhost).

To reset the compose database volume: docker compose -f compose.yaml down -v.

Starting the server

source "$HOME/.asdf/asdf.sh"   # if needed
source .env
mix phx.server

The relay listens on port 4000 by default (see PORT in config/runtime.exs). Open http://localhost:4000. After edits to config/config.exs, config/dev.exs, or config/runtime.exs, restart the server manually; other code is hot-reloaded.

NIP-11 relay info

curl -s -H "Accept: application/nostr+json" http://localhost:4000 | jq

Edit the relay metadata in config/config.exs under the :relay_info key.

Testing

Unit tests (no database required):

mix test.unit

Integration tests (requires the database to be running). The mix test.integration task runs a subprocess with REQUIRE_DB=true so the Repo starts under test—you do not need to export it yourself. Still source .env (or set POSTGRES_*) so the test database host and port match your container:

source .env
mix test.integration

Run the full integration probe against the relay API (covers all endpoints, CORS, NIP-11, NIP-70):

source .env
mix test.integration test/gc_index_relay_web/relay_integration_test.exs

The test scenarios are documented in test/features/relay_api.feature and cover:

  • Discovery (GET /api)
  • Publishing and rejecting events (POST /api/events)
  • Deleting events (DELETE /api/events/:id)
  • Cacheable queries (GET /api/events?since=&until=&limit=)
  • Filter-based queries (POST /api/events/filter)
  • Single-event lookup (GET /api/events/:id)
  • CORS preflight and headers
  • NIP-11 relay info document including the icon field

Pre-commit check (compile with warnings-as-errors, format, credo, unit tests):

source .env
mix precommit

Project Overview

Database

  • The database stores Nostr events.
  • Nostr events, once signed, are considered immutable.
  • Uses Apache AGE (PostgreSQL with graph extensions).

Migrations

After modifying an Ecto schema, generate a migration with:

mix ecto.gen.migration <migration-name>

Edit the generated migration file as needed, then apply it:

mix ecto.migrate

Refer to the Fly.io guide Safe Ecto Migrations for additional information.

Learn more