# Parrhesia Local API Parrhesia can run as a normal standalone relay application, but it also exposes a stable in-process API for Elixir callers that want to embed the relay inside a larger OTP system. This document describes that embedding surface. The runtime is still alpha, so treat the API as usable but not yet frozen. ## What embedding means today Embedding currently means: - the host app adds `:parrhesia` as a dependency and OTP application - the host app provides `config :parrhesia, ...` explicitly - the host app migrates the Parrhesia database schema - callers interact with the relay through `Parrhesia.API.*` Current operational assumptions: - Parrhesia runs one runtime per BEAM node - core processes use global module names such as `Parrhesia.Config` and `Parrhesia.Web.Endpoint` - the config defaults in this repo's `config/*.exs` are not imported automatically by a host app If you want multiple isolated relay instances inside one VM, Parrhesia does not support that cleanly yet. ## Minimal host setup Add the dependency in your host app: ```elixir defp deps do [ {:parrhesia, path: "../parrhesia"} ] end ``` Configure the runtime in your host app. At minimum you should carry over: ```elixir import Config config :postgrex, :json_library, JSON config :parrhesia, relay_url: "wss://relay.example.com/relay", listeners: %{}, storage: [backend: :postgres] config :parrhesia, Parrhesia.Repo, url: System.fetch_env!("DATABASE_URL"), pool_size: 10, types: Parrhesia.PostgresTypes config :parrhesia, Parrhesia.ReadRepo, url: System.fetch_env!("DATABASE_URL"), pool_size: 10, types: Parrhesia.PostgresTypes config :parrhesia, ecto_repos: [Parrhesia.Repo] ``` Notes: - Set `listeners: %{}` if you only want the in-process API and no HTTP/WebSocket ingress. - If you do want ingress, copy the listener shape from the config reference in [README.md](../README.md). - Production runtime overrides still use the `PARRHESIA_*` environment variables described in [README.md](../README.md). Migrate before serving traffic: ```elixir Parrhesia.Release.migrate() ``` In development, `mix ecto.migrate -r Parrhesia.Repo` works too. ## Starting the runtime In the common case, letting OTP start the `:parrhesia` application is enough. If you need to start the runtime explicitly under your own supervision tree, use `Parrhesia.Runtime`: ```elixir children = [ {Parrhesia.Runtime, name: Parrhesia.Supervisor} ] ``` ## Primary modules The in-process surface is centered on these modules: - `Parrhesia.API.Events` for publish, query, and count - `Parrhesia.API.Stream` for REQ-like local subscriptions - `Parrhesia.API.Auth` for event validation and NIP-98 auth parsing - `Parrhesia.API.Admin` for management operations - `Parrhesia.API.Identity` for relay-owned key management - `Parrhesia.API.ACL` for protected sync ACLs - `Parrhesia.API.Sync` for outbound relay sync management Generated ExDoc groups these modules under `Embedded API`. ## Request context Most calls take a `Parrhesia.API.RequestContext`. This carries authenticated pubkeys and caller metadata through policy checks. ```elixir %Parrhesia.API.RequestContext{ caller: :local, authenticated_pubkeys: MapSet.new() } ``` If your host app has already authenticated a user or peer, put that pubkey into `authenticated_pubkeys` before calling the API. ## Example ```elixir alias Parrhesia.API.Events alias Parrhesia.API.RequestContext alias Parrhesia.API.Stream context = %RequestContext{caller: :local} {:ok, publish_result} = Events.publish(event, context: context) {:ok, events} = Events.query([%{"kinds" => [1]}], context: context) {:ok, ref} = Stream.subscribe(self(), "local-sub", [%{"kinds" => [1]}], context: context) receive do {:parrhesia, :event, ^ref, "local-sub", event} -> event {:parrhesia, :eose, ^ref, "local-sub"} -> :ok end :ok = Stream.unsubscribe(ref) ``` ## Where to look next - [README.md](../README.md) for setup and the full config reference - [docs/SYNC.md](./SYNC.md) for relay-to-relay sync semantics - module docs under `Parrhesia.API.*` for per-function behavior