briny
briny is one of the only Rust crates that enforces binary trust boundaries at compile time - zero unsafe, no-alloc, no-macro.
briny gives you airtight control over what data is trusted and when. It helps you securely parse, validate, and serialize binary-structured data without ever trusting unchecked input.
What Makes briny Different?
briny enforces Zero Trust Architecture (ZTA) principles at compile time. Just like Rust's ownership system prevents memory safety bugs before runtime, briny prevents logic from touching untrusted or unvalidated input. No hopeful parsing, no runtime footguns.
If you follow briny's rules
- All external data must pass
Validatebefore use - All deserialized structures are wrapped in
TrustedData - No logic can access unchecked input without being explicit
If you don't follow the rules
- It's like misusing
unsafe {}- you opt out of the safety net brinywon't stop you from writing broken or insecureValidateimpls- You can still violate trust boundaries after validation, if you ignore discipline
brinycan't enforce runtime misuse beyond the type system.
Why Use briny
- Enforce trust boundaries with marker traits (
Trusted,Untrusted) - Zero dependencies and
#![no_std]compatible - noalloceither - Built for embedded, security-critical, and sandboxed Rust systems
- Prevent bugs before you even test for them
Features
- Zero Trust Architecture (ZTA)–aligned
- Binary-safe serialization via
Pack/Unpack - Trusted vs. untrusted data split at the type level
- Fixed-size byte buffer abstraction (
ByteBuf<T, N>) - Dependency-free (no
stdoralloc)
Trust Model
briny enforces explicit trust boundaries using:
UntrustedData<T>: Marker for unsafe input (from users, network, disk, etc.)Validate: Trait that defines the rules for converting untrusted data into trusted formTrustedData<T>: Guarantees validation has occurred - only safe data gets in- Sealed trait
Trustedensures trust cannot be used outside the crate
This ZTA-style model improves security on many frontiers, meaning:
- No unchecked logic runs on untrusted data
- All transitions are explicit and type-checked
- You can't forget to validate
Example
use *;
;
Comparison
| Feature | serde |
validator |
nom |
briny |
|---|---|---|---|---|
| Compile-time security guarantees | N | N | N | Y |
| Blocks parsing before validation | N | N | N | Y |
| Validation enforced by compiler | N | N | N | Y |
| Type-level trust separation | N | N | N | Y |
no_std compatible |
~ | N | Y | Y |
| Accidental bypasses impossible | N | N | N | Y |
What briny Does Not Do
While briny enforces trust boundaries at compile time, it's not a one-size-fits-all validation framework. It doesn't...
- Parse or validate complex or nested data formats like JSON, XML, or YAML.
- Handle cryptographic operations or key management.
- Provide runtime-configurable validation rules or dynamic schema updates.
- Offer detailed validation error reporting with rich diagnostics.
- Support heap allocations or complex data structures requiring
stdoralloc.
Use briny when you need binary-safe, zero-cost, compile-time enforced trust for fixed-layout, embedded, or low-level data structures.
For everything else-especially rich data formats or dynamic validation-consider combining briny with crates like serde, validator, or nom.
Here is a comparison between briny, serde, validator, and nom where each must validate a 4-byte array where the first byte is 42.
serde - Deserialization without enforced validation
use Deserialize;
;
// deserialize blindly, even if data is bad
let my: Data = deserialize?;
// no guarantee this is safe!
assert_eq!; // Could panic or be wrong
Risk: Data is used before it's validated. The deserialized value is implicitly trusted.
validator - Runtime validation, trust still implicit
use ;
// data is deserialized before it's validated
let my: Data = from_str?;
my.validate?; // You must remember to call this!
Risk: Forgeting to call .validate() could end horrifically; everything is runtime-based.
nom - Binary parsing with separate validation
use ;
// parse succeeds regardless of content
let = parse?;
if my_data != 42
Risk: Parsing and validation are disconnected. It's easy to skip checks.
briny - Enforced trust boundaries at the type level
use *;
;
// from raw input to trusted, validated value
let buf = new;
let trusted: = unpack_and_validate?;
// cannot access trusted logic until valid
trusted.as_ref; // fully safe
Guaranteed: Data must be validated before it compiles. No unsafe access is even possible without going through the Validate gate.
Raw Bytes
Use ByteBuf<T, N> to handle fixed-size byte arrays before parsing:
use *;
This is useful when reading from sockets, files, or hardware registers.
Prelude
briny provides a prelude module for ergonomic imports:
use *;
// brings in: Validate, TrustedData, UntrustedData, Pack, Unpack, etc.
This crate is #![no_std], fully portable, and ideal for embedded and security-critical systems.
Project Status
- Security-first API design
- 100% safe Rust (no unsafe)
- Fully tested (integration and unit tests)
- No dependencies
#![no_std]support- Not dependent on
alloc - Community audits welcome
Contributing
Contributions, bug reports, and suggestions are welcome! This project aims to help build verifiably secure foundations for low-level and embedded Rust development.
License
briny is under an MIT license.