These docs are under active development.
On this page
How-to 5 min read

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 peios is 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 --open
    

    This 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, process Access control
    registry The registry
    event, msgpack Events

    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 normal std Rust program must use.
  • Static (--features static) links libpeios.a. That archive is a Rust staticlib: it bakes in its own copy of the Rust runtime (the panic = "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 links std — collides on those symbols (rust_begin_unwind, __rust_alloc_error_handler, …). So the static path is for C consumers and no_std Rust binaries that supply no conflicting runtime. A std Rust consumer — including cargo testmust 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:

  1. Environment override — set all three:
    • PEIOS_LIB_DIR — the directory containing libpeios.{so,a}
    • PEIOS_INCLUDE — the directory containing <peios.h>
    • PKM_UAPI — the directory containing <pkm/*.h> (referenced by peios.h's signatures)
  2. pkg-config — otherwise, if libpeios installs its peios.pc on PKG_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 peios crate; drop to peios-sys only 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.