active-rpc 1.1.9

Active RPC — capability-based, promise-pipelined, multi-lane RPC for Active Infinity payloads and multi-agent systems
Documentation

Active RPC

Capability-based, promise-pipelined RPC for Active Infinity and multi-agent systems. gRPC is a transport; Active RPC is a security and execution model with a transport attached.

cap ──► call ──► promise ──► pipelined call ──► result
 │                    │
 └── attenuation ─────┴── lanes / channels / tools

Why it exists

An agent that calls tools and other agents needs four things that a request/response RPC does not give it:

  1. No ambient authority. A capability is an unforgeable id minted by the receiving kernel; holding it is the whole permission, attenuation can only narrow it, and revocation is immediate and transitive.
  2. Pipelining. A pending result can be an argument to the next call, so a chain costs one provider round-trip instead of one per step. Measured, not asserted: see tests/unit_tests.rs::pipelining_costs_one_round_trip….
  3. Concurrency primitives. Many lanes with independent budgets, many named channels, capability transfer over channels, and many tool handles in flight.
  4. Domain operations. evaluate, why, impact, subgraph and patch run on Active Infinity modules under the capability's budget, not on JSON.

Quick start

use active_rpc::cap::CapAttrs;
use active_rpc::domain::ModuleProvider;
use active_rpc::value::{Arg, Value};
use active_rpc::{Agent, Budget};

# fn main() -> Result<(), active_rpc::Error> {
let mut provider = ModuleProvider::new();
let object = provider.install(active_rpc::domain::module_from_source(
    "[server]\nport = 8080\n", "demo.active")?);
let agent = Agent::new();
let provider_id = agent.register(Box::new(provider));

let cap = agent.mint(provider_id, object, CapAttrs::new(&["evaluate", "why"]))?;
let lane = agent.create_lane("main", Budget::default())?;

let promise = agent.call(lane, &cap, "evaluate", vec![
    Arg::Value(Value::Path("server".into())),
])?;
assert!(matches!(promise.value()?, Value::Map(_)));
# Ok(())
# }

Pipelining:

# use active_rpc::{Agent, Budget, CapAttrs};
# use active_rpc::value::{Arg, Value};
# use active_rpc::domain::ModuleProvider;
# fn main() -> Result<(), active_rpc::Error> {
# let mut provider = ModuleProvider::new();
# let object = provider.install(active_rpc::domain::module_from_source("[a]\nb = 1\n", "x")?);
# let agent = Agent::new();
# let id = agent.register(Box::new(provider));
# let cap = agent.mint(id, object, CapAttrs::new(&["stats"]))?;
# let lane = agent.create_lane("main", Budget::default())?;
// The second call names the first result; both dispatch in one batch.
let first = agent.call(lane, &cap, "stats", vec![])?;
let second = agent.call(lane, &cap, "stats", vec![Arg::Promise(first.id())])?;
let _ = second.value()?;
assert_eq!(agent.round_trips(), 1);
# Ok(())
# }

Architecture

Module Responsibility
kernel capability table, promise graph, lane/channel registry, dispatch loop, events
cap ids, attributes, attenuation rules, handles with drop notification
promise promise states and single-settlement rules
lane / channel execution contexts and named streams with capability transfer
domain evaluate/why/impact/subgraph/patch over Infinity modules
tool MCP, HTTP/OpenAPI and sandboxed local-process adapters
wire the ARP1 envelope and capability table
transport in-process, TCP, shared memory, QUIC, gRPC/Connect bridge
security redaction and value mapping

The kernel is a deterministic state machine: providers are plain objects, the clock is a counter, and Agent::run() drives dispatch. That is why pipelining, attenuation, budget exhaustion and drop notification are testable without timing flakes — and why a transport is a thin adapter (ADR-0002).

Feature flags

Feature Default Meaning
quic on real QUIC transport (quinn + rustls, self-signed, loopback)

--no-default-features builds every transport except QUIC (in-process, TCP, shared memory, Connect bridge).

Minimum supported Rust

MSRV 1.88 (rust-version in Cargo.toml; ADR-0007 records why the QUIC transport's patched time sets the floor). active-infinity itself supports 1.85.

Testing

cargo test                 # unit, integration, concurrency, conformance, red team, property

cargo test --no-default-features

cargo bench                # wire, transports, kernel call loop

The suite proves, among other things: a forged capability is refused; attenuation cannot widen; a dropped handle notifies the provider; a pipelined chain costs one round-trip where a sequential chain costs two; each transport moves a real Infinity payload plus a capability table and rejects a malformed frame; two agents do not cross-talk over TCP; and secret material never appears in any value, envelope, annotation or provenance path.

Specifications

License

MIT OR Apache-2.0.