plana
PLANA - Protocol for Live Agent Network Automation: a typed application-layer protocol for real-time state synchronization and control between a client shell and a backend service runtime, built on JSON-RPC 2.0 (the way HTTP is built on TCP). Not a general-purpose RPC framework.
This repo is split into four publishable crates:
| Crate | Role in the stack |
|---|---|
plana-protocol-core |
Generic protocol core - handshake and identity negotiation, base protocol messages, health/network descriptors, RBAC, and region policy. Independent of any specific platform domain. |
plana-celestia-types |
Celestia domain profile - the celestia-island platform's agent, task, panel, industrial and tool domain messages, built on the generic plana-protocol-core message set. |
plana-jsonrpc |
Framing & transport base - JSON-RPC 2.0 correlation, typed method routing, and Unix-socket / HTTP / WebSocket bindings. |
plana |
The protocol layer - re-exports the core, the domain profile and the framing, and adds server-side session management (SSE, events) behind the rpc-server feature. |
Architecture: generic core + domain profiles
PLANA is layered: the generic protocol core sits at the bottom, a domain profile plugs its message vocabulary in on top of it, and the umbrella crate re-exports both for consumers.
plana-protocol-core (generic message set) ← plana-celestia-types (domain profile) ← plana (umbrella)
└────────────────────────────────────────── plana-jsonrpc (framing; namespace! macro machinery)
- The generic core -
plana-protocol-coreowns the platform-independent message set: handshake/version/identity negotiation, base protocol messages, health and network descriptors, RBAC, and region policy. It knows nothing about agents, tasks, panels or any specific platform. The generic JSON-RPC 2.0 envelope lives with the framing crate (plana-jsonrpc) as the single canonical definition - a former copy here drifted and was removed. - Registering a domain - two mechanisms, one for the protocol's own
families and one for third-party profiles:
- Built-in method families declare their namespaces with the
namespace!macro (re-exported asplana::jsonrpc::namespace), which generates the typedMethodvariants whose wire names are derived from the enum path (Sync.Ping,Base.Heartbeat, …). TheMethodcatalog itself is a closed set — extending it is a change to the protocol crate. - Third-party profiles do not fork or patch the protocol: they register
their own method names directly into
plana::jsonrpc::RpcMethodMap(RpcMethodMap::empty().method("my.domain.op", handler), string-keyed dynamic dispatch over HTTP/WS viarpc_axum_router), so any"client shell <-> backend runtime"scenario can speak the same JSON-RPC 2.0 framing without the protocol knowing its methods.
- Built-in method families declare their namespaces with the
- The celestia profile -
plana-celestia-typesis the celestia-island platform's domain profile, built onplana-protocol-core. It ships by default (featurecelestia) because the celestia platform is PLANA's primary consumer; other consumers can disable it (default-features = false) and register their own profiles. - Shared module names -
httpandenumsexist in both the core and the profile; the umbrella merges them under one path (plana::http::*), with generic and domain types side by side. The full domain surface is also reachable atplana::celestia. - Why it matters - the protocol is usable beyond its origin: any "client shell <-> backend runtime" state-synchronization scenario can implement its own profile without forking the protocol.
Usage
Add to your Cargo.toml — either the umbrella crate:
[]
= { = "0.1", = ["rpc-server"] }
or just the parts you need:
[]
= { = "0.2" }
= { = "0.1" }
= { = "0.1" }
Registering and calling a JSON-RPC method
Built-in method families are declared with the namespace! macro (defined in
plana-jsonrpc, see pending.rs) and dispatched through the typed Method
enum. Third-party method names register directly into RpcMethodMap — no
enum extension needed:
use ;
use ;
Using the shared health/network wire types
use ;
Unix-socket transport
use Path;
use ;
let mut transport = connect.await?;
let response = transport.send.await?;
Feature flags
| Feature | Default | What it enables |
|---|---|---|
celestia |
yes | plana-celestia-types re-exported at the crate root and at plana::celestia; plana-protocol-core is always re-exported at the root. |
rpc-server |
no | Server-side SSE event streaming and request network/geo detection (rpc_server::detect_network). Transport sessions for SSE live in plana::jsonrpc::session. |
jsonrpc |
no | No-op compatibility feature — plana-jsonrpc is an always-on dependency now, so there is nothing to gate. Kept so consumers that declare it keep resolving. |
tracing-helpers |
no | Forwards plana-protocol-core/tracing-helpers: plana::tracing_helpers module with a ShortTimer formatting type. |
Consumer migration (post re-pin)
The feature surface of plana changed when the generic core was extracted:
jsonrpc— no-op now (plana-jsonrpc is always on). A no-op compat feature is kept so existing declarations keep resolving; do not rely on it to disable anything.types— renamed tocelestia. The domain types now live inplana-celestia-types; enablecelestiafor the domain vocabulary.tracing-helpers— forwarded toplana-protocol-core/tracing-helpers.
After re-pinning, declare the following features:
| Consumer | Features to declare |
|---|---|
| arona | rpc-server |
| shittim-chest | jsonrpc, tracing-helpers, celestia |
| evernight | jsonrpc |
| entelecheia | rpc-server, celestia (aspirational — see note below) |
| scriptum | jsonrpc (aspirational — see note below) |
entelecheia and scriptum rows are re-pin guidance, not current fact. entelecheia's manifest still pins the dead
plana-typescrate name (plana = { package = "plana-types", git = "…/plana", tag = "v0.1.9" }), and scriptum's Cargo.toml still pins the deadplana-typescrate name as a git dependency on livemaster; neither has been migrated to the splitplana/plana-celestia-typescrates yet. The rows above are the feature sets they should declare once re-pinned.
TypeScript bindings
Generated TS bindings live in two places, both regenerated by
just gen bindings:
packages/protocol-core/bindings/— the generic core types (health, RBAC, handshake, region policy, base messages). The JSON-RPC envelope (owned byplana-jsonrpc) is Rust-only and has no TypeScript export. In-repo for now; an npm home for the generic bindings package is a follow-up decision.packages/celestia-types/bindings/— the celestia domain profile types (agent, task, industrial, tool, malkuth supervision, …), shipped as@celestia-island/plana-celestia-types.
The generic types (e.g. HealthResponse, RbacUser, ConnectionStatus,
BaseHeartbeatParams, RegionPolicy) are exported only from
packages/protocol-core/bindings/; the celestia package ships domain types
only.
HealthDetailedwas removed in the core split (dead in-repo, unused by in-repo consumers). If you consumed it from the old@celestia-island/plana-typesnpm bindings, migrate to the structured health fields (HealthResponse/ServiceStatus/ConnectionStatus).
Stability
Pre-1.0: the API is subject to change. The wire formats (serde layouts, method naming conventions) are treated carefully — changes that affect on-the-wire compatibility are always considered breaking.
Not a general-purpose RPC framework
plana is the scaffold for one specific typed bidirectional sync protocol,
not a general-purpose RPC framework. The wire types and JSON-RPC machinery
are shaped by that protocol's needs; for a generic RPC stack, look at
established frameworks instead.