mirror of
https://github.com/zap-proto/dev.git
synced 2026-10-30 15:05:18 +00:00
Replace the fictional MCP-style tool-calling API (Zap.connect / call_tool / Gateway / ToolCall) and wrong package names with the verified zero-copy wire + capability RPC surface, every name checked against source. - go.mdx: github.com/zap-proto/[email protected]. Builder→Parse round-trip, zapgen codegen (NewPing/WrapPing/PingInput), the rpc call envelope (BuildRequest/ParseResponse + 3-value pipelining client), and the cap layer (Issue/Attenuate/Verifier.Verify). Compiled+ran a generated Calculator client/server end to end (prints 15). - python.mdx: PyPI zap-proto, import zap (not zap_protocol). Wire round-trip, zap.rpc (Server/Client/Method — ran end to end), zap.cap (issue/attenuate/ Verifier — ran issue+verify), and the [app] decorator app (ZAP + Client). - rust.mdx: zap-schema crate (zapc bin) + the zap-proto/rust workspace crates (zap/zapc/zap-rpc/zap-futures). Real zap-rpc Server trait + RpcSystem client + zap::cap (issue/Verifier). Drops nonexistent zap-proto/zap-codegen crates, spec-rs, zap_protocol module. - cpp.mdx: separates the cpp-core schema front-end + zero-copy zap:: runtime (MallocMessageBuilder / messageToFlatArray / TwoPartyClient) from the cpp high-level binding; drops the fictional zap::connect/callTool client. - quick-start.mdx: real install/codegen (zapgen, cargo install zap-schema, @zap-proto/zapc — not zap-codegen / @zap-proto/codegen / spec/cmd/zap) and real generated-RPC server/client per language.
233 lines
8.5 KiB
Plaintext
233 lines
8.5 KiB
Plaintext
---
|
|
title: Rust
|
|
description: ZAP Rust SDK — the zero-copy runtime, zapc codegen, Level-1 capability RPC, and the cap layer.
|
|
---
|
|
|
|
# Rust Binding
|
|
|
|
The Rust implementation of ZAP is a Cargo workspace
|
|
([`zap-proto/rust`](https://github.com/zap-proto/rust)) providing the zero-copy
|
|
message runtime, the Rust code-generation backend, and a Level-1 capability RPC
|
|
stack. The schema compiler ships separately as the
|
|
[`zap-schema`](https://crates.io/crates/zap-schema) crate, which also carries the
|
|
capability runtime (`zap::cap`).
|
|
|
|
ZAP describes data and interfaces in a whitespace-significant schema — no braces,
|
|
no required byte-offsets; blocks are delimited by indentation and ordinals are
|
|
assigned in declaration order. The legacy brace form still parses for backward
|
|
compatibility.
|
|
|
|
## Install
|
|
|
|
The schema compiler and the runtime are obtained separately.
|
|
|
|
**Schema compiler** — the CLI that turns a `.zap` file into Rust:
|
|
|
|
```bash
|
|
cargo install zap-schema # provides the `zapc` binary
|
|
```
|
|
|
|
The `zap-schema` crate bundles the canonical schema front-end (the same parser
|
|
every other language plugin uses, built from
|
|
[`zap-proto/cpp-core`](https://github.com/zap-proto/cpp-core)) together with the
|
|
Rust backend, so no external C++ toolchain is required at build time. The npm
|
|
wrapper [`@zap-proto/zapc`](https://www.npmjs.com/package/@zap-proto/zapc)
|
|
exposes the same backend for projects already on a Node toolchain — it downloads
|
|
a prebuilt, checksum-verified binary on install:
|
|
|
|
```bash
|
|
npm install -g @zap-proto/zapc
|
|
```
|
|
|
|
**Runtime** — add the workspace crates you need to `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
zap = { git = "https://github.com/zap-proto/rust" } # zero-copy messages
|
|
zapc = { git = "https://github.com/zap-proto/rust" } # build.rs codegen hook
|
|
zap-rpc = { git = "https://github.com/zap-proto/rust" } # capability RPC
|
|
zap-futures = { git = "https://github.com/zap-proto/rust" } # async read/write
|
|
tokio = { version = "1", features = ["full"] }
|
|
```
|
|
|
|
## Codegen
|
|
|
|
To generate code as part of a `cargo` build, call `zapc` from `build.rs`:
|
|
|
|
```rust
|
|
fn main() {
|
|
zapc::CompilerCommand::new()
|
|
.file("schema/hello_world.zap")
|
|
.run()
|
|
.expect("zap schema compile");
|
|
}
|
|
```
|
|
|
|
`zapc::CompilerCommand` execs the canonical `zap` schema front-end and feeds its
|
|
code-generator request to the Rust backend — the whitespace grammar (and its
|
|
brace back-compat) is handled entirely by that front-end, so there is exactly one
|
|
schema parser across the whole stack.
|
|
|
|
For this schema, the generated code includes a `point::Reader<'a>` with
|
|
`get_x()` / `get_y()`, a `point::Builder<'a>` with `set_x()` / `set_y()`, and —
|
|
for the interface — a `point_tracker::Server` trait and a `point_tracker::Client`:
|
|
|
|
```zap
|
|
struct Point
|
|
x Float32
|
|
y Float32
|
|
|
|
interface PointTracker
|
|
addPoint (p Point) -> (totalPoints UInt64)
|
|
```
|
|
|
|
The `'a` lifetime records that a reader/builder borrows the raw buffer holding
|
|
the encoded message — that buffer is never copied into a separate structure. The
|
|
encoding doubles as the in-memory representation, so reading a field is zero-copy.
|
|
|
|
## RPC
|
|
|
|
`zap-rpc` is an object-capability RPC system with Level-1 promise pipelining.
|
|
Generated from an interface, a `Server` trait carries one `async` method per
|
|
declaration, and a `Client` carries one `*_request()` builder per method. For
|
|
this schema:
|
|
|
|
```zap
|
|
interface HelloWorld
|
|
sayHello (request HelloRequest) -> (reply HelloReply)
|
|
```
|
|
|
|
### Server
|
|
|
|
Implement the generated `Server` trait, then hand an instance to
|
|
`zap_rpc::new_client` and serve it over a transport.
|
|
|
|
```rust
|
|
use zap_rpc::{rpc_twoparty_zap, twoparty, RpcSystem};
|
|
use crate::hello_world_zap::hello_world;
|
|
use futures::AsyncReadExt;
|
|
|
|
struct HelloWorldImpl;
|
|
|
|
impl hello_world::Server for HelloWorldImpl {
|
|
async fn say_hello(
|
|
self: std::rc::Rc<Self>,
|
|
params: hello_world::SayHelloParams,
|
|
mut results: hello_world::SayHelloResults,
|
|
) -> Result<(), ::zap::Error> {
|
|
let name = params.get()?.get_request()?.get_name()?.to_str()?;
|
|
results.get().init_reply().set_message(format!("Hello, {name}!"));
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Bind a listener and serve the bootstrap capability:
|
|
let hello_world_client: hello_world::Client = zap_rpc::new_client(HelloWorldImpl);
|
|
let (stream, _) = listener.accept().await?;
|
|
let (reader, writer) =
|
|
tokio_util::compat::TokioAsyncReadCompatExt::compat(stream).split();
|
|
let network = twoparty::VatNetwork::new(
|
|
futures::io::BufReader::new(reader),
|
|
futures::io::BufWriter::new(writer),
|
|
rpc_twoparty_zap::Side::Server,
|
|
Default::default(),
|
|
);
|
|
let rpc_system =
|
|
RpcSystem::new(Box::new(network), Some(hello_world_client.client));
|
|
tokio::task::spawn_local(rpc_system);
|
|
```
|
|
|
|
### Client
|
|
|
|
Bootstrap the remote interface, build a request, and `await` the promise.
|
|
|
|
```rust
|
|
let mut rpc_system = RpcSystem::new(rpc_network, None);
|
|
let hello_world: hello_world::Client =
|
|
rpc_system.bootstrap(rpc_twoparty_zap::Side::Server);
|
|
tokio::task::spawn_local(rpc_system);
|
|
|
|
let mut request = hello_world.say_hello_request();
|
|
request.get().init_request().set_name("world");
|
|
|
|
let reply = request.send().promise.await?;
|
|
println!("{}", reply.get()?.get_reply()?.get_message()?.to_str()?); // Hello, world!
|
|
```
|
|
|
|
Because `say_hello_request()` returns a request whose result can be used as the
|
|
target of another call before it resolves, dependent calls pipeline into a single
|
|
round trip — that is the Level-1 feature.
|
|
|
|
## Capabilities
|
|
|
|
The [`zap-schema`](https://crates.io/crates/zap-schema) crate's `cap` module is
|
|
the capability runtime. A `Capability` is a signed, attenuable token of authority
|
|
over a `target`, granted to a `holder` by an `issuer`; caps form a chain via the
|
|
`parent` field, and `Verifier::verify_chain` walks back to a root checking each
|
|
signature, expiry, revocation, target invariance, and monotonically-narrowing
|
|
permissions.
|
|
|
|
```rust
|
|
use zap::cap::{issue, Issuance, Verifier, Ed25519Signer, CapKind, Caveat, CaveatKind};
|
|
|
|
// Mint a root cap. Ed25519Signer is the built-in mandatory scheme; ML-DSA-65 is
|
|
// also provided, and a SchemeVerify hook wires hybrid / secp256k1.
|
|
let signer = Ed25519Signer::generate();
|
|
|
|
let cap = issue(
|
|
Issuance {
|
|
kind: CapKind::IamSession.value(),
|
|
target, // [u8; 32] resource hash
|
|
holder, // [u8; 32] holder hash
|
|
permissions: 0xDEAD_BEEF_CAFE_BABE, // bits the holder may exercise
|
|
expires_at: 2_000_000_000, // unix seconds; 0 = never
|
|
caveats: vec![
|
|
Caveat::new(CaveatKind::IpCidr, b"10.0.0.0/8".to_vec()),
|
|
],
|
|
..Default::default()
|
|
},
|
|
&signer,
|
|
)?;
|
|
|
|
// Verify a single cap. The Verifier carries the policy as builder closures.
|
|
let verifier = Verifier::new()
|
|
.with_issuer_key(move |_issuer| Ok(signer.public_key().to_vec()));
|
|
verifier.verify(&cap, 1_700_000_000)?; // Err(CapError::Expired) / Revoked / SigMismatch / …
|
|
```
|
|
|
|
Derive a narrower child with `Capability::attenuate`; the parent must carry
|
|
`PermAttenuate` (or be a `CapKind::Delegate` cap), permissions intersect, and
|
|
expiry can only shrink. `cap.cap_id()` is `SHA-256(canonical_bytes ‖ Sig)` and
|
|
`cap.canonical_bytes()` is the SPEC §3 signed scope — both byte-identical across
|
|
language runtimes. `verify_chain(leaf, chain, op, target, holder, now)` validates
|
|
a full chain.
|
|
|
|
> The crate name on crates.io is `zap-schema`; its library name is `zap`, so you
|
|
> `use zap::cap::…` after `zap-schema = "1"` (or the workspace `zap` crate, which
|
|
> re-exports the cap types as `zap::cap_issue`, `zap::Capability`, etc.).
|
|
|
|
## Features
|
|
|
|
- Tagged unions, generics, and forward-compatible protocol evolution
|
|
- Canonicalization for deterministic, signable encodings
|
|
- `Result`-based error handling — invalid pointers surface as errors, never UB
|
|
- `no_std` and no-alloc support
|
|
- Run-time reflection
|
|
|
|
## Build
|
|
|
|
```bash
|
|
cargo build --release
|
|
```
|
|
|
|
The library crates (`zap`, `zapc`, `zap-rpc`, `zap-futures`) build with no
|
|
external tooling. The example, test, and benchmark crates additionally need the
|
|
`zap` schema front-end on `PATH` (CI provisions it by building `zap_tool` from
|
|
`zap-proto/cpp-core`).
|
|
|
|
## Examples
|
|
|
|
- [zap-rpc/examples/hello-world](https://github.com/zap-proto/rust/tree/main/zap-rpc/examples/hello-world) — the generated client/server above, end to end
|
|
- [zap-rpc/examples](https://github.com/zap-proto/rust/tree/main/zap-rpc/examples) — calculator, pubsub, streaming, reconnect
|
|
- [example/addressbook](https://github.com/zap-proto/rust/tree/main/example/addressbook) — serialization round-trip
|