Skip to main content

Crate toolkit_zero

Crate toolkit_zero 

Source
Expand description

§toolkit-zero

A feature-selective Rust utility crate. Declare only the functionality your project requires via Cargo feature flags; each feature compiles exclusively the code it depends on, with no extraneous overhead.


§Table of Contents

  1. Feature flags
  2. Serialization
  3. Socket — server
  4. Socket — client
  5. Location
  6. Encryption — Timelock
  7. Dependency Graph — IronPrint
  8. Backend deps

§Feature flags

| Feature | Enables | Exposes | |—|—||—| | serialization | VEIL cipher (seal / open) | serialization | | socket-server | VEIL + typed HTTP server builder | socket::server | | socket-client | VEIL + typed HTTP client builder | socket::client | | socket | Both socket-server and socket-client | both | | location-native | Browser-based geolocation | location::browser | | location | Alias for location-native | location | | enc-timelock-keygen-now | Time-lock key derivation from the system clock | [encryption::timelock::derive_key_now] | | enc-timelock-keygen-input | Time-lock key derivation from a caller-supplied time | [encryption::timelock::derive_key_at] | | enc-timelock-async-keygen-now | Async variant of enc-timelock-keygen-now | [encryption::timelock::derive_key_now_async] | | enc-timelock-async-keygen-input | Async variant of enc-timelock-keygen-input | [encryption::timelock::derive_key_at_async] | | encryption | All four enc-timelock-* features | encryption::timelock | | dependency-graph-build | Attach a normalised dependency-graph snapshot (ironprint.json) at build time | dependency_graph::build | | dependency-graph-capture | Read the embedded ironprint.json snapshot at runtime | dependency_graph::capture | | backend-deps | Re-exports all third-party deps used by each active module | *::backend_deps |

[dependencies]
# Only the VEIL cipher
toolkit-zero = { version = "3", features = ["serialization"] }

# HTTP server only
toolkit-zero = { version = "3", features = ["socket-server"] }

# HTTP client only
toolkit-zero = { version = "3", features = ["socket-client"] }

# Both sides of the socket
toolkit-zero = { version = "3", features = ["socket"] }

# Geolocation (bundles socket-server automatically)
toolkit-zero = { version = "3", features = ["location"] }

# Full time-lock encryption suite
toolkit-zero = { version = "3", features = ["encryption"] }

# Attach IronPrint fingerprint in build.rs
# [build-dependencies]
toolkit-zero = { version = "3", features = ["dependency-graph-build"] }

# Read IronPrint fingerprint at runtime
# [dependencies]
toolkit-zero = { version = "3", features = ["dependency-graph-capture"] }

# Re-export deps alongside socket-server
toolkit-zero = { version = "3", features = ["socket-server", "backend-deps"] }

§Serialization

The serialization feature exposes the VEIL cipher — a custom, key-dependent binary codec that transforms any bincode-encodable value into an opaque byte sequence and back.

The two entry points are serialization::seal and serialization::open. Every output byte is a function of the complete input and the key; without the exact key, the output cannot be reversed.

use toolkit_zero::serialization::{seal, open, Encode, Decode};

#[derive(Encode, Decode, Debug, PartialEq)]
struct Point { x: f64, y: f64 }

let p = Point { x: 1.0, y: -2.0 };
let blob = seal(&p, None).unwrap();                          // default key
let back: Point = open(&blob, None).unwrap();
assert_eq!(p, back);

let blob2 = seal(&p, Some("my-key")).unwrap();              // custom key
let back2: Point = open(&blob2, Some("my-key")).unwrap();
assert_eq!(p, back2);

§Socket — server

The socket-server feature exposes a fluent, type-safe builder API for declaring and serving HTTP routes. Begin each route with socket::server::ServerMechanism, optionally attach a JSON body expectation, URL query parameters, or shared state, then finalise with .onconnect(handler). Register routes on a socket::server::Server and serve them with a single call to .serve(addr).await.

The [socket::server::reply!] macro is the primary way to construct responses.

use toolkit_zero::socket::server::{Server, ServerMechanism, reply, Status};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};

