ZyrisDocs
Addon crate guides

zyris-fs

Serve local files, page text reads, stream binary data, edit safely and understand the filesystem boundary.

zyris-fs provides LocalFileIo, the local-filesystem implementation of file_io v3. Use it for checkout tools, workstation nodes or services that expose files already on their host. It uses tokio::fs; it does not require a display, a PTY or a peer transport.

Dependencies

[dependencies]
zyris = { git = "https://github.com/attacca-cc/zyris-protocol", features = ["caps"] }
zyris-fs = { git = "https://github.com/attacca-cc/zyris-protocol" }

Add tls-ring or enroll when connecting to Attacca over wss://. Supply a Tokio runtime in the application. Keep all Zyris dependencies on the same source.

Register the implementation

use zyris::caps::FileIoServer;
use zyris::{Node, NodeKind};
use zyris_fs::LocalFileIo;

fn file_node() -> zyris::Result<Node> {
    Node::builder()
        .name("files")
        .kind(NodeKind::Service)
        .capability(FileIoServer(LocalFileIo::rooted("/srv/work")))
        .build()
}

Construction registers the capability but does not connect. Use Connecting or the complete addon node example.

Tools and arguments

ToolArgumentsResult / behavior
statpathFileStat: requested path, size, directory flag and optional modification time.
listpathVec<DirEntry>, sorted by name; each entry contains name, size and directory flag.
readpath, optional offset, optional lenUTF-8 text with byte offsets, actual byte length and a truncated flag. At most 128 KiB per response.
read_streampath, optional offset, optional lenA FileStat head followed by byte Chunk items. No 128 KiB total-response ceiling.
writepath, data: Datum, overwrite: boolWrites text or inline file/image bytes, creates parent directories, returns the new stat.
editpath, old_string, new_string, replace_all: boolExact UTF-8 replacement; returns stat and replacement count.
removepath, optional recursiveRemoves a file or directory. Recursive removal is off when omitted.
mkdirpathCreates the directory and missing parents.

Import the FileIo trait when calling either LocalFileIo or FileIoClient methods.

Page through a text file

use zyris::caps::{FileIo, FileIoClient};
use zyris::Connection;
use std::time::Duration;

async fn read_text(conn: &Connection, path: String) -> zyris::Result<String> {
    let files: FileIoClient = conn.wait_capability(Duration::from_secs(5)).await?;
    let mut text = String::new();
    let mut offset = 0;
    loop {
        let page = files.read(path.clone(), Some(offset), None).await?;
        text.push_str(&page.content);
        offset = page.offset + page.len;
        if !page.truncated { break; }
        if page.len == 0 {
            return Err(zyris::Error::internal("file read made no progress"));
        }
    }
    Ok(text)
}

len counts source bytes, not Rust characters or the length of the decoded string. Invalid UTF-8 is replaced; a multibyte character split at a page boundary can also be replaced. Use read_stream for exact bytes rather than concatenating text pages for binary or byte-perfect work. The helper accumulates the whole result in memory; impose an application limit for untrusted files.

Stream exact bytes

Add futures-util = "0.3" to consume the typed stream:

use futures_util::StreamExt;
use zyris::caps::{FileIo, FileIoClient};
use zyris::Connection;
use std::time::Duration;

async fn count_bytes(conn: &Connection, path: String) -> zyris::Result<u64> {
    let files: FileIoClient = conn.wait_capability(Duration::from_secs(5)).await?;
    let mut read = files.read_stream(path, None, None).await?;
    let mut received = 0;
    while let Some(item) = read.items.next().await {
        let zyris::Chunk(bytes) = item?;
        received += bytes.len() as u64;
    }
    Ok(received)
}

For a file sink, write each chunk instead of collecting it. Save the successfully written byte offset if you need to restart after disconnect. An agent's unary tool caller may not support read_stream; use paged read for that caller.

Writes, edits and failures

This example creates a new text file and replaces one known substring. Run it only on a path your application is authorized to modify; overwrite = false intentionally rejects an existing file.

use std::time::Duration;
use zyris::caps::{FileIo, FileIoClient};
use zyris::{Connection, Datum};

async fn create_note(conn: &Connection) -> zyris::Result<()> {
    let files: FileIoClient = conn.wait_capability(Duration::from_secs(5)).await?;
    files.write(
        "notes/example.txt".into(),
        Datum::Text { text: "Status: draft\n".into(), format: None },
        false,
    ).await?;
    let edited = files.edit(
        "notes/example.txt".into(), "Status: draft".into(), "Status: ready".into(), false,
    ).await?;
    println!("replacements={}", edited.replaced);
    Ok(())
}
  • write accepts text and inline file/image blobs. Attachment blobs currently fail with invalid_params; do not mistake protocol attachment support for support in this implementation.
  • overwrite = false checks existence, but the check and write are not an atomic create-only operation. It is not a concurrency guarantee against another writer.
  • edit rejects an empty search string, invalid UTF-8, no match, or multiple matches when replace_all is false. It reads and rewrites the file; there is no compare-and-swap revision check.
  • Missing paths return not_found; OS permission errors map to forbidden_scope; other I/O errors normally map to internal. Here forbidden_scope can mean host permissions, not a credential grant.

Security and operational limits

rooted is a path default, not a jail. Absolute paths and .. can leave it, and filesystem symlinks can point outside it. The implementation neither isolates the OS user nor applies your product's read/write/delete policy. For an actual boundary, enforce policy consistently across all methods, including streams, and use appropriate OS isolation. Account for symlinks and races; a lexical prefix check alone is insufficient.

Use a separate custom capability for a read-only or narrower tool surface, or implement FileIo over your own storage and policy. See Writing capabilities.

Reference

Source and API · Published crate · Released rustdoc

On this page