On this page
Using the SDK from Rust
The SDK is a C ABI, so it is reachable from any language with a C FFI — but Rust gets first-class, maintained bindings rather than hand-rolled extern blocks. If you are writing Rust on Peios, use them. This page is the on-ramp: what the crates are, how to wire them into a build, and where the docs live. It is deliberately not an API reference — that job belongs to the crate's rustdoc, which is generated from the code and so never drifts from it.
The two crates
peios-rs is two crates, layered:
| Crate | What it is | Use it when |
|---|---|---|
peios-sys |
Raw, unsafe FFI. Bindings are generated at build time by bindgen from the hand-written <peios.h> (the shipping API, which the ABI verifier proves is identical to the Rust source). |
You need the raw C surface — an escape hatch, or to build your own abstractions. |
peios |
The safe, idiomatic wrapper: RAII handle types, Result-returning methods, typed wire-constant families, and owned buffers — so you never touch a raw fd, a sticky-error builder, or a getxattr-style size probe directly. |
Almost always. This is the crate you want. |
Both bind libpeios strictly as a C library through its stable ABI — they never depend on libpeios's internal Rust crates. The C ABI is the only supported entry point, and binding through it means the crates exercise the exact surface every other consumer uses.
The safe crate's modules mirror the libpeios concept headers one-for-one — security, token, access, file, process, event, msgpack, registry — so everything you learn about the C surface maps straight across.
Where the docs live
Two places, and the split is deliberate:
-
The API reference is the crate's rustdoc. Every public item in
peiosis documented (the crate is built with#![warn(missing_docs)], so this is enforced), with a crate-level overview, per-module docs, and the error model. Build and browse it locally with:cargo doc -p peios --openThis is the reference precisely because it is generated from the code — it can never fall out of sync with the actual method signatures and types the way hand-written prose would.
-
The concepts live in these docs. The what and why — what a token is, how the two-call protocol works, how an access check reaches a verdict, the RSI source model — are identical whether you call from C or Rust, and they are documented once, here. The safe crate mirrors the concept headers, so each learn section maps to a module:
Rust module Concepts in security,token,access,file,processAccess control registryThe registry event,msgpackEvents Read the concept page for the model, then the rustdoc for the exact Rust API.
Adding the dependency
Add the peios crate to your Cargo.toml:
[dependencies]
peios = { git = "https://github.com/peios/peios-rs" }
(or a path/registry dependency, however you consume Peios crates). The features:
| Feature | Effect |
|---|---|
| default | Dynamic linking against libpeios.so — the intended production model (one soname-versioned system copy; fixes ship once). |
static |
Static linking against libpeios.a. See the caveat below. |
uapi |
Reuse the canonical Rust mirror of the pkm UAPI types (peios-uapi) instead of letting bindgen emit its own copy, so the kacs_*/pkm types are one identity across crates. |
A first taste
The safe crate turns the C conventions into ordinary Rust. Compare this with the C first program — no size probes, no manual close, errors as Result:
use peios::token::{Token, TokenAccess};
fn main() -> peios::Result<()> {
// Open my own token; the handle closes on drop.
let me = Token::open_self(false, TokenAccess::QUERY)?;
let sid = me.user()?; // owned Sid, not a caller buffer + length
let il = me.integrity()?; // typed IntegrityLevel, not a raw u32
println!("running as {sid} at integrity {il:?}");
Ok(())
}
The idiomatic wins show up most in the fiddly places. Impersonation, for instance, is an RAII guard — identity reverts when the guard drops, so it is exception-safe by construction rather than needing a manual revert in every cleanup path:
use std::os::fd::BorrowedFd;
fn serve(conn: BorrowedFd<'_>) -> peios::Result<()> {
let caller = Token::open_peer(conn)?;
let _guard = caller.impersonate_scoped()?; // now acting as the caller
do_work_as_caller()?; // any early return still reverts
Ok(())
// `_guard` drops here (or on the `?` above) → identity reverts automatically
}
Success-side status values — a file's opened-vs-created disposition, a key's created-new-vs-opened — come back alongside the handle, not as errors; genuine failures are the Err(Error) side, where Error wraps the errno the C ABI set.
Linking: dynamic vs static
This is the one Rust-specific wrinkle worth understanding, because the static path has a sharp edge.
- Dynamic (default) links
libpeios.so— the production model, and the path a normalstdRust program must use. - Static (
--features static) linkslibpeios.a. That archive is a Rust staticlib: it bakes in its own copy of the Rust runtime (thepanic = "abort"handler, the global-allocator shim, the alloc-error handler). Linking it into a consumer that also carries that runtime — i.e. any Rust binary that linksstd— collides on those symbols (rust_begin_unwind,__rust_alloc_error_handler, …). So the static path is for C consumers andno_stdRust binaries that supply no conflicting runtime. AstdRust consumer — includingcargo test— must use the dynamic path.
Keep that rule in mind and static linking is a non-issue: reach for it only from no_std, and use the default dynamic link everywhere else.
Finding libpeios at build time
peios-sys's build script resolves the library and headers in this order:
- Environment override — set all three:
PEIOS_LIB_DIR— the directory containinglibpeios.{so,a}PEIOS_INCLUDE— the directory containing<peios.h>PKM_UAPI— the directory containing<pkm/*.h>(referenced bypeios.h's signatures)
- pkg-config — otherwise, if libpeios installs its
peios.pconPKG_CONFIG_PATH.
On a system where libpeios is installed from its packages, pkg-config just works and you set nothing. For a local checkout, point the three variables at your build tree:
PEIOS_LIB_DIR=../libpeios/target/release \
PEIOS_INCLUDE=../libpeios/include \
PKM_UAPI=../pkm/uapi \
cargo build
One bring-up gotcha for local dynamic builds: the crate bakes an rpath that resolves libpeios by its soname (libpeios.so.0), but a plain cargo build of libpeios emits only the unversioned libpeios.so. Create the soname link once so the run resolves:
ln -sf libpeios.so ../libpeios/target/release/libpeios.so.0
(This is a checkout convenience — on a real system the packaged libpeios.so.0 is already there.)
The bottom line
- Depend on the
peioscrate; drop topeios-sysonly for the raw surface. - Concepts: these docs. API reference:
cargo doc -p peios --open. - Use the default dynamic link unless you are
no_std.
Everything the library conventions page teaches still holds underneath — the crate just wraps it in Rust idiom.