Skip to content

API Module (api)

The api crate serves as the primary ingress point (Driving Adapter) on the left side of the Hexagonal Architecture. It receives external requests, enforces security policies, and routes them to the Core’s APIInterface. All API logic runs within the single pai-engine process.

The capability ports that the API Gateway routes to are: DeviceControlPort, SessionConfigPort, SensorRelayPort, InferencePort (see Core-Facing Ports and the routing matrix below). The table here lists the ingress adapters (how requests enter the system) and the protocol traits they implement; the routing matrix then maps each adapter to which of the four capability ports it may access.

Adapter names match the system architecture diagram (LocalSystemAdapter, SecureNetworkAdapter, OllamaAdapter, OpenAiAdapter, McpServerAdapter). The same implementation may back multiple adapter identities (e.g. one gRPC implementation for LocalSystem and SecureNetwork; one REST implementation for Ollama and OpenAI).

AdapterImplements Port(s)Capability FeatureTechnology / Purpose
LocalSystemAdapterGrpcServerPortapi_grpc_udsTonic → Unix Domain Sockets for local IPC
SecureNetworkAdapterGrpcServerPortapi_grpc_tcpTonic → Secure network RPC over TCP/TLS
OllamaAdapterRestServerPortapi_httpAxum → REST API (Ollama compatibility)
OpenAiAdapterRestServerPortapi_httpAxum → REST API (OpenAI compatibility)
McpServerAdapterMcpServerPortapi_mcp_serverSSE/Stdio → MCP Server for host/desktop integration
MockAdapterAll Ports (Mock)api_mockSimulated API endpoints for CI/testing

The api crate serves as the primary ingress point (Driving Adapter) on the left side of the Hexagonal Architecture. It receives external requests, enforces security policies via its routing matrix, and then invokes the core orchestrator. The system architecture diagram shows the API Gateway block with the same adapters, ports, and feature flags as documented on this page.

GUI / Companion App: A desktop or mobile companion application can be built as a client against these same APIs (gRPC UDS for local, gRPC TCP for remote, or REST for simple integration). The architecture does not require a separate “GUI port”; a GUI is simply another client of the existing API surface, subject to the same routing matrix and permission checks as any other caller.

crates/api/
├── src/
│ ├── domain/ # ApiManager, Router, Auth
│ │ └── ports.rs # Traits: `RestServerPort`, `GrpcServerPort`, `McpServerPort`
│ ├── adapters/ # Protocol implementations
│ │ ├── rest.rs # Axum HTTP/REST endpoints (OpenAI compat)
│ │ ├── grpc.rs # Tonic gRPC (UDS/TCP)
│ │ └── mcp_server.rs # SSE/Stdio MCP Server for external clients
│ └── lib.rs # Implements Core's `ApiInterface`
└── Cargo.toml

The gateway supports multiple protocols, each isolated by Cargo features:

FeatureProtocolUse Case
api_grpc_udsUnix Domain SocketsPrimary IPC interface: comparable to Linux syscalls. For applications/services running on the same hardware where paiEngine runs. This is the main API for 3rd-party developers building local applications on top of the engine (local IPC, systemd, CLI tools)
api_grpc_tcpSecure Network RPCFor communicating with external clients on other PCs over the network (TCP/TLS required). Enables remote control and configuration
api_httpREST APIOllama and OpenAI client compatibility (legacy/convenience API)
api_mcp_serverModel Context ProtocolHost PC integration (Claude Desktop, OpenClaw)
pub trait ApiAdapter: Send + Sync {
fn start(&self, core: Arc<dyn APIInterface>) -> Result<()>;
fn stop(&self) -> Result<()>;
}

The composition root instantiates appropriate API adapters based on configuration and feature flags.

The API module consists of several core port/adapter pairs. The port is a trait in the domain layer and the adapter is a protocol-specific implementation.

1. GrpcServerPort (Port) & LocalSystemAdapter / SecureNetworkAdapter

Section titled “1. GrpcServerPort (Port) & LocalSystemAdapter / SecureNetworkAdapter”

Role: gRPC server endpoint for local IPC and secure network access.

Description: GrpcServerPort is the domain port that defines how the Core is exposed over gRPC. A single gRPC implementation in grpc.rs backs two adapter identities in the routing matrix: LocalSystemAdapter (IPC) and SecureNetworkAdapter. LocalSystemAdapter binds to Unix Domain Sockets (api_grpc_uds) for same-machine callers; SecureNetworkAdapter binds to TCP/TLS (api_grpc_tcp) for remote clients. Feature flags control which listeners are compiled and enabled in the composition root.

Feature flags and defaults:

  • api_grpc_uds: LocalSystemAdapter; enabled in both desktop and rockchip profiles; primary local IPC.
  • api_grpc_tcp: SecureNetworkAdapter; enabled in the rockchip profile for remote clients.

2. RestServerPort (Port) & OllamaAdapter / OpenAiAdapter

Section titled “2. RestServerPort (Port) & OllamaAdapter / OpenAiAdapter”

Role: HTTP/REST compatibility layer.

