---
title: "Core Link"
url: "https://control.actana.ai/docs/architecture/core-link"
description: "The persistent mTLS WebSocket between a Core client and a Core: transport, bearer auth frame, and the event cursor."
updated: 2026-09-01T09:02:50+00:00
---

The **core link** is the persistent bidirectional WebSocket between a **Core client** and a **Core**. It carries PTY streams, task mutations, hook events, and notifications as framed JSON. Long-lived; survives Panel sleep; replays missed events on reconnect. File bytes do not go here — they use the Core's HTTPS `/v1/...` routes on the same port.

A Core client is any program that terminates a core link: the Panel, the `actana` CLI, an SDK automation. A Core serves many at once. None of them is privileged. The Panel is one Core client among the others.

## Transport

The socket is `wss://` with **TLS 1.3** and mutual certificate pinning. At first boot the Core mints a self-signed CA and a server certificate. Each pairing issues a **client certificate** for a key that was generated on the client and never left it. The TLS handshake is the identity handshake; TLS 1.3's AES-GCM / ChaCha20 is the encryption. There is no custom frame-level crypto.

After mTLS, the client presents a signed bearer `{coreId, exp, sig}` in an `auth` frame. The Core checks `exp`. On expiry the client drops the socket, re-handshakes TLS, presents a fresh bearer, and reconnects. There is no mid-stream token swap: expiry uses the same reconnect path as laptop sleep.

The Panel dials the Core, never the reverse. Default port `8443`. Your reverse proxy does not terminate this TLS — the Core mints it.

```mermaid
sequenceDiagram
  participant Client as Core client
  participant Core as Core
  Client->>Core: TLS 1.3 handshake (mTLS, Core CA)
  Client->>Core: auth frame (bearer)
  Core->>Client: ready (capabilities)
  Client->>Core: lastEventId
  Core->>Client: eventsReplayed tail
  Note over Client,Core: PTY, mutations, hooks as JSON frames
  Note over Client,Core: File bytes never on this socket
```

## Event cursor

Every Event on a Core has a monotonic `eventId`, persisted in that Core's SQLite. A Core client stores the highest id it has seen — `lastEventId` — and sends it on reconnect to request the replay tail. For the Panel, that cursor is the only per-Core state besides the Core registry.

One core link per Core client, multiplexed. All PTY streams, task ops, and events between that client and that Core share a single WebSocket. A new connection never evicts an existing one.

## See also

- [Architecture](/docs/architecture) — Panel vs Core, Singular UI.
- [Panel link](/docs/architecture/panel-link) — the browser's socket to the Panel.
- [Write path](/docs/architecture/write-path) — mutations are core-link frames.
- [Filesystem](/docs/architecture/filesystem) — why file bytes are not on this socket.
- [Pairing](/docs/pairing) — short code, CA fingerprint, client cert.
