i2pd-sys 0.0.1

Raw FFI bindings to a minimal C shim over libi2pd (PurpleI2P/i2pd).
Documentation
# i2pd-sys

Raw FFI bindings to a minimal C shim over [`libi2pd`](https://github.com/PurpleI2P/i2pd)
(PurpleI2P/i2pd), the reference C++ implementation of the I2P anonymous-networking protocol.

This crate builds a real I2P router in-process — not a client for a separately-running `i2pd`
daemon — and exposes just enough of it (destinations and streams) through a small, stable `extern
"C"` surface for Rust to call into safely at the ABI level.

> **Status:** pre-1.0 (`0.0.1`), developed alongside and consumed by the
> [`tachyon-web`]https://codeberg.org/hacer-bark/tachyon-web workspace. The API may still
> change without notice.

## Is this the crate you want?

This crate is **100% `unsafe`** — raw pointers, manual lifetime management, no ownership types.
Unless you specifically need the bindgen-generated FFI surface itself (e.g. you're building your
own safe wrapper), you almost certainly want
[`tachyon-i2p`](https://codeberg.org/hacer-bark/tachyon-web/src/branch/main/tachyon-i2p) instead:
an async, safe `tokio`-based wrapper built directly on top of this crate.

## How it's built

`build.rs` does the following, entirely offline (no network access, no shelling out to
`configure`/`b2`/`bootstrap.sh`):

1. Compiles a trimmed **zlib** snapshot (`vendor/zlib-src`) directly via `cc::Build`.
2. Builds a minimal **Boost** subset (`vendor/boost-src`, extracted from the upstream git
   superproject with `bcp`) via Boost's own CMake support.
3. Builds **[AWS-LC]https://github.com/aws/aws-lc** (via the `aws-lc-sys` dependency) as the
   crypto/TLS backend, standing in for OpenSSL — nothing OpenSSL-shaped is vendored or linked.
4. Builds **libi2pd**/**libi2pdclient** (`vendor/i2pd-src`, a trimmed snapshot of upstream i2pd)
   via its own CMake build, as static libraries only (`WITH_BINARY=OFF` — no daemon binary).
5. Compiles `shim/shim.cpp`, a hand-written C++ file implementing the flat C ABI declared in
   `shim/shim.h`, and links everything together.
6. Runs `bindgen` against `shim/shim.h` **only** — never against `libi2pd`'s own C++ headers,
   which use `std::shared_ptr`/STL types with no stable C ABI.

Every dependency is a **committed source snapshot**, not a submodule or a build-time fetch, so
the crate builds reproducibly and works from a `crates.io` tarball with no network egress at
build time. See [`scripts/update-vendor.sh`](scripts/update-vendor.sh) for how each snapshot was
produced and how to refresh one against a newer upstream release.

**Hardening:** every C/C++ translation unit this crate compiles (zlib, i2pd, and the shim — all
three either handle attacker-controlled network/decompression input directly or sit right next to
code that does) is built with `-D_FORTIFY_SOURCE=2` and `-fstack-protector-strong`, applied as
plain compiler flags in `build.rs` rather than relying on i2pd's own `WITH_HARDENING` CMake option
(which is wired up for GCC only upstream — a silent no-op under Clang).

## Requirements

- A C++17 compiler (`cc`/`c++` on the host, found automatically).
- [CMake]https://cmake.org/ (used to build Boost and libi2pd).
- Rust `1.89`+ (edition 2024).

No system `boost`, `openssl`, or `i2pd` packages are required or used — everything is vendored
and statically linked.

### Cross-compilation

Native builds (including building natively *inside* a musl system, e.g. an Alpine container)
need nothing beyond the ambient `cc`/`c++`. True cross-compilation (e.g. a glibc host targeting
`x86_64-unknown-linux-musl`) requires a real musl C++ cross toolchain, since no C++ compiler
shipped by default on a glibc distro can target musl. `build.rs` looks for one, in order:

1. `CC_<target>` / `CXX_<target>` env vars (the standard [`cc`]https://docs.rs/cc crate
   convention), e.g. `CXX_x86_64_unknown_linux_musl`.
2. `MUSL_CROSS_TOOLCHAIN` pointing at a
   [`musl-cross-make`]https://github.com/richfelker/musl-cross-make-style install (expects
   `<dir>/bin/<target-triple>-g++`).
3. A same-named compiler already on `$PATH` (`<target-triple>-g++`).

If none is found, the build fails with an explanation rather than attempting to build a
toolchain from scratch (a 20–40 minute surprise on a library crate's first build).

## Usage

```toml
[dependencies]
i2pd-sys = "0.0.1"
```

```rust,no_run
use i2pd_sys::*;
use std::ffi::{c_void, CString};

unsafe {
    // 1. Initialize i2pd's config/crypto/router-context globals — exactly once, before
    //    anything else.
    let app_name = CString::new("my-app").unwrap();
    i2pd_init(app_name.as_ptr());
    i2pd_start();

    // 2. Create a destination (an I2P address you can send/receive on).
    let dest = i2pd_create_transient_destination();
    assert!(!dest.is_null());

    let addr = i2pd_destination_b32_address(dest);
    // ... read the `<52 chars>.b32.i2p` C string, then:
    i2pd_free_string(addr);

    // 3. Register an acceptor, then create streams to other destinations with
    //    i2pd_create_stream / i2pd_stream_send / i2pd_stream_receive.

    // 4. Tear down in reverse order.
    i2pd_destroy_destination(dest);
    i2pd_stop();
    i2pd_terminate();
}
```

See [`shim/shim.h`](shim/shim.h) for the full, documented API surface (lifecycle, destinations,
streams), and [`tests/roundtrip.rs`](tests/roundtrip.rs) for a complete two-destination,
real-network round trip.

### API overview

| Group | Functions |
|---|---|
| Lifecycle | `i2pd_init`, `i2pd_start`, `i2pd_stop`, `i2pd_terminate` |
| Destinations | `i2pd_create_transient_destination`, `i2pd_generate_keys`, `i2pd_create_persistent_destination`, `i2pd_destroy_destination`, `i2pd_destination_b32_address`, `i2pd_destination_ident_hash`, `i2pd_accept_stream` |
| Streams | `i2pd_create_stream`, `i2pd_stream_send`, `i2pd_stream_receive`, `i2pd_stream_is_open`, `i2pd_stream_close`, `i2pd_destroy_stream` |
| Memory | `i2pd_free_buffer`, `i2pd_free_string` |

### Thread safety

`i2pd_stream_send`/`i2pd_stream_receive` are safe to call concurrently from different threads on
the same stream (one thread sending, another receiving) — each posts work onto i2pd's internal
`io_service` and blocks the *calling* thread on a condition variable, the same pattern i2pd's own
SAM/BOB bridges use. The inbound-stream callback registered via `i2pd_accept_stream` runs on
i2pd's own internal thread and must return quickly without blocking, or it stalls the event loop
for every destination sharing that router.

Every shim function catches all C++ exceptions internally (`try { ... } catch (...) {}`), since
an exception unwinding across the `extern "C"` boundary would be undefined behavior — failures
surface as `NULL`/`0`/`-1` return values instead.

## Testing

```sh
cargo test -p i2pd-sys                              # build + unit tests, no network
cargo test -p i2pd-sys --test roundtrip -- --ignored --nocapture   # live I2P network round trip
```

The round-trip test is `#[ignore]`d by default: it builds real tunnels over the live I2P network
between two in-process destinations, which can take several minutes.

## Vendored third-party code

| Component | License | Snapshot |
|---|---|---|
| [i2pd]https://github.com/PurpleI2P/i2pd | BSD-3-Clause | tag `2.61.0`, commit [`635b013`]https://github.com/PurpleI2P/i2pd/commit/635b013a612ff47278ef02acf8580a28e10e26c5 |
| [Boost]https://www.boost.org/ (minimal subset via `bcp`) | Boost Software License 1.0 | tag `boost-1.86.0` |
| [zlib]https://zlib.net/ | zlib License | `1.3.1` |
| [AWS-LC]https://github.com/aws/aws-lc (via `aws-lc-sys`, not vendored directly) | Apache-2.0 OR ISC ||

Five small compatibility patches ([`scripts/patches/`](scripts/patches/)) are applied
automatically (by `i2pd_snapshot` in [`scripts/update-vendor.sh`](scripts/update-vendor.sh)) on
top of pristine upstream i2pd source. Four are `#ifdef OPENSSL_IS_AWSLC`-guarded, working around
AWS-LC's OpenSSL-1.1.1 API-compatibility gaps (siphash, GOST curve naming, raw ChaCha20,
compressed EC point decoding); those four apply equally to the `fips` backend below, since
AWS-LC-FIPS defines the same `OPENSSL_IS_AWSLC` marker. The fifth is unconditional (a missing
`#include <openssl/hmac.h>` that only matters — see the "FIPS" section below — under the `fips`
backend's slightly different header graph). Full attribution and license text:
[`LICENSE-THIRD-PARTY`](LICENSE-THIRD-PARTY).

## FIPS

By default this crate links regular [AWS-LC](https://github.com/aws/aws-lc) (via `aws-lc-sys`).
Enabling the `fips` feature instead (mutually exclusive with the default `aws-lc` feature —
`default-features = false, features = ["fips"]`) swaps in the FIPS 140-3-validated
[AWS-LC-FIPS](https://github.com/aws/aws-lc/tree/fips-2024-09-27) module via
[`aws-lc-fips-sys`](https://crates.io/crates/aws-lc-fips-sys) instead, with no *manual* source
changes needed on i2pd's side beyond the five patches above (four shared with the default
backend, one FIPS-specific) applied automatically by `scripts/update-vendor.sh`.

**What this does and does not get you:** linking a FIPS-validated crypto *module* is not the same
thing as a FIPS-*certified application*. i2pd's own protocol code hasn't been evaluated by NIST,
and some of i2pd's supported algorithm choices (legacy ElGamal destinations, GOST signatures —
`libi2pd/Gost.cpp`) aren't FIPS-approved at all; those code paths still compile and still work
identically either way (they only use generic, algorithm-agnostic EC/bignum primitives, not a
FIPS "service"), they just don't contribute to any compliance claim. Treat `fips` as "the
underlying crypto library has been through FIPS 140-3 validation," not as "this application is
FIPS compliant" — check with whoever owns your compliance requirement before relying on it as one.

**Costs of enabling it:**
- A much larger build: `aws-lc-fips-sys` vendors the full AWS-LC-FIPS source tree (~53 MiB) and
  compiles considerably more (self-test and integrity-check infrastructure the non-FIPS module
  doesn't have).
- A mandatory runtime self-check: AWS-LC-FIPS links a startup hook into every dependent binary
  that calls `FIPS_mode()` and **aborts the process** if the runtime library isn't actually
  running in FIPS mode. This is by design (it's what "FIPS-validated" requires operationally),
  but it's a real behavior change worth knowing about before flipping the feature on in
  production — a binary that built fine can still abort at startup on a host where FIPS mode
  isn't available.
- Narrower pregenerated-bindings platform support than the default backend: currently
  `{x86_64,aarch64}-unknown-linux-{gnu,musl}` and `{x86_64,aarch64}-apple-darwin`. Other targets
  may still work (`bindgen` can generate bindings on the fly) but aren't validated here.
- Go and Perl toolchains are used for some AWS-LC-FIPS code generation *if present*, falling back
  to committed pre-generated code if not — so, same as the rest of this crate, no new hard
  build-time tool requirement, just potentially slower/less-optimized codegen without them.

**Verified (2026-07-23):** `aws-lc-fips-sys` builds successfully standalone on this workspace's
host target, and its compiled `libcrypto` exports (BoringSSL-prefix-renamed, transparently to
i2pd's own source via the crate's generated headers) `CRYPTO_chacha_20`, `EC_GROUP_new_curve_GFp`,
`EC_POINT_set_compressed_coordinates_GFp`, and `FIPS_mode`/`BORINGSSL_integrity_test` (confirming
a genuine FIPS build, not a silent non-FIPS fallback). `cargo build --no-default-features
--features fips` compiles and links all of i2pd against it successfully end-to-end (this needed
the fifth patch mentioned above — the build failed with an undeclared-identifier error on plain
`HMAC()` without it), and `cargo test --no-default-features --features fips` passes the same
non-network test suite as the default backend, including real Ed25519/X25519 key generation and
persistent-destination round-tripping through AWS-LC-FIPS.

## License

Licensed under either of

- [Apache License, Version 2.0]LICENSE-APACHE
- [MIT license]LICENSE-MIT

at your option, in addition to the vendored third-party licenses listed above (which govern the
statically-linked C/C++ code, not this crate's own Rust source) — see
[`LICENSE-THIRD-PARTY`](LICENSE-THIRD-PARTY) for the full text and attribution of each.

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.