Skip to main content

guides/protocol.md

# Protocol

SafeRPC transports Erlang external term format (ETF) frames over a packet-oriented transport. The default transport is a Unix domain socket.

## Term shapes

SafeRPC currently uses four protocol terms.

Call request:

```elixir
{:safe_rpc, 1, id, cap, :call, op, payload, meta}
```

Cast request:

```elixir
{:safe_rpc, 1, id, cap, :cast, op, payload, meta}
```

Cancel request:

```elixir
{:safe_rpc_cancel, 1, id}
```

Reply:

```elixir
{:safe_rpc_reply, 1, id, result}
```

Each term is encoded with `:erlang.term_to_binary/1` and decoded with `:erlang.binary_to_term(binary, [:safe])`.

## Fields

- `id` correlates requests and replies.
- `cap` is an optional capability token checked on the server.
- `kind` is `:call` or `:cast`.
- `op` is the application operation. `use SafeRPC` services use `{Module, function}`.
- `payload` is the request term.
- `meta` is a map of per-request metadata.
- `result` is normally `{:ok, term}` or `{:error, reason}`, but SafeRPC does not enforce an application result schema.

## Built-in operations

SafeRPC reserves two operation atoms:

```elixir
:safe_rpc_describe
:safe_rpc_atoms
```

`:safe_rpc_describe` returns a native descriptor for tooling on compatible code paths.

`:safe_rpc_atoms` returns the service atom vocabulary as strings so independent clients can prepare their VM for safe ETF decoding.

Both operations go through normal server-side capability and authorizer checks.

## Safe decoding

SafeRPC does not use unsafe `binary_to_term/1` for protocol frames. Safe decoding rejects unknown atoms, remote references that would create atoms indirectly, and unsafe external function references.

For atom-rich cross-release replies, use `SafeRPC.prepare/2` before the call. See [Atom vocabularies and safe ETF](atom-vocabularies.md).

## Versioning

The protocol term version is currently `1`. Unknown or malformed request/reply terms are rejected by the protocol decoder.