4.0 KiB
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 — it installs Erlang/Elixir via asdf, starts the Apache AGE database container, and runs mix setup:
chmod +x setup.sh
./setup.sh
After setup, database credentials are written to .env. Source it before running any mix commands:
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
mix setup
Starting the server
source .env
mix phx.server
The relay is available at 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.
API
| Method | Path | Description |
|---|---|---|
GET |
/api |
List available endpoints |
GET |
/api/events |
Query events via URL params (since, until, limit required) |
POST |
/api/events/filter |
Query events with a NIP-01 filter body (limit required) |
GET |
/api/events/:id |
Fetch a single event by ID |
POST |
/api/events |
Publish a new event |
DELETE |
/api/events/:id |
Delete an event by ID |
GET |
/api/swagger |
Swagger UI |
GET |
/health |
Health check |
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):
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
iconfield
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
- Phoenix: https://www.phoenixframework.org/
- Nostr protocol: https://github.com/nostr-protocol/nostr
- NIP-01 (basic protocol): https://github.com/nostr-protocol/nips/blob/master/01.md