Description: RestServerPort defines a REST-style API over HTTP. A single REST implementation in rest.rs (Axum) backs two adapter identities in the routing matrix: OllamaAdapter and OpenAiAdapter, providing endpoints compatible with Ollama and OpenAI client libraries respectively.

Feature flags and defaults:

  • api_http: OllamaAdapter and OpenAiAdapter; enabled in both desktop and rockchip profiles; compiles the Axum HTTP server and exposes the REST API.

3. McpServerPort (Port) & McpServerAdapter

Section titled “3. McpServerPort (Port) & McpServerAdapter”

Role: Model Context Protocol server for host/desktop integration.

Description: McpServerPort describes the contract for exposing the engine as an MCP Server. The McpServerAdapter in mcp_server.rs implements this port using SSE/Stdio so that external tools (e.g. Claude Desktop, OpenClaw) can connect to the AI device and use it as a remote sensor/tool. In the routing matrix this corresponds to the McpServer adapter type.

Feature flags and defaults:

  • api_mcp_server: enabled in both desktop and rockchip profiles; turns on the MCP Server endpoints.

Role: Test-only adapter implementing all API ports for CI and local development without network bindings.

Description: The MockAdapter is a composite adapter that implements all API-facing ports in-process, without opening real sockets. It is used in tests and CI to exercise the routing matrix and Core integration without binding to the OS networking stack.

Feature flags and defaults:

  • api_mock: enabled only in the test profile; provides simulated API endpoints and prevents accidental exposure of real network services in tests.

The Core exposes four capability ports to the API Gateway as separate Rust traits, defined in core and implemented by the SessionManager. These are not the same as the API module’s own protocol ports (GrpcServerPort, RestServerPort, McpServerPort): those describe how requests enter the system; the capability ports in this section describe what those requests can do.

Keeping each capability as a separate trait enables compile-time enforcement: an adapter that only receives &dyn InferencePort physically cannot call lifecycle operations, regardless of runtime checks.

PortResponsibility
DeviceControlPort (CRITICAL)Device physical state: reboot, firmware updates, factory reset, power off. Highest privilege; only accessible from local/IPC callers.
SessionConfigPortAI session state and preferences: model switching, system prompts, user permissions
SensorRelayPortAuthorized relay of sensor streams (camera frames, audio) to external callers. Coordinates the streaming pipeline; does not own hardware.
InferencePortStandard endpoint for AI workloads (text generation, object detection)

Routing & Authorization Matrix (Defense in Depth)

Section titled “Routing & Authorization Matrix (Defense in Depth)”

The APIManager enforces hardcoded routing rules. This matrix is the primary defense against “Confused Deputy” attacks:

AdapterDeviceControlPortSessionConfigPortSensorRelayPortInferencePort
LocalSystem (UDS/IPC)
SecureNetwork (gRPC TCP)
McpServer (MCP)
Ollama (HTTP)
OpenAI (HTTP)
  • Receives parsed requests from protocol adapters
  • Validates each request against the routing matrix
  • Forwards only authorized calls to the Core; disallowed port access is rejected before reaching the Core
  • Routing rules are hardcoded (in routing.rs) for auditability
impl ApiRouter {
pub fn route_request(&self, adapter_type: AdapterType, request: ApiRequest) -> Result<ApiResponse> {
let allowed_ports = match adapter_type {
AdapterType::LocalSystem => vec![Port::All],
AdapterType::SecureNetwork | AdapterType::McpServer => {
vec![Port::Inference, Port::SensorRelay, Port::SessionConfig]
},
AdapterType::Ollama | AdapterType::OpenAi => {
vec![Port::Inference]
},
};
if !allowed_ports.contains(&request.target_port()) {
return Err(ApiError::PortAccessDenied);
}
self.core.handle_request(request)
}
}

The MCP Server adapter (api_mcp_server) allows external clients (e.g., Claude Desktop on a host PC) to use the AI device as a remote sensor/tool.

The routing matrix provides the first security layer (port isolation). The second layer is the Permission System (HITL) which requires user confirmation for sensitive operations even within allowed ports. See Security Architecture for the full security model.

All Protobuf definitions use a versioned package name (package pai.v1;). This is a hard rule:

  • Never remove or repurpose a field number. Once field 1 is string text, it is string text forever in the v1 schema.
  • Major breaking changes require a new package version (pai.v2). The engine can serve both versions concurrently during a transition period, allowing the companion app and external clients to migrate independently.
  • Why: gRPC Protobuf is binary-encoded. Reusing field numbers silently corrupts data for any client that was compiled against the old schema.
// Correct: versioned package
package pai.v1;
message ChatRequest {
string text = 1; // Never reassign field 1 to a different type
// string image = 2; // Adding new fields with new numbers is safe
}

API endpoints that trigger state changes (start recording, load a model, start a stream) must be idempotent. If the same request arrives twice (due to a double-tap, a network retry, or a companion-app reconnect), the second call must return a success response (Already Recording) rather than starting a second parallel operation or returning an error.

This is enforced by the SessionManager’s StateMachine (see Core: Idempotency). The API Gateway forwards the request to Core; Core’s state machine determines whether the operation is already in progress and returns an appropriate idempotent response. The API adapter never needs to track state itself.