#[derive(Deserialize, Serialize, Clone)]
struct Item { id: u32, name: String }

#[derive(Deserialize)]
struct NewItem { name: String }

#[derive(Deserialize)]
struct Filter { page: u32 }

let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));

let mut server = Server::default();
server
    // Plain GET — no body, no state
    .mechanism(
        ServerMechanism::get("/health")
            .onconnect(|| async { reply!() })
    )
    // POST with a JSON body
    .mechanism(
        ServerMechanism::post("/items")
            .json::<NewItem>()
            .onconnect(|body: NewItem| async move {
                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
            })
    )
    // GET with shared state
    .mechanism(
        ServerMechanism::get("/items")
            .state(store.clone())
            .onconnect(|state: Arc<Mutex<Vec<Item>>>| async move {
                let items = state.lock().unwrap().clone();
                reply!(json => items)
            })
    )
    // GET with URL query parameters
    .mechanism(
        ServerMechanism::get("/items/search")
            .query::<Filter>()
            .onconnect(|f: Filter| async move {
                let _ = f.page;
                reply!()
            })
    );

server.serve(([127, 0, 0, 1], 8080)).await;

VEIL-encrypted routes are also supported via socket::server::ServerMechanism::encryption and socket::server::ServerMechanism::encrypted_query. The body or query is decrypted before the handler is called; a wrong key or corrupt payload returns 403 Forbidden automatically.

§#[mechanism] attribute macro

The socket::server::mechanism attribute macro is a concise alternative to the builder calls above. It replaces the decorated async fn in-place with the equivalent server.mechanism(...) statement — no separate registration step required. All 10 builder combinations are supported: plain, json, query, state, encrypted, encrypted_query, and every state + … combination.

use toolkit_zero::socket::server::{Server, mechanism, reply, Status};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct NewItem { name: String }

#[derive(Serialize, Clone)]
struct Item { id: u32, name: String }

let mut server = Server::default();

#[mechanism(server, GET, "/health")]
async fn health() { reply!() }

#[mechanism(server, POST, "/items", json)]
async fn create_item(body: NewItem) {
    reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
}

server.serve(([127, 0, 0, 1], 8080)).await;

See socket::server::mechanism for the full syntax reference.


§Socket — client

The socket-client feature exposes a fluent socket::client::Client for issuing HTTP requests. Construct a client from a socket::client::Target (a localhost port or a remote URL), select an HTTP method, optionally attach a body or query parameters, and call .send().await (async) or .send_sync() (blocking).

use toolkit_zero::socket::client::{Client, Target};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Clone)]
struct Item { id: u32, name: String }

#[derive(Serialize)]
struct NewItem { name: String }

#[derive(Serialize)]
struct Filter { page: u32 }

// Async-only client — safe inside #[tokio::main]
let client = Client::new_async(Target::Localhost(8080));

// Plain GET
let items: Vec<Item> = client.get("/items").send().await?;

// POST with JSON body
let created: Item = client
    .post("/items")
    .json(NewItem { name: "widget".into() })
    .send()
    .await?;

// GET with query params
let page: Vec<Item> = client
    .get("/items")
    .query(Filter { page: 2 })
    .send()
    .await?;

// Synchronous DELETE (Client::new_sync must be called outside any async runtime)
let _: Item = client.delete("/items/1").send_sync()?;

VEIL-encrypted requests are available via socket::client::RequestBuilder::encryption and socket::client::RequestBuilder::encrypted_query. The body or query parameters are sealed before the wire send; the response is opened automatically.

§#[request] attribute macro

The socket::client::request attribute macro is a concise alternative to the builder calls above. It replaces the decorated fn in-place with a let binding that performs the HTTP request. The function name becomes the binding name; the return type becomes R in the .send::<R>() turbofish. The function body is discarded. A return type annotation is required.

All five builder modes are supported: plain, json, query, encrypted, and encrypted_query. Each mode accepts either async (.send::<R>().await?) or sync (.send_sync::<R>()?).

use toolkit_zero::socket::client::{Client, Target, request};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Clone)]
struct Item { id: u32, name: String }

#[derive(Serialize)]
struct NewItem { name: String }

