sacp-cbor
sacp-cbor is a strict, deterministic CBOR implementation of the SACP-CBOR/1 profile from the
Synext Agent Control Protocol (SACP).
It is designed for hot-path validation (WebSocket frames, API request bodies) with:
- allocation-free validation on success (
validate_canonical) - deterministic map ordering enforcement (canonical CBOR key ordering, by encoded key bytes)
- strict canonical integer/length encoding checks (shortest form)
- strict numeric rules (safe integers, bignums via tags 2/3, float64-only, canonical NaN, forbid
-0.0) - strict tag rules (only bignum tags 2 and 3)
no_stdsupport (with optionalallocfor owned types)
This crate intentionally keeps the core small and uncompromising. If bytes validate under SACP-CBOR/1, they are already canonical; therefore, for opaque payloads, semantic equality reduces to byte equality.
Status
- Version:
0.1.0 - License: MIT
- MSRV: Rust
1.75
Features
| Feature | Default | Meaning |
|---|---|---|
std |
yes | Implements std::error::Error for CborError. |
alloc |
yes | Enables owned AST types (CborValue, CborMap, CanonicalCbor) and canonical encoding. |
sha2 |
yes | Enables SHA-256 helpers for canonical CBOR bytes (sha256()). |
serde |
no | Enables serde-based conversions to/from canonical CBOR (to_vec, from_slice). |
Note: serde currently requires std + alloc.
no_std usage
- Validation-only (no allocation): disable default features:
= { = "0.1", = false }
no_std+alloc(owned values + encoding): enablealloc:
= { = "0.1", = false, = ["alloc"] }
no_std+alloc+sha2:
= { = "0.1", = false, = ["alloc", "sha2"] }
Note: alloc requires an allocator provided by your environment.
Quick start
Validate SACP-CBOR/1 bytes (hot path)
use ;
Decode into an owned AST (requires alloc)
use ;
let bytes = ; // {"a":1}
let v = decode_value?;
assert_eq!;
# Ok::
Serde encode/decode (requires serde + alloc)
use ;
use ;
let msg = Msg ;
let bytes = to_vec?;
let decoded: Msg = from_slice?;
assert_eq!;
# Ok::
Hash canonical bytes (requires sha2)
use ;
let bytes = ;
let canon = validate_canonical?;
let digest = canon.sha256;
API overview
validate_canonical(bytes, limits) -> CanonicalCborRefvalidate(bytes, limits) -> ()decode_value(bytes, limits) -> CborValue(featurealloc)CborValue::encode_canonical() -> Vec<u8>(featurealloc)cbor_equal(a, b) -> bool(featurealloc)to_vec<T: Serialize>(&T) -> Vec<u8>(featureserde+alloc)from_slice<T: DeserializeOwned>(bytes, limits) -> T(featureserde+alloc)
Fuzzing
This repository includes cargo-fuzz targets under ./fuzz.
Prerequisites:
Run:
The fuzz targets:
- validate arbitrary bytes under strict limits
- when validation succeeds, decode and re-encode and assert roundtrip identity
Benchmarks
Criterion benchmarks are under ./benches.
Run:
License
MIT. See LICENSE.