Skip to content

KoderIPC Protocol

ipc specs/ipc/protocol.kmd

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.

When this spec applies

Primary triggers

All triggers

Specification body

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:

CodeMeaning
-32700Parse error
-32600Invalid request
-32601Method not found (capability not declared)
-32602Invalid params
-32000App not authorized (signature check failed)
-32001Capability disabled by user
-32002Target 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 String extra "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>
    • reply param carries the caller's own URL scheme so the target can respond
  • App Group shared container: group.dev.koder for 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:

MethodDescription
capabilitiesReturns slug, version and capability list
pingReturns {"pong": true} — used for health checks

All Koder apps should implement these if applicable:

MethodParamsDescription
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" on dev.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 via codesign (macOS) or sha256sum against 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 capabilities response 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.

References