let client = Client::new_async(Target::Localhost(8080));

// Plain async GET
#[request(client, GET, "/items", async)]
async fn items() -> Vec<Item> {}

// POST with JSON body
#[request(client, POST, "/items", json(NewItem { name: "widget".into() }), async)]
async fn created() -> Item {}

// Synchronous DELETE
#[request(client, DELETE, "/items/1", sync)]
fn deleted() -> Item {}

See socket::client::request for the full syntax reference.


§Location

The location (or location-native) feature provides browser-based geographic coordinate acquisition. A temporary HTTP server is bound on a randomly assigned local port, the system default browser is directed to a consent page, and the coordinates are submitted via the standard Web Geolocation API. The server shuts itself down upon receiving a result.

Two entry points are available in location::browser:

FunctionContext
location::browser::__location__Blocking — safe from sync or async
location::browser::__location_async__Async — preferred inside #[tokio::main]
use toolkit_zero::location::browser::{__location__, __location_async__, PageTemplate};

// Blocking — works from sync main or from inside a Tokio runtime
match __location__(PageTemplate::default()) {
    Ok(data) => println!("lat={:.6}  lon={:.6}  ±{:.0}m",
                         data.latitude, data.longitude, data.accuracy),
    Err(e)   => eprintln!("location error: {e}"),
}

// Async — preferred when already inside #[tokio::main]
match __location_async__(PageTemplate::default()).await {
    Ok(data) => println!("lat={:.6}  lon={:.6}", data.latitude, data.longitude),
    Err(e)   => eprintln!("location error: {e}"),
}

The location::browser::PageTemplate enum controls what the user sees: a plain single-button page, a checkbox-gated variant, or a fully custom HTML document.


§Encryption — Timelock

The encryption feature (or any enc-timelock-* sub-feature) exposes a time-locked key derivation scheme. A 32-byte key is derived from a time string through a three-pass KDF chain:

Argon2id (pass 1) → scrypt (pass 2) → Argon2id (pass 3)

The key is reproducible only when the same time value, precision, format, and salts are supplied. An additional passphrase may be incorporated for a combined time × passphrase security model.

Features:

FeatureEnables
enc-timelock-keygen-nowencryption::timelock::timelock(None) — decryption path (key from system clock)
enc-timelock-keygen-inputencryption::timelock::timelock(Some(t)) — encryption path (key from explicit time)
enc-timelock-async-keygen-nowencryption::timelock::timelock_async(None) — async decryption path
enc-timelock-async-keygen-inputencryption::timelock::timelock_async(Some(t)) — async encryption path
encryptionAll four of the above

Presets: encryption::timelock::KdfPreset provides named parameter sets tuned per platform: Balanced, Paranoid, BalancedMac, ParanoidMac, BalancedX86, ParanoidX86, BalancedArm, ParanoidArm, and Custom(KdfParams).

use toolkit_zero::encryption::timelock::*;

// Encryption side — caller sets the unlock time
let salts = TimeLockSalts::generate();
let kdf   = KdfPreset::BalancedMac.params();
let at    = TimeLockTime::new(14, 30).unwrap();
// params = None → _at (encryption) path
let enc_key = timelock(
    Some(TimeLockCadence::None),
    Some(at),
    Some(TimePrecision::Minute),
    Some(TimeFormat::Hour24),
    Some(salts.clone()),
    Some(kdf),
    None,
).unwrap();

// Pack all settings (incl. salts + KDF params) into a self-contained header;
// store it in the ciphertext — salts and KDF params are not secret.
let header = pack(TimePrecision::Minute, TimeFormat::Hour24,
                  &TimeLockCadence::None, salts, kdf);

// Decryption side — load header from ciphertext; call at 14:30 local time.
// params = Some(header) → _now (decryption) path
let dec_key = timelock(
    None, None, None, None, None, None,
    Some(header),
).unwrap();
// enc_key.as_bytes() == dec_key.as_bytes() when called at 14:30 local time

§Dependency Graph — IronPrint

Two features; one for each side of the boundary.

dependency-graph-build (goes in [build-dependencies]): dependency_graph::build::generate_ironprint runs cargo metadata, hashes Cargo.lock and every .rs file under src/, captures the profile, target triple, rustc version, and active features, then writes a compact, normalised JSON document to $OUT_DIR/ironprint.json. dependency_graph::build::export optionally writes a pretty-printed copy alongside Cargo.toml for local inspection — pass false or cfg!(debug_assertions) to suppress it in release builds.

dependency-graph-capture (goes in [dependencies]): dependency_graph::capture::parse deserialises the embedded snapshot into a typed dependency_graph::capture::IronprintData struct. dependency_graph::capture::as_bytes returns the raw JSON bytes, which are stable and deterministic across equivalent builds.

§Sections captured in ironprint.json

SectionContents
packagecrate name + version
buildprofile, opt-level, target triple, rustc version, active feature flags
depsfull normalised cargo metadata graph (sorted, no absolute paths)
cargo_lock_sha256SHA-256 of Cargo.lock (comment lines stripped)
sourceSHA-256 of every .rs file under src/

§Setup

[dependencies]
toolkit-zero = { version = "3", features = ["dependency-graph-capture"] }

[build-dependencies]
toolkit-zero = { version = "3", features = ["dependency-graph-build"] }

build.rs:

fn main() {
    toolkit_zero::dependency_graph::build::generate_ironprint()
        .expect("ironprint generation failed");
    // optional: pretty-print to crate root for local inspection
    toolkit_zero::dependency_graph::build::export(cfg!(debug_assertions))
        .expect("ironprint export failed");
}

src/main.rs (or any binary):

use toolkit_zero::dependency_graph::capture;

const IRONPRINT: &str = include_str!(concat!(env!("OUT_DIR"), "/ironprint.json"));

fn main() {
    let data = capture::parse(IRONPRINT).expect("failed to parse ironprint");
    println!("{} v{}", data.package.name, data.package.version);
    println!("target : {}", data.build.target);
    println!("lock   : {}", data.cargo_lock_sha256);

    let raw = capture::as_bytes(IRONPRINT);
    println!("{} bytes", raw.len());
}

§Risks and considerations

  • Not tamper-proof — the fingerprint is embedded as plain text in the binary’s read-only data section and is readable by anyone with access to the binary. It is informational in nature; it does not constitute a security boundary.
  • Export fileexport(true) writes ironprint.json to the crate root. Add it to .gitignore to prevent accidental commits.
  • Build-time overheadcargo metadata runs on every rebuild triggered by the cargo:rerun-if-changed directives (changes to src/, Cargo.toml, or Cargo.lock).
  • Feature scopebuild.features captures active features of the crate being built, not toolkit-zero’s own features.
  • Path stripping — absolute and machine-specific paths are removed from cargo metadata output so the fingerprint is stable across machines.
  • Compile-time only — the snapshot reflects the build environment at compile time; it does not change at runtime.

§Backend deps

The backend-deps feature appends a backend_deps sub-module to each active module. Every such sub-module re-exports via pub use all third-party crates used internally by the parent module, allowing downstream crates to access those dependencies without separate Cargo.toml declarations.

ModulePathRe-exports
serializationserialization::backend_depsbincode, base64
socket (server)socket::backend_depsbincode, base64, serde, tokio, log, bytes, serde_urlencoded, warp
socket (client)socket::backend_depsbincode, base64, serde, tokio, log, reqwest
locationlocation::backend_depstokio, serde, webbrowser, rand
encryption (timelock)encryption::timelock::backend_depsargon2, scrypt, zeroize, chrono, rand; tokio (async variants only)
dependency_graphdependency_graph::backend_depsserde_json; sha2 (build side only)

Enabling backend-deps without any other feature compiles successfully but exposes no symbols; every re-export within backend_deps is individually gated on the corresponding parent feature.

Modules§

dependency_graph
Build-time dependency-graph fingerprinting (IronPrint).
encryption
location
Geographic location acquisition API.
serialization
Struct-to-binary serialization via the VEIL cipher.
socket
HTTP server and client utilities, selectively compiled via Cargo features.

Macros§

reply
Convenience macro for constructing warp reply results inside route handlers.