asx-rs
AS2 and AS4 B2B messaging for Rust — async-native, memory-safe, and fail-closed by default.
asx-rs implements AS2 (RFC 4130) and
AS4 (OASIS ebMS3 / eDelivery)
— the wire protocols behind PEPPOL, CEF eDelivery, BDEW, and a large share of the
world's EDI trading-partner connections.
It is an embeddable library, not a gateway. There is no container to run, no database to provision, and no JVM. You get the protocol and the cryptography; storage, key management and deployment stay yours.
📖 Documentation · 🦀 API reference · 📋 Changelog
Install
as2 and as4 are not on by default — enable the protocols you need.
| Flag | Enables | Default |
|---|---|---|
crypto |
Shared OpenSSL gate (RSA/ECDSA, X.509, CMS, OCSP) — pulled in by as2/as4 |
via protocol |
as2 |
AS2 send/receive, S/MIME, MDN | ❌ |
as4 |
AS4 send/receive, pull store, WS-Security, XML encryption | ❌ |
client |
HTTP egress via reqwest |
❌ |
server |
Axum router integration for inbound | ❌ |
compression |
AS2 (RFC 5402 CMS) and AS4 (gzip) payload compression | ✅ |
async-ocsp |
Async OCSP responder fetching | ✅ |
interop-strict |
Strict interop mode as the compile-time default | ✅ |
interop-relaxed |
Relaxed-mode helpers for legacy partners | ❌ |
trace |
tracing instrumentation on hot paths |
✅ |
prometheus / opentelemetry |
Metrics sink adapters | ❌ |
dns |
Built-in BDXL/NAPTR resolver for Peppol participant discovery | ❌ |
testing |
Bypass verifiers, mock endpoint, keypair generators | ❌ |
The
testingfeature raises acompile_error!in release builds. It cannot reach a production binary by accident.
Example
Send a signed AS4 message and verify the counterparty's receipt:
use ;
use ;
let = new
.action
.service
.signing_cert_pem
.signing_key_pem
.build?;
let sent = send_async.await?;
let outcome = new?
.send_and_verify
.await?;
A receipt is only evidence of delivery once its signature and non-repudiation
digests are checked against the message you sent. send_and_verify does both in
one call, so that step cannot be skipped by accident.
More examples — AS2 send, inbound routers, encryption enforcement, testing without a PKI — are in the getting-started guide.
What makes it different
The type system enforces the trust boundary. Payloads move through
UntrustedBytes → StructurallyParsed → CryptographicallyVerified → ContentDecrypted → DomainReady. Application code cannot receive bytes that
skipped a gate, because no value exists in that state.
Insecure configurations are unreachable by omission. Signatures are required, AS4 pull is denied, SMP lookup results are not trusted, and encrypted-spool profiles refuse to run without a key — until you explicitly choose otherwise. Every relaxation is a named variant you select, never a field you forgot.
Integration points are traits, not built-ins. Dedup, reconciliation, durable audit, DNS resolution and spool-encryption keys are interfaces you implement against your own PostgreSQL, Redis, resolver, KMS or HSM. The crate does not guess at their protocols — it ships a conformance suite instead, so "durable" and "cluster-safe" are things your backend demonstrates rather than declares. And it does not ship what it cannot know: there is no retry scheduler, no storage backend and no alerting engine, because a retry budget and a failure-rate threshold are properties of your partners and your topology, not of a protocol.
See the security model for trust boundaries, defaults, and what the library does not protect against.
Status
Beta. Core AS2 and AS4 flows are implemented and covered by ~1,000 unit and
integration tests, plus property, fuzz, golden-vector and cross-implementation
gates. The library contains no unsafe — #![forbid(unsafe_code)] keeps it
that way — and is clippy-clean at --all-features --all-targets.
MSRV is Rust 1.88, and CI builds on that exact floor.
Implemented:
- AS2 send/receive — signed, encrypted, compressed, sync and async MDN
- AS4 push send/receive — signed, encrypted, dedup, fragment reassembly
- AS4 pull with reliability classification and MPC authorization
- WS-Security: RSA-SHA256, ECDSA-SHA256, RSA-OAEP, ECDH-ES + ConcatKDF + AES-KW
- Cross-implementation interop, tested live: a containerized Holodeck B2B counterparty exchanges a signed AS4 push for a signed NRR receipt; C14N/digests are verified against xmlsec1; AS2 S/MIME is exchanged in both directions with the openssl CLI
- Reception Awareness / Non-Repudiation of Receipt, verified end to end
- OCSP and PKIX chain validation
- Multi-payload AS4, both directions — every
eb:PartInfoattachment signed, verified, and independently compressed/encrypted - ebMS3 Test Service (§5.2.2): a connectivity ping is acknowledged and reported as one, never delivered as a business document
- Peppol/CEF dynamic discovery — BDXL U-NAPTR participant lookup against the
OpenPeppol SML zones, plus SMP 1.0 and 2.0
ServiceMetadata, with the DNS query behind a trait so DNSSEC validation stays your choice
Known gaps:
- No official conformance certificate yet. The self-hosted counterparty loop passes; the free EC eDelivery conformance service is the next step — see the conformance guide.
- No storage backends ship in-tree.
DedupStorage,ReconciliationStorageandDurableAuditSinkare trait-defined; you supply the implementation — andasx_rs::storage::conformanceis the suite that proves yours is durable and itsfirst_seenatomic, rather than taking your word for it.
Development
Recipes mirror .github/workflows/ci.yml and
RELEASING.md; when a gate changes in one, change it in the
other.
The documentation site is built with Zola:
Versioning
Pre-1.0, breaking changes ship in minor versions. The changelog separates wire/crypto behaviour — which can break interoperability with a counterparty even when your code compiles unchanged — from API changes, which break the build instead.
License
Dual-licensed under MIT or Apache-2.0, at your option.