ipc

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package ipc is the wire layer between a Pilot app (the wallet, for now) and the daemon that hosts it. Length-prefixed JSON envelopes carry requests and replies over any io.ReadWriter — typically a unix-domain socket the daemon hands to the app at spawn time, but anything duplex works (net.Pipe in tests, a TCP connection for remote dev).

The wire is intentionally simple: 4-byte big-endian length + JSON envelope. Mirrors the pilot_header / ipc_envelope split in the architecture graph — framing here, semantics in the envelope.

Index

Constants

View Source
const MaxFrameSize = 1 << 20

MaxFrameSize bounds any single envelope. Defends against a malicious or runaway peer that tries to allocate gigabytes by sending a giant length prefix. 1 MiB is comfortably larger than any wallet IPC reply (the biggest is a paginated history slice).

Variables

View Source
var ErrFrameTooLarge = errors.New("ipc: frame exceeds max size")

ErrFrameTooLarge is returned by ReadFrame when the length prefix exceeds MaxFrameSize. The connection should be dropped.

View Source
var ErrMethodNotFound = errors.New("method not found")

ErrMethodNotFound is returned to a caller when no handler is registered for the requested method.

Functions

func Call

func Call(conn io.ReadWriter, method string, args, result any) error

Call sends one request envelope and waits for the matching reply on the same conn. Synchronous: callers must serialize concurrent Calls on a single conn (one in flight at a time).

If args is nil, the request payload is empty. If result is nil, the reply payload is discarded. Both are otherwise json-marshaled / unmarshaled.

Returns *ErrServerError on EnvErr replies, a wrapped framing error on transport failures, or nil on success.

func Serve

func Serve(ctx context.Context, conn io.ReadWriter, d *Dispatcher) error

Serve reads envelopes from conn in a loop, dispatches each request to the matching handler, and writes the reply. Returns nil on clean EOF (peer closed gracefully between frames) or a non-nil error otherwise.

Single-threaded: one request at a time per connection. If multiple goroutines need to share a connection on the caller side, they must coordinate write order outside this package. For higher fan-out, the daemon opens one Serve per connection in its own goroutine.

func WriteFrame

func WriteFrame(w io.Writer, env *Envelope) error

WriteFrame serializes env as JSON and writes it as a 4-byte BE length prefix followed by the JSON bytes. Safe to call from one goroutine; not safe to call concurrently on the same writer (no internal locking).

Types

type Dispatcher

type Dispatcher struct {
	// contains filtered or unexported fields
}

Dispatcher routes method names to handlers. Build once at startup, register all methods, then hand to Serve. Safe to read after setup; not safe to mutate concurrently with Serve.

func NewDispatcher

func NewDispatcher() *Dispatcher

NewDispatcher returns an empty Dispatcher.

func (*Dispatcher) Methods

func (d *Dispatcher) Methods() []string

Methods returns the registered method names (for inspection / logging).

func (*Dispatcher) Register

func (d *Dispatcher) Register(method string, h Handler)

Register binds method → handler. A second Register for the same method replaces the first (caller's responsibility to not do that by accident).

type Envelope

type Envelope struct {
	Type            EnvelopeType    `json:"type"`
	ReqID           string          `json:"req_id"`
	Method          string          `json:"method,omitempty"`
	AppID           string          `json:"app_id,omitempty"`
	ManifestVersion int             `json:"manifest_version,omitempty"`
	Payload         json.RawMessage `json:"payload,omitempty"`
	Error           string          `json:"error,omitempty"`
}

Envelope is the single message shape on the wire. ReqID is set by the caller and echoed in the reply so a multiplexing client can match.

AppID and ManifestVersion are set by the daemon when it bridges a call from one app to another — handlers can use them for the equivalent of `ipc.context()` (knowing who is calling, under which manifest_version). On a direct connection (no daemon in between) both fields are zero.

func ReadFrame

func ReadFrame(r io.Reader) (*Envelope, error)

ReadFrame reads one envelope from r. Returns io.EOF cleanly when the peer closes between frames. Any partial-frame condition is returned as a non-EOF error.

type EnvelopeType

type EnvelopeType string

EnvelopeType discriminates the three kinds of envelopes that flow.

const (
	// EnvReq is a method call from caller to server.
	EnvReq EnvelopeType = "req"
	// EnvReply is a successful return from server to caller.
	EnvReply EnvelopeType = "reply"
	// EnvErr is a typed error return; never raw exceptions.
	EnvErr EnvelopeType = "err"
)

type ErrServerError

type ErrServerError struct{ Msg string }

ErrServerError wraps an EnvErr reply from the server side. Callers can `errors.As` to extract the wire-level error string.

func (*ErrServerError) Error

func (e *ErrServerError) Error() string

type Handler

type Handler func(ctx context.Context, req *Envelope) (json.RawMessage, error)

Handler is the server-side function that processes one request envelope. Return either (payload, nil) to send EnvReply, or (nil, err) to send EnvErr with err.Error() as the message.

Handlers should not write to the conn directly — the Serve loop owns the write side. ctx may carry deadlines / cancellation set by Serve.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL