CopperlineOS message‑port IPC: spec + client libraries.
ports defines how CopperlineOS processes talk: a tiny, low‑latency message protocol over Unix domain sockets with optional file‑descriptor passing (SCM_RIGHTS). This repo ships the spec, a Rust crate and a small C library, plus schemas and examples. Phase‑0 uses JSON/NDJSON; later phases add a compact binary ring format.
TL;DR: predictable, scriptable IPC you can use from Rust/C/shell—great for timelines and media control.
We want deterministic, observable IPC without the weight of gRPC/dbus. “Ports” give every service (e.g., copperd, compositord, audiomixerd) a named, scriptable endpoint that can move small commands, events, and the occasional FD for zero‑copy buffers.
- Small & explicit: request/response + event streams.
- FDs for zero‑copy: DMABUFs, memfd rings, etc.
- Scriptable: works with
socat,jq, and theportctltool. - Upgradable: stable JSON v0, future binary framing with zero‑copy rings.
- Protocol v0 (JSON/NDJSON): usable (subject to RFCs).
- Libraries: Rust crate + C library scaffolding.
- OS: Linux (Unix sockets, SCM_RIGHTS).
- License: MIT OR Apache‑2.0.
ports/
├─ spec/ # human-readable spec & schemas (JSON/YAML)
├─ rust/ # Rust crate: copperline-ports
├─ c/ # tiny C client library + headers
├─ examples/ # minimal client samples (Rust/C/shell)
└─ tests/ # protocol tests (golden cases)
flowchart LR
c1["Apps (Rust/C)"];
c2["Tools/CLI (portctl)"];
c3["Scripts (ARexx-Next)"];
p["Service Port"];
r["Req/Resp Handler"];
e["Event Stream"];
c1 --> p;
c2 --> p;
c3 --> p;
p --> r;
p --> e;
r --> e;
Text-only fallback (if Mermaid fails)
Apps/Tools/Scripts -> Service Port -> { Req/Resp Handler, Event Stream }
Req/Resp Handler -> Event Stream
- One socket per service (e.g.,
/run/copperline/copperd.sock). - Bidirectional: same connection carries requests, responses, and subscribed events.
- NDJSON framing: one JSON message per line (
\n). Binary payloads go via FDs.
- Deterministic semantics (idempotent commands where possible).
- Backpressure‑aware (bounded queues, explicit subscription).
- Composability (consistent envelopes across services).
- Security‑first (capability tokens, FD quotas, message size limits).
Non‑goals: remote networking, TLS, cross‑host routing (use a gateway if needed).
- Default path:
/run/copperline/<service>.sock(override via*D_SOCKETenv). - Optional abstract sockets (
@copperline/<service>) for sandboxed cases. - Services announce on
ping:{"cmd":"ping"} → {"ok":true,"name":"copperd","version":"0.1.0","protocol":0}
Each line is a single UTF‑8 JSON object. Clients MUST NOT embed newlines.
{
"id": "6f8f3c2a", // opaque by client
"cmd": "create_layer", // command name
"args": { "w":128,"h":128 }, // command arguments
"caps": "cap-xyz" // optional capability token
}idis echoed in responses; strings or integers are allowed.capscarries a bearer capability (see Security).- FDs (e.g., DMABUFs) are passed via SCM_RIGHTS alongside the JSON. If used, include an
fdsarray describing logical positions:{"id":"x","cmd":"bind_dmabuf","args":{"id":1,"fd_index":0,"w":128,"h":128,"format":"RGBA8"}}
{ "ok": true, "id": "6f8f3c2a", "result": { "id": 1 } }
{ "ok": false, "id": "6f8f3c2a", "error": { "code":"BAD_ARG", "message":"w must be >0" } }Out‑of‑band notifications (after subscribe). Events MAY include a sub field linking to the subscription id.
{ "event":"vsync", "usec":12345678, "frame":4242 }
{ "event":"irq", "tag":"tick", "time_us":123, "prog":42 }
{ "event":"error", "message":"device lost" }{ "id":"sub1", "cmd":"subscribe", "args":{"events":["vsync","pageflip"]} }Servers may support filters, e.g., {"events":["irq"],"tag":"tick"}.
BAD_CMD,BAD_ARG,NOT_FOUND,DENIED,BUSY,TIMEOUT,INTERNAL.- Services can extend with domain‑specific codes (e.g.,
BAD_FORMAT).
- Servers MUST bound their per‑client queues and can drop events with:
{"event":"dropped","reason":"backpressure","count":N}. - Clients SHOULD read continuously after subscribing.
- Default request timeout is service‑specific; clients may send
{"cmd":"cancel","id":"…"}'(optional v0 feature).
- Socket permissions: created with restrictive mode (e.g.,
0660) and owned by service group. - Capabilities: bearer tokens or numeric caps allowing specific commands/regs; carried in
caps. - Quotas: services enforce FD count, message size (e.g., 64 KiB), and rate limits.
- Validation: every service validates IDs, dimensions, formats, and FD lifetimes.
Network IPC is out‑of‑scope for v0. Use a gateway process that proxies and authenticates if remote control is required.
use copperline_ports::{PortClient, JsonMessage};
use serde_json::json;
fn main() -> anyhow::Result<()> {
// Connect
let mut cli = PortClient::connect("/run/copperline/copperd.sock")?;
// Ping
let resp: JsonMessage = cli.request(json!({"id":"ping1","cmd":"ping"}))?;
println!("pong: {}", resp);
// Load a tiny program
let program = json!({
"version":1,
"program":[ {"op":"WAIT","vsync":true} ]
});
let load = cli.request(json!({"id":"load1","cmd":"load","program":program}))?;
let pid = load["result"]["id"].as_i64().unwrap();
// Start it
cli.request(json!({"id":"start1","cmd":"start","id":pid}))?;
Ok(())
}Until crates are published, use a path dependency in your project’s Cargo.toml:
[dependencies]
copperline-ports = { path = "../ports/rust" }#include "cl_ports.h"
#include <stdio.h>
int main() {
clp_client_t* c = clp_connect("/run/copperline/copperd.sock");
clp_msg_t req = clp_msg_json("{"id":"ping1","cmd":"ping"}");
clp_msg_t resp;
if (clp_request(c, &req, &resp) == 0) {
printf("resp: %.*s\n", (int)resp.len, resp.data);
}
clp_close(c);
return 0;
}Build with pkg‑config once the library is installed:
cc ping.c $(pkg-config --cflags --libs copperline-ports)For quick testing (lives in CopperlineOS/tools):
# Send a request
portctl /run/copperline/copperd.sock '{"cmd":"ping"}'
# Subscribe to events
portctl /run/copperline/compositord.sock '{"cmd":"subscribe","args":{"events":["vsync"]}}' --stream- Protocol:
protocol= 0 for JSON v0; breaking changes bump this number. - Libraries: semver; v0.x until protocol stabilises.
- Services must return their protocol in
pingresponses.
- v0.1: JSON/NDJSON, request/response, subscriptions, FD passing, Rust/C clients.
- v0.2: Binary framing + zero‑copy rings; credit‑based backpressure.
- v0.3: Capability negotiation, per‑message priorities, metrics stream.
- v0.4: Formal schemas → codegen for clients/servers.
RFCs tracked in CopperlineOS/rfcs.
- Read
spec/README.mdandCONTRIBUTING.md. - Keep examples runnable with
portctl. - Protocol changes require an RFC and compatibility notes.
Code of Conduct: CODE_OF_CONDUCT.md.
Dual‑licensed under Apache‑2.0 OR MIT.
copperd: timeline enginecompositord: Vulkan/KMS compositorblitterd: 2D blitteraudiomixerd: audio graphtools/portctl: CLI for ports