OTP ERLANG Wiregrid Plainwire · Manual 1.0

Boundary

Authorization and security

Wiregrid bounds its own runtime. The embedding application decides who a user is and what that user may do. Identity, TLS, database credentials, network segmentation, distribution cookies, backups, and product abuse rules stay outside the library.

The authorizer callback

defmodule MyApp.Auth do
  @behaviour Wiregrid.Authorizer

  @impl true
  def authorize(action, session, resource, context) do
    if MyApp.Policy.allow?(session.user_id, action, resource, context),
      do: :ok,
      else: {:error, :unauthorized}
  end
end

Wiregrid.start_instance(:chat,
  authorizer: MyApp.Auth,
  security_mode: :strict
)

authorize/4 receives the action, the session map, the resource (a topic, room, user id, or receipt id), and a context map. Return :ok or true to allow. Return false for {:error, :unauthorized}. Return {:error, reason} to propagate that error. Any other return becomes {:error, :authorization_failed}.

The callback runs through Wiregrid.Callback. A crash or a thrown value fails closed as {:error, :authorization_failed}. A timeout is {:error, :authorization_timeout}. A full isolated queue is {:error, :authorization_overloaded}. authorizer_mode is :inline by default, with callback_timeout_ms of 100. Isolated mode uses max_callback_pending.

Wiregrid.Authorizer.AllowAll is the default module. authorize_actor/6 skips the callback entirely when session_id is nil. That is the trusted in-process publish path: a node-local job with no acting session is not asked for permission. authorize_direct/6 does the same when the option list is empty or session_id is nil. Internet-facing transports need a real authorizer. Cowboy refuses to upgrade a socket while the instance still uses AllowAll, unless allow_permissive_authorizer: true is set on the handler. security_mode: :strict refuses to boot at all when the authorizer is AllowAll (:strict_security_requires_authorizer). Wiregrid.Authorizer.DenyAll is the opposite module, for tests and for instances that should stay closed until a policy is installed.

Actions you will see include :publish, :publish_room, :read, :activity, :receipt, {:signal, kind}, plus subscribe, join, and presence checks issued by the runtime. Restored resume edges are authorized again.

Terms, tokens, and logs

Untrusted strings are not converted to atoms. External Erlang terms go through Wiregrid.SafeTerm: binary_to_term with :safe, a byte limit, and compressed ETF rejected before decode. Storage and cache adapters use that codec for event blobs.

Transport tokens from issue_transport_token/3 and verify_transport_token/3 are a versioned binary envelope: HMAC-SHA256, constant-time compare, expiry, audience binding via transport_audience/1, nonces, and key ids so a ring can rotate. Resume tokens are separate: random, stored as hashes, bound to one user, single use, and rotated by resume_session/5.

Wiregrid does not export telemetry by itself. prometheus_metrics/1 emits aggregate counters and gauges with a fixed schema and no labels taken from users, topics, or URLs. Logs are written to avoid message bodies, authorization headers, bearer tokens, database credentials, and webhook secrets.

Network edges

The foreign gateway binds {127, 0, 0, 1} unless ip: says otherwise. allow_anonymous: true is accepted only for a loopback address: IPv4 127.0.0.0/8, IPv6 ::1, and the IPv4-mapped loopback form the gateway recognizes. Any other bind with that flag raises at startup. The gateway does not speak TLS. Terminate TLS in front of it, or keep the socket on loopback, and pass authenticate: fn user_id, token -> :ok end for anything you do not fully trust. Anonymous sessions are rejected when that function is absent and allow_anonymous is false.

Cowboy authenticates before websocket_init/1 creates a session. An Origin header is denied unless it is listed in origin_allowlist. Distribution and EPMD should stay on a private network. Cluster peers are trusted infrastructure.

Webhooks re-resolve the destination on every attempt, refuse private and special-use ranges, connect to the validated address while keeping TLS hostname checks, ignore redirects, and sign the body. Enable them only with an allowlist. See the configuration chapter for the numeric caps.

Before production

Run ./scripts/verify.sh on the OTP and Elixir pair you will deploy. Run the adapter integration tests for the stores you actually use. Load-test with the event sizes and fanout you expect; scripts/load.sh accepts USERS, TOPICS, and MESSAGES. Keep secrets out of the repository. Test restore of the durable store separately from the BEAM node. Report vulnerabilities through the path in SECURITY.md.

Wiregrid 1.0 · Plainwire · github.com/Plainwire-development/Wiregrid