tachyon-i2p 0.0.4

Safe async wrapper around i2pd-sys (native I2P `.b32.i2p` eepsite support)
Documentation

tachyon-i2p

Safe async wrapper around i2pd-sys, giving Tokio code a Destination (an I2P eepsite identity, reachable at a .b32.i2p address) with an I2pStream connection type that implements AsyncRead/AsyncWrite — no external i2pd/Java-I2P process, no SAM bridge, no separate service to run alongside your binary.

Status: pre-0.1 (0.0.x), developed alongside and consumed by the tachyon-web workspace. The API may still change without notice.

Why it's unsafe internally

libi2pd (the underlying router, from PurpleI2P/i2pd) is a C++ library with no stable C ABI. i2pd-sys bridges it through a small hand-written extern "C" shim, and this crate is where every unsafe call site into that shim lives, so consumers never have to relax their own forbid(unsafe_code). The public API exposes no pub unsafe fn and no raw pointers, but that safety rests on this crate's own review of libi2pd's threading and ownership contracts — documented at each unsafe block and in i2pd-sys/shim/shim.h — not on anything the compiler checks.

Usage

use tachyon_i2p::{I2pRouter, SigType, I2pError};

#[tokio::main]
async fn main() -> Result<(), I2pError> {
    let router = I2pRouter::start("my-eepsite").await?;
    let mut dest = router
        .destination_from_keys_file("my-eepsite.keys", true, SigType::default(), &[])
        .await?;
    println!("reachable at http://{}", dest.b32_address());

    loop {
        let _stream = dest.accept().await?; // implements AsyncRead + AsyncWrite
        // ... spawn a task to serve it ...
    }
}

The keys file is this destination's private identity — anyone who obtains it can impersonate the eepsite. It is created owner-only (0600) and written atomically; back it up accordingly, and reuse the same path across restarts to keep the same .b32.i2p address.

Only one I2pRouter may run per process at a time — libi2pd keeps its router context as a process-wide global. Starting a new one after the previous router has been dropped works.

Network participation

This crate runs a real I2P router, so it has a position in the network beyond hosting your own destinations. Defaults:

Transit tunnels Carried.
Bandwidth 256 KB/s, whole router.
Floodfill Off.
Config files None read. libi2pd only parses i2pd.conf in upstream's daemon.

Transit means carrying other users' tunnels, and is unrelated to your own destinations, which work either way. It defaults to on because a router that relays nothing gives an observer no cover traffic: every byte crossing your link is then yours, which makes correlating your service easier, and the refusal is itself a fingerprint. Turn it off when bandwidth is metered, or when the risk you care about is a memory-safety bug in libi2pd rather than traffic analysis.

RouterConfig is the only way to change any of this — the settings are read as the router comes up, so there is no equivalent on a running one:

use tachyon_i2p::{I2pRouter, RouterConfig, I2pError};

#[tokio::main]
async fn main() -> Result<(), I2pError> {
    let _router = I2pRouter::start_with_config(
        "my-eepsite",
        // Keep carrying transit, but bound what it costs.
        RouterConfig::default()
            .bandwidth_limit_kbps(512)
            .transit_share_percent(25)
            .max_transit_tunnels(500),
    )
    .await?;
    Ok(())
}

Building without the default transit feature is the stronger form of accepts_transit(false): libi2pd's tunnel build-request path is compiled out, so no transit tunnel can exist whatever RouterConfig says. I2pRouter::supports_transit() reports which build you got.

Crypto backend: aws-lc (default) vs fips

Both mirror i2pd-sys's features of the same name: aws-lc links regular AWS-LC, fips links the FIPS 140-3-validated AWS-LC-FIPS module instead. At least one must be enabled — building with neither is a compile error, not a silently crypto-less router.

For fips, prefer default-features = false, features = ["fips"]. Cargo features are additive, so a dependent crate reaching for aws-lc can still switch it back on; if both end up enabled, fips wins. See the "FIPS" section of i2pd-sys's README for what it does and does not get you before reaching for it to satisfy a compliance requirement.

The third feature, transit (also default), is the compile-time half of Network participation above.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.