KoderIPC Protocol
Protocolo de comunicação entre apps Koder (cross-process, cross-app IPC). Mensagens, descoberta, autenticação, contratos. Implementação: engines/sdk/koder_ipc. Aplicável em qualquer integração app↔app local.
Quando esta spec se aplica
Triggers primários
- Implementar comunicação entre dois apps Koder (IPC)
Todos os triggers
- Implementar comunicação entre dois apps Koder (IPC)
- Adicionar handler IPC em app Koder existente
- Definir contrato de mensagem cross-app
Corpo da especificação
KoderIPC Protocol — Spec v0.1
Overview
KoderIPC is the inter-app communication protocol for the Koder Stack. It enables any Koder app — regardless of language or platform — to send and receive structured messages from other Koder apps installed on the same device.
The protocol is language-agnostic: any language with a socket or pipe abstraction can implement it. Language-specific SDKs (Dart, Go, Rust, Koder Koda, etc.) are thin bindings over this spec.
Wire Format
All messages are JSON-RPC 2.0 objects, newline-delimited (\n terminates each message).
Request
{
"jsonrpc": "2.0",
"id": "<uuid-v4>",
"method": "<action>",
"params": { ... }
}
Response
{
"jsonrpc": "2.0",
"id": "<uuid-v4 matching request>",
"result": { ... }
}
Error response
{
"jsonrpc": "2.0",
"id": "<uuid-v4>",
"error": {
"code": -32000,
"message": "<human-readable>",
"data": { "koder_error_id": "<PRODUCT-CAT-CODE-SEQ>" }
}
}
Error codes follow JSON-RPC 2.0 standard codes plus Koder-defined codes in the -32000 to -32099 range:
| Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid request |
| -32601 | Method not found (capability not declared) |
| -32602 | Invalid params |
| -32000 | App not authorized (signature check failed) |
| -32001 | Capability disabled by user |
| -32002 | Target app not running |
Notification (fire-and-forget, no id)
{
"jsonrpc": "2.0",
"method": "<event>",
"params": { ... }
}
Transport Layer
The transport varies by platform. The message format is identical regardless of transport.
Linux desktop + CLI
- Unix domain socket:
/run/user/<uid>/koder/<slug>.sock - Created by the app at startup; removed on clean shutdown
- Permissions:
0600(owner only) - Multiple clients may connect simultaneously
macOS desktop + CLI
- Unix domain socket:
$TMPDIR/../C/koder/<slug>.sock(sandbox-compatible path) - Same semantics as Linux
Windows desktop + CLI
- Named pipe:
\\.\pipe\koder-<slug> - ACL: restricted to processes signed with the Koder code-signing certificate
- Multiple clients via
FILE_FLAG_OVERLAPPED+ connection instances
Android
- Primary: Explicit Intent to
KoderIPCService(component name:dev.koder.<slug>/.ipc.KoderIPCService)- Used for one-shot fire-and-forget actions
- Message JSON passed as
Stringextra"koder_ipc_message"
- Bidirecional: AIDL Bound Service (
IKoderIPC.aidl)- Used when the caller needs a response or ongoing interaction
- Protected by
dev.koder.permission.IPC(protectionLevel=signature)
iOS
- URL Scheme:
koder-<slug>://ipc/<action>?p=<base64-json-params>&reply=<caller-scheme>replyparam carries the caller's own URL scheme so the target can respond
- App Group shared container:
group.dev.koderfor file transfers and state - One-shot only; bidirecional patterns require polling or callback URL chains
Service Discovery
An app discovers other Koder apps at runtime via platform-specific mechanisms:
Linux/macOS
ls /run/user/<uid>/koder/*.sock → one socket per running app
The slug is the socket filename without .sock.
Windows
\\.\pipe\koder-* → enumerate via DeviceIoControl or CreateFile probing
Android
val intent = Intent("dev.koder.ipc.CAPABILITIES")
val apps = packageManager.queryIntentServices(intent, PackageManager.GET_META_DATA)
iOS
No native discovery. Apps are discovered via a shared group.dev.koder plist file (koder-registry.plist) that each app writes its slug and URL scheme to on launch.
Capability Advertisement
Every Koder app must respond to the capabilities method immediately after connection:
Request:
{ "jsonrpc": "2.0", "id": "1", "method": "capabilities", "params": {} }
Response:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"slug": "dek",
"version": "1.2.0",
"capabilities": ["play", "pause", "seek", "queue"],
"protocol_version": "0.1"
}
}
An app must not accept calls for capabilities it did not declare. Return error -32601 for undeclared methods.
Standard Methods
All Koder apps must implement these methods:
| Method | Description |
|---|---|
capabilities | Returns slug, version and capability list |
ping | Returns {"pong": true} — used for health checks |
All Koder apps should implement these if applicable:
| Method | Params | Description |
|---|---|---|
open | {"uri": "string"} | Open a file or URL |
share | {"uri": "string", "mime": "string"} | Receive a shared item |
auth_token | {} | Return current user's Koder ID token |
Domain-specific methods (e.g., play, pause, queue) are declared by the app and validated at runtime against the capability list.
Security
Signature verification
- Android: enforced by
protectionLevel="signature"ondev.koder.permission.IPC. Only apps signed with the same keystore can bind. - iOS: enforced by App Group entitlement membership (
group.dev.koder) — Apple only grants this to apps from the same developer account. - Desktop Linux/macOS: socket is
0600(only the owning user). Caller trust is implicit (same user session). For elevated trust, the receiver may verify the caller's binary signature viacodesign(macOS) orsha256sumagainst a known hash (Linux). - Desktop Windows: Named pipe ACL restricted to processes signed with the Koder EV certificate.
No cross-user IPC
Sockets are created under /run/user/<uid>/ — kernel enforces user isolation. Cross-user IPC is explicitly out of scope.
Versioning
- Protocol version is declared in the
capabilitiesresponse as"protocol_version": "<major>.<minor>" - Minor bumps are backward compatible (additive only)
- Major bumps require both sides to be updated
- Current version: 0.1
Sub-protocol: koder-x ↔ kolide activity feed (infra/linux/x #012)
The Wayland compositor (koder-x) and the desktop services daemon
(kolide-services) share a one-way input-activity feed that does not
go over the koder-x command IPC socket nor the session D-Bus.
Why it's separate. The activity feed runs in the compositor's input
hot path (every cursor motion, every keypress). The general koder-x
IPC is a request/response stream over a Unix socket — designed for
control plane traffic from kolide-shell. D-Bus is similarly heavy for
something that fires hundreds of times a second. The activity feed
needs a transport with negligible per-event cost: a single pwrite
plus an futimens on tmpfs.
Path. $XDG_RUNTIME_DIR/koder-x/last-activity. When
XDG_RUNTIME_DIR is unset, fall back to
/tmp/koder-x-last-activity-<uid>. Both koder-x and kolide-services
resolve the path with identical logic.
Format. 8 bytes, little-endian uint64, set to the compositor's
clock_gettime(CLOCK_MONOTONIC) value in nanoseconds at write time.
Consumers that only care about edges may ignore the body and watch
stat().st_mtim; the producer always bumps mtime via futimens to
make that valid.
Producer (koder-x). Calls input_activity_report() from every
server_cursor_motion/server_cursor_motion_absolute/
server_cursor_button and from keyboard_handle_key. Throttled to
one write per ≥1 second; throttle gate is a per-process static
int64_t last_write_ns. Resource: a single long-lived file
descriptor opened with O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC in
input_activity_init().
Consumer (kolide-services). power_manager.idle_tick() runs once
per second and stat()s the file. If st_mtim changed since the
last tick, it sets last_activity_monotonic = g_get_monotonic_time(),
flips activity_ever_reported = TRUE, and clears
lock_fired_this_idle / suspend_fired_this_idle.
Coexistence with ReportActivity D-Bus. Apps that want to keep
the screen alive (media players during playback, presentation tools)
still call org.koder.Kolide.Power.ReportActivity(). The file feed
exists in addition — it's the compositor-side emitter, not a
replacement for the D-Bus method.
Liveness. Absence of the file = compositor never started or never
seeded its initial write. kolide-services treats absence as
indistinguishable from "no activity yet" and does not auto-lock (the
same gate activity_ever_reported enforces today).
File: meta/docs/stack/specs/ipc/protocol.kmd
Trigger: read this spec before implementing IPC between any two Koder apps.
Referências
engines/sdk/koder_ipcpolicies/sdk-first.kmd