Files
parrhesia/README.md
Steffen Beyer 1199369dd9 test: NAME:
nak - the nostr army knife command-line tool

USAGE:
   nak [global options] [command [command options]]

VERSION:
   0.17.3

COMMANDS:
   event    generates an encoded event and either prints it or sends it to a set of relays
   req      generates encoded REQ messages and optionally use them to talk to relays
   filter   applies an event filter to an event to see if it matches.
   fetch    fetches events related to the given nip19 or nip05 code from the included relay hints or the author's outbox relays.
   count    generates encoded COUNT messages and optionally use them to talk to relays
   decode   decodes nip19, nip21, nip05 or hex entities
   encode   encodes notes and other stuff to nip19 entities
   key      operations on secret keys: generate, derive, encrypt, decrypt
   verify   checks the hash and signature of an event given through stdin or as the first argument
   relay    gets the relay information document for the given relay, as JSON
   admin    manage relays using the relay management API
   bunker   starts a nip46 signer daemon with the given --sec key
   serve    starts an in-memory relay for testing purposes
   blossom  an army knife for blossom things
   dekey    handles NIP-4E decoupled encryption keys
   encrypt  encrypts a string with nip44 (or nip04 if specified using a flag) and returns the resulting ciphertext as base64
   decrypt  decrypts a base64 nip44 ciphertext (or nip04 if specified using a flag) and returns the resulting plaintext
   gift     gift-wraps (or unwraps) an event according to NIP-59
   outbox   manage outbox relay hints database
   wallet   displays the current wallet balance
   mcp      pander to the AI gods
   curl     calls curl but with a nip98 header
   fs       mount a FUSE filesystem that exposes Nostr events as files.
   publish  publishes a note with content from stdin
   git      git-related operations
   nip      list NIPs or get the description of a NIP from its number
   sync     sync events between two relays using negentropy
   spell    downloads a spell event and executes its REQ request
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --quiet, -q    do not print logs and info messages to stderr, use -qq to also not print anything to stdout (default: false)
   --verbose, -v  print more stuff than normally (default: false)
   --help, -h     show help
   --version      prints the version (default: false) based E2E tests
2026-03-14 00:15:06 +01:00

3.7 KiB

Parrhesia

Parrhesia is a Nostr relay server written in Elixir/OTP with PostgreSQL storage.

It exposes:

  • a WebSocket relay endpoint at /relay
  • NIP-11 relay info on GET /relay with Accept: application/nostr+json
  • operational HTTP endpoints (/health, /ready, /metrics)
  • a NIP-86-style management API at POST /management (NIP-98 auth)

Supported NIPs

Current supported_nips list:

1, 9, 11, 13, 17, 40, 42, 43, 44, 45, 50, 59, 62, 66, 70, 77, 86, 98

Requirements

  • Elixir ~> 1.19
  • Erlang/OTP 28
  • PostgreSQL (18 used in the dev environment; 16+ recommended)

Run locally

1) Prepare the database

Parrhesia uses these defaults in dev:

  • PGDATABASE=parrhesia_dev
  • PGHOST=localhost
  • PGPORT=5432
  • PGUSER=$USER

Create the DB and run migrations/seeds:

mix setup

2) Start the server

mix run --no-halt

Server listens on http://localhost:4000 by default.

WebSocket clients should connect to:

ws://localhost:4000/relay

Useful endpoints

  • GET /health -> ok
  • GET /ready -> readiness status
  • GET /metrics -> Prometheus metrics
  • GET /relay + Accept: application/nostr+json -> NIP-11 document
  • POST /management -> management API (requires NIP-98 auth)

Production configuration

In prod, these environment variables are used:

  • DATABASE_URL (required), e.g. ecto://USER:PASS@HOST/parrhesia_prod
  • POOL_SIZE (optional, default 10)
  • PORT (optional, default 4000)

config/runtime.exs reads these values at runtime in production releases.

Typical relay config

Add/override in config files (for example in config/prod.exs or a config/runtime.exs):

config :parrhesia, Parrhesia.Web.Endpoint,
  ip: {0, 0, 0, 0},
  port: 4000

config :parrhesia,
  limits: [
    max_frame_bytes: 1_048_576,
    max_event_bytes: 262_144,
    max_filters_per_req: 16,
    max_filter_limit: 500,
    max_subscriptions_per_connection: 32,
    max_event_future_skew_seconds: 900,
    max_outbound_queue: 256,
    outbound_drain_batch_size: 64,
    outbound_overflow_strategy: :close
  ],
  policies: [
    auth_required_for_writes: false,
    auth_required_for_reads: false,
    min_pow_difficulty: 0,
    accept_ephemeral_events: true,
    mls_group_event_ttl_seconds: 300,
    marmot_require_h_for_group_queries: true,
    marmot_group_max_h_values_per_filter: 32,
    marmot_group_max_query_window_seconds: 2_592_000,
    marmot_media_max_imeta_tags_per_event: 8,
    marmot_media_max_field_value_bytes: 1024,
    marmot_media_max_url_bytes: 2048,
    marmot_media_allowed_mime_prefixes: [],
    marmot_media_reject_mip04_v1: true,
    marmot_push_server_pubkeys: [],
    marmot_push_max_relay_tags: 16,
    marmot_push_max_payload_bytes: 65_536,
    marmot_push_max_trigger_age_seconds: 120,
    marmot_push_require_expiration: true,
    marmot_push_max_expiration_window_seconds: 120,
    marmot_push_max_server_recipients: 1
  ],
  features: [
    nip_45_count: true,
    nip_50_search: true,
    nip_77_negentropy: true,
    marmot_push_notifications: false
  ]

Deploy

Option A: Elixir release

export MIX_ENV=prod
export DATABASE_URL="ecto://USER:PASS@HOST/parrhesia_prod"
export POOL_SIZE=20

mix deps.get --only prod
mix compile
mix ecto.migrate
mix release

_build/prod/rel/parrhesia/bin/parrhesia foreground

For systemd/process managers, run the release command in foreground mode.

Option B: Nix package (default.nix)

Build:

nix-build

Run the built release from ./result/bin/parrhesia (release command interface).


Development quality checks

Before opening a PR:

mix precommit

For external CLI end-to-end checks with nak:

mix test.nak_e2e