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
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 /relaywithAccept: 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_devPGHOST=localhostPGPORT=5432PGUSER=$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->okGET /ready-> readiness statusGET /metrics-> Prometheus metricsGET /relay+Accept: application/nostr+json-> NIP-11 documentPOST /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_prodPOOL_SIZE(optional, default10)PORT(optional, default4000)
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