Skip to main content

zyris_transfer/
lib.rs

1//! The reference implementation of node-to-node file transfer for Zyris.
2//!
3//! [`zyris_caps`] declares two capabilities here and says nothing about how either is served.
4//! `peer_transfer` is the exchange two nodes have with each other; `file_transfer` is the surface
5//! an agent calls to start one and to read what has arrived. This crate is one answer to both.
6//!
7//! There is no human confirmation on the receiving side — an agent calls a tool and a file lands
8//! on someone else's machine. **The path jail, the size limit and the audit log are the only
9//! defenses there are.** This crate splits those between its parts.
10//!
11//! | Module | What it owns |
12//! |---|---|
13//! | [`name`] | Proposed name → a single path component. Never touches the filesystem |
14//! | [`inbox`] | Choosing the destination and checking the real path. Rejects symlinks |
15//! | [`undo`] | Moves the original aside before overwriting |
16//! | [`audit`] | Writes one line per transfer |
17//! | [`peer`] | Wires the above together into `peer_transfer` |
18//! | `send` | The other direction — the `file_transfer` surface an agent calls |
19//! | `listen` | Accepting peer connections, and deciding whose they are |
20//! | `rendezvous` | The Attacca client both of those ask where a peer is |
21//!
22//! `name` is kept apart from `inbox` on purpose — sanitizing is a pure decision, so dozens of
23//! cases run as one table test in an instant. Merged in, every one of those tests would need a
24//! `tempfile`.
25//!
26//! # Features
27//!
28//! Everything above the line is unconditional: it touches nothing but the filesystem, so it costs
29//! no transport dependencies, and it alone is enough to drive a whole exchange over
30//! `zyris::testing::duplex`.
31//!
32//! | Feature | What it adds |
33//! |---|---|
34//! | `send` | `file_transfer.send_to`: the rendezvous client and the iroh dial |
35//! | `listen` | Accepting peer connections: the rendezvous, and iroh to accept at all |
36//!
37//! **Both are off by default and each stands on its own.** The two directions are separately
38//! useful — a node can take deliveries without carrying the tool surface that makes them, and a
39//! node can make them without listening. `rendezvous` is compiled for either, which is what keeps
40//! that claim true: it used to live inside `send`, so `listen` reached across into it and did not
41//! compile alone, however plainly this said otherwise.
42//!
43//! Turning either on reaches iroh, and through it a `ring` build that needs a C toolchain. See
44//! this crate's `Cargo.toml`.
45//!
46//! ```no_run
47//! use zyris::{Node, NodeKind};
48//! use zyris_caps::PeerTransferServer;
49//! use zyris_transfer::{LocalPeerTransfer, TransferConfig};
50//!
51//! # fn serve() -> zyris::Result<Node> {
52//! let config = TransferConfig {
53//!     inbox: "/srv/inbox".into(),
54//!     undo: "/srv/undo".into(),
55//!     ..TransferConfig::default()
56//! };
57//! // The peer's own name, which decides which subdirectory of the inbox its files land in.
58//! let receiving = LocalPeerTransfer::receiver_pending(config, "laptop".to_string());
59//! let node = Node::builder()
60//!     .name("workstation")
61//!     .kind(NodeKind::Service)
62//!     .capability(PeerTransferServer(receiving))
63//!     .build()?;
64//! # Ok(node)
65//! # }
66//! ```
67
68pub mod audit;
69pub mod inbox;
70#[cfg(feature = "listen")]
71pub mod listen;
72pub mod name;
73pub mod peer;
74#[cfg(any(feature = "send", feature = "listen"))]
75pub mod rendezvous;
76#[cfg(feature = "send")]
77pub mod send;
78pub mod undo;
79
80pub use audit::{Audit, AuditLine};
81pub use inbox::{Inbox, InboxError};
82#[cfg(feature = "listen")]
83pub use listen::{
84    serve_peers, PeerCache, PeerDirectory, DEFAULT_DIRECTORY_TTL, DEFAULT_REFRESH_INTERVAL,
85};
86pub use name::safe_name;
87pub use peer::{InFlight, LocalPeerTransfer, TRANSFER_IN_FLIGHT, TransferConfig, part_path};
88#[cfg(any(feature = "send", feature = "listen"))]
89pub use rendezvous::Rendezvous;
90#[cfg(feature = "send")]
91pub use send::{
92    FileTransferConfig, IrohPeerLink, LocalFileTransfer, PeerLink, PeerSession,
93    DEFAULT_WIRE_DEADLINE,
94};
95pub use undo::UndoStore;