How Magnitude's Two-Layer Daemon Architecture (ACN and ICN) Works

Magnitude separates its local runtime into two cooperating daemons: the Application Control Daemon (ACN) manages durable application state and owns the Inference Control Daemon (ICN), which hosts the actual local-model inference engine.

The ACN and ICN form a parent-child relationship that isolates application lifecycle concerns from ML inference workloads. According to the Magnitude source code, this design lets clients interact with a single stable RPC surface while the ACN handles process supervision, recovery, and sandbox isolation of the ICN child. The architecture is implemented across packages/acn, packages/icn-protocol, and packages/sdk in the magnitudedev/magnitude repository.


ACN Responsibilities: The Application Control Daemon

The ACN is the authoritative local daemon that owns all durable application state.

  • Process lifecycle management: Handles ACN spawning, startup grace periods (2 s publication grace), shutdown sequences, and recovery logic.
  • Fenced spawn claims: Enforces that only one client may publish a new ACN candidate at a time, preventing race conditions without a separate coordinator.
  • ICN ownership: Launches, supervises, and terminates the ICN child process when shutting down.

The lifecycle rules are documented in [info/acn/lifecycle.md](https://github.com/magnitudedev/magnitude/blob/main/info/acn/lifecycle.md) and implemented in [packages/acn/src/boundary/acn.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/boundary/acn.ts).


ICN Responsibilities: The Inference Control Daemon

The ICN hosts the actual local-model inference engine.

  • Inference backends: Supports GGUF, Llama.cpp, and mlx-LM model formats.
  • Model catalog: Exposes discovery endpoints for available models.
  • Generation endpoints: Handles chat completions and other inference tasks.

Clients never talk directly to the ICN. Instead, the ACN forwards requests using the ICN protocol (@magnitudedev/icn-protocol), as shown in [packages/acn/src/icn-command-failure.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/icn-command-failure.ts).


How the Two Layers Interact

JIT Ensurance of the ACN

When a client initializes, the SDK calls ensureACN(target) to guarantee a compatible ACN is running. This function:

  1. Scans the filesystem for an existing ACN binary.
  2. If absent, spawns a new ACN process.
  3. Blocks until the ACN reaches Ready state (respecting the 30 s startup stall window and 5 min absolute ceiling).
import { ensureACN } from "@magnitudedev/sdk"

// Blocks until ACN is Ready; handles spawn claims automatically
await ensureACN({ target: "default" })

The lifecycle constraints are defined in [info/acn/lifecycle.md](https://github.com/magnitudedev/magnitude/blob/main/info/acn/lifecycle.md).

ACN Spawning and Claim Coordination

Magnitude uses fenced spawn claims to coordinate without a central coordinator:

  • One client holds the spawn claim exclusively.
  • That client publishes a new ACN candidate.
  • Other clients observe the claim and either adopt the new ACN or wait for Ready state.

This avoids split-brain scenarios during ACN replacement.

ACN Starts and Monitors ICN

Once Ready, the ACN launches its ICN child:

  • Keeps a process handle for monitoring and termination.
  • Wraps ICN errors into LocalModelMutationFailed via icnCommandFailure().
  • Enforces sandbox isolation boundaries.

Error translation occurs in [packages/acn/src/icn-command-failure.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/icn-command-failure.ts):

import { icnCommandFailure } from "@magnitudedev/acn"

// Convert low-level transport errors to typed failures
const err = icnCommandFailure("chatCompletion", transportError)

The Client → ACN → ICN RPC Flow

Step Actor Action
1 Client Calls SDK method (e.g., listModels, createChatCompletion).
2 SDK Routes request to ACN RPC surface.
3 ACN Forwards to ICN using ICN protocol.
4 ICN Executes inference, returns result.
5 ACN Adds metadata (model ranking, fits) and returns to client.

The ICN protocol schemas live in packages/icn-protocol/schemas/.

Recovery and Replacement

If the ACN transport fails, the SDK does not assume the ACN is dead. Instead:

  • Re-observes ACN state via filesystem signals.
  • Spawns a compatible successor ACN if needed.
  • Relaunches ICN under the new ACN parent.

This "recovery without panic" behavior is documented in [info/acn/lifecycle.md](https://github.com/magnitudedev/magnitude/blob/main/info/acn/lifecycle.md).


Complete Usage Example

Below is a runnable pattern showing how a client performs inference through the two-layer architecture:

// 1️⃣ Ensure ACN is running (JIT ensurance with lifecycle handling)
import { ensureACN } from "@magnitudedev/sdk"
await ensureACN({ target: "default" })

// 2️⃣ Create ICN client (SDK hides ACN↔ICN routing)
import { makeIcnApiClient } from "@magnitudedev/icn-protocol/client"
import * as S from "@magnitudedev/icn-protocol/schemas"

const icn = makeIcnApiClient({
  baseUrl: "http://127.0.0.1:8000"  // Routed to ACN-managed ICN
})

// 3️⃣ Perform OpenAI-compatible chat completion
const response = await icn.chat.completions.create({
  model: "ggml-phi-2",
  messages: [{ role: "user", content: "Explain ACN vs ICN" }]
})

// 4️⃣ Handle ICN errors (translated by ACN layer)
if (response._tag === "GeneratedClientTransportError") {
  const err = icnCommandFailure("chatCompletion", response)
  console.error(err.message)
}

Key implementation notes from the source:


Key Source Files

Component File Purpose
ACN Core [packages/acn/src/boundary/acn.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/boundary/acn.ts) RPC surface and lifecycle state machine.
ACN Protocol [packages/acn-protocol/src/boundary/acn.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn-protocol/src/boundary/acn.ts) Wire contract for SDK↔ACN communication.
ICN Error Mapping [packages/acn/src/icn-command-failure.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/icn-command-failure.ts) ICN error → LocalModelMutationFailed conversion.
SDK Inference API [packages/sdk/src/inference-client.ts](https://github.com/magnitudedev/magnitude/blob/main/packages/sdk/src/inference-client.ts) High-level client for model operations.
Lifecycle Rules [info/acn/lifecycle.md](https://github.com/magnitudedev/magnitude/blob/main/info/acn/lifecycle.md) Startup, shutdown, and recovery semantics.
ICN Schemas packages/icn-protocol/schemas/ Model catalog and completion request/response types.

Summary

  • ACN owns durable state, process lifecycle, and ICN supervision. It provides the sole RPC entry point for clients.
  • ICN runs isolated inference engines (GGUF, Llama.cpp, mlx-LM) and exposes model operations via the ICN protocol.
  • Coordination uses JIT ensurance (ensureACN), fenced spawn claims, and filesystem-based state observation—no separate coordinator process required.
  • Recovery is stateless: failed transports trigger re-observation, not panic, allowing seamless ACN/ICN replacement.

Frequently Asked Questions

What happens if the ICN crashes while the ACN is running?

The ACN monitors the ICN process handle and will terminate the ICN on shutdown. If the ICN exits unexpectedly, the ACN can relaunch it or propagate the failure via icnCommandFailure() as a LocalModelMutationFailed error, allowing clients to retry through a fresh ACN instance.

Why does Magnitude use two daemons instead of one?

Separation of concerns: the ACN handles long-lived application state and stable client-facing RPC, while the ICN runs sandboxed inference workloads that may crash, hang, or consume GPU resources unpredictably. This isolation prevents model-engine failures from corrupting application state.

How does ensureACN() prevent multiple clients from spawning conflicting ACNs?

It uses fenced spawn claims—filesystem-based exclusive locks where only one client holds the claim at a time. That client publishes the new ACN candidate; others observe and either adopt it or wait for Ready state, eliminating race conditions without a centralized service.

Can clients bypass the ACN and talk directly to the ICN?

No. The ICN protocol is only exposed through the ACN's forwarding layer. Clients use makeIcnApiClient() from the SDK, which routes through the ACN RPC surface. The ICN's network port is not directly exposed to clients, enforcing the ACN's supervision and sandbox policies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →