Capability addons
Reuse ready-made file, terminal, desktop and transfer implementations and combine them with your own capabilities.
You do not have to implement every tool before building a node. Capability addons are reusable Rust crates that supply existing implementations. Choose the ones your application needs, register them on your node, and focus on the tools that make your application different.
These addons are libraries compiled into your program, not plugins downloaded by a running node. Installing a crate alone does not announce tools: your program explicitly registers capabilities.
Understand the three layers
zyrissupplies the runtime, connection and capability registration.zyris-caps, also available aszyris::capswith thecapsfeature, supplies the standard traits, schemas, generated server wrappers and typed clients. It does not operate the machine.- An implementation addon supplies behavior behind a trait. For example,
zyris-fsimplementsFileIousing local files, andFileIoServermakes that implementation callable.
The standard catalogue describes tool names and versions. This page explains how to reuse and extend their implementations.
Choose an addon
Each addon has a detailed guide with setup, typed usage, limits and troubleshooting:
zyris-fs
Serve files, page text reads, stream bytes and enforce your storage policy.
zyris-terminal
Run commands, maintain PTYs, read a TUI screen and manage session cleanup.
zyris-screen
Configure screenshots, backends, byte budgets and display coordinates.
zyris-input
Choose input backends, restore portal grants and share screenshot geometry.
zyris-transfer
Wire sending and listening, peer trust, resumable delivery and receiving storage.
| Addon | Implementation and wrapper | What it provides |
|---|---|---|
zyris-fs | LocalFileIo, FileIoServer | Local file listing, metadata, reads, writes, edits, removal, directories and streaming reads. |
zyris-terminal | PtyTerminal, TerminalServer | Real PTY sessions, output buffers, terminal screen snapshots and one-shot commands. |
zyris-screen | HostScreenCapture, ScreenCaptureServer | Display enumeration and screenshots; HostDisplays supplies the shared display layout. |
zyris-input | EnigoInput, InputServer | Keyboard and pointer control in the same coordinate space as screenshots. |
zyris-transfer | LocalPeerTransfer, PeerTransferServer; LocalFileTransfer, FileTransferServer | Resumable peer deliveries and inboxes, plus the agent-facing sending surface. |
Not every zyris-* package is an implementation addon. zyris-core, zyris-proto and
zyris-macros are runtime, wire and code-generation layers. zyris-p2p
is a transport. zyris-attacca declares the server API a node consumes.
Registry releases versus main
File, terminal, screen and transfer addons are published on crates.io. zyris-input is git-only
because it pins an enigo fork. These docs track main, while the registry's 0.2.0 release
predates the current credential API. Use the same source for the runtime, declarations and addons
in one application. The examples here use git throughout; for registry releases, use compatible
registry versions throughout and consult their versioned docs.rs documentation.
The detailed addon guides and their Rust snippets were checked against
d9975e6.
Platform permissions and backend limitations still require testing on the actual host.
Combine ready-made tools with your own
This complete example adds file and terminal addons beside one custom health.status tool.
There is no need to fork the addons or recreate their schemas.
[package]
name = "addon-node"
version = "0.1.0"
edition = "2021"
[dependencies]
zyris = { git = "https://github.com/attacca-cc/zyris-protocol", features = ["caps", "tls-ring"] }
zyris-fs = { git = "https://github.com/attacca-cc/zyris-protocol" }
zyris-terminal = { git = "https://github.com/attacca-cc/zyris-protocol" }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }use zyris::caps::{FileIoServer, TerminalServer};
use zyris::{Node, NodeKind};
use zyris_fs::LocalFileIo;
use zyris_terminal::PtyTerminal;
#[zyris::capability(name = "health", version = 1)]
pub trait Health {
/// Check whether this application is ready to serve requests.
async fn status(&self) -> zyris::Result<String>;
}
struct AppHealth;
#[zyris::async_trait]
impl Health for AppHealth {
async fn status(&self) -> zyris::Result<String> {
Ok("ready".into())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = std::env::var("ZYRIS_CREDENTIAL")?;
let root = std::env::current_dir()?;
let link = Node::builder()
.name("addon-node")
.kind(NodeKind::Service)
.capability(FileIoServer(LocalFileIo::rooted(root.clone())))
.capability(TerminalServer(PtyTerminal::rooted(root)))
.capability(HealthServer(AppHealth))
.build()?
.connect(zyris::DEFAULT_SERVER_URL, &credential)
.await?;
tokio::select! {
closed = link.wait_closed() => closed?,
_ = tokio::signal::ctrl_c() => link.disconnect().await,
}
Ok(())
}Run from the directory relative paths should start in. Supply a stored credential, or adapt the quickstart enrollment flow for first-run registration. Never commit the credential.
A root directory is not a sandbox
LocalFileIo::rooted and PtyTerminal::rooted establish defaults for relative paths. Absolute
paths and .. can leave the root, and a shell can access anything its operating-system user can.
Enforce authorization and isolation before exposing these tools to untrusted callers.
Desktop addons
Serve screenshot and input capabilities together when an agent needs to see and act on a desktop.
Pass HostDisplays(capture.backend()) from zyris-screen to EnigoInput::new(..) so both use the
same backend, including an explicit capture override. Input initialization can fail on a headless
host; do not announce an unusable backend.
HostScreenCapture supports with_format and with_max_width. When screenshots are downscaled,
their descriptions identify the coordinate conversion needed before calling input. Do not apply
Display::scale_factor a second time.
| Addon / feature | Host requirements |
|---|---|
zyris-screen | Display development libraries on Linux. Uses wlroots capture where available, otherwise xcap; Wayland portal support matters. |
zyris-input | libxkbcommon on Linux; Accessibility permission on macOS. Default Linux input uses XTEST. |
zyris-input/wayland | wlroots virtual keyboard/pointer protocols and Wayland development libraries. |
zyris-input/libei | GNOME/KDE RemoteDesktop portal support and a user permission grant. |
The current libei input backend has a fractional-scaling limitation. Screen capture also documents
multi-monitor and rotation limitations. Read the maintained
screen README and
input README for your target
platform. Keep display dependencies out of headless services that do not need them.
File-transfer addons
zyris-transfer separates filesystem handling from the heavier peer transport:
| Feature | What it adds |
|---|---|
| No optional features | Receiving state, inbox handling, resumable transfers, audit records and replacement undo storage. |
send | Peer discovery, key pinning and dialing for file_transfer.send_to. |
listen | Accepting and identifying incoming peer connections. |
send and listen are independent and off by default. Both pull in zyris-p2p and iroh, which can
introduce a TLS C build. Registering a receiving implementation does not itself start a listener
or configure discovery; follow the
transfer documentation
when wiring those pieces together.
Receiving has its own defenses: names are reduced to one component, destination paths are checked
for symlinks, the per-file size limit applies, and existing files are moved aside before replacement.
The current max_inbox_bytes field is not enforced, and audit logging defaults to off. Configure
storage quotas and audit locations yourself. See the transfer guide
for the separate incoming/outgoing trust rules; these checks are not a host-wide sandbox.
Extend without starting from zero
- Compose. Add a domain-specific trait with
#[zyris::capability]and register its generated server beside the addons, as theHealthexample does. - Replace. Implement a standard trait yourself—for example,
FileIobacked by object storage—and use the same generated server wrapper. Callers retain the standard typed client. - Wrap. Put application policy in front of an addon, delegate allowed operations and reject
others. Cover streaming methods as well as unary methods; guarding only
readleaves other paths open.
Preserve the standard tool contract when replacing an implementation. Use a new capability name or version for incompatible arguments or behavior, and register the replacement instead of the original. Writing capabilities covers schemas, errors, streaming and versioning.
Migrate from zyris-capkit
zyris-capkit is a git-only compatibility facade, not the recommended dependency for new code.
Its implementations have moved into individual crates:
| Old import | New import |
|---|---|
zyris_capkit::LocalFileIo | zyris_fs::LocalFileIo |
zyris_capkit::PtyTerminal | zyris_terminal::PtyTerminal |
zyris_capkit::{HostScreenCapture, HostDisplays, ScreenBackend} | zyris_screen::{HostScreenCapture, HostDisplays, ScreenBackend} |
zyris_capkit::{EnigoInput, settings_for_session} | zyris_input::{EnigoInput, settings_for_session} |
zyris_capkit::transfer::* | zyris_transfer::* |
zyris_capkit::{Displays, resolve_under} | zyris_caps::{Displays, resolve_under} |
Move dependencies and imports together. Facade features transfer-send and input-libei become
zyris-transfer's send and zyris-input's libei. Add only the crates and features you use.
The facade is intended to disappear after its existing consumers migrate.
Source and API reference
- Implementation crates in zyris-protocol
- Published Zyris packages and their versioned docs.rs API references
- Crates and features for runtime layers and transports
- Standard capabilities for the tool catalogue