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
- Feature flags
- Serialization
- Socket — server
- Socket — client
- Location
- Encryption — Timelock
- Dependency Graph — IronPrint
- 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:
| Function | Context |
|---|---|
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:
| Feature | Enables |
|---|---|
enc-timelock-keygen-now | encryption::timelock::timelock(None) — decryption path (key from system clock) |
enc-timelock-keygen-input | encryption::timelock::timelock(Some(t)) — encryption path (key from explicit time) |
enc-timelock-async-keygen-now | encryption::timelock::timelock_async(None) — async decryption path |
enc-timelock-async-keygen-input | encryption::timelock::timelock_async(Some(t)) — async encryption path |
encryption | All 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
| Section | Contents |
|---|---|
package | crate name + version |
build | profile, opt-level, target triple, rustc version, active feature flags |
deps | full normalised cargo metadata graph (sorted, no absolute paths) |
cargo_lock_sha256 | SHA-256 of Cargo.lock (comment lines stripped) |
source | SHA-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 file —
export(true)writesironprint.jsonto the crate root. Add it to.gitignoreto prevent accidental commits. - Build-time overhead —
cargo metadataruns on every rebuild triggered by thecargo:rerun-if-changeddirectives (changes tosrc/,Cargo.toml, orCargo.lock). - Feature scope —
build.featurescaptures active features of the crate being built, not toolkit-zero’s own features. - Path stripping — absolute and machine-specific paths are removed from
cargo metadataoutput 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.
| Module | Path | Re-exports |
|---|---|---|
| serialization | serialization::backend_deps | bincode, base64 |
| socket (server) | socket::backend_deps | bincode, base64, serde, tokio, log, bytes, serde_urlencoded, warp |
| socket (client) | socket::backend_deps | bincode, base64, serde, tokio, log, reqwest |
| location | location::backend_deps | tokio, serde, webbrowser, rand |
| encryption (timelock) | encryption::timelock::backend_deps | argon2, scrypt, zeroize, chrono, rand; tokio (async variants only) |
| dependency_graph | dependency_graph::backend_deps | serde_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.