📦 asx-rs
Async-native, memory-safe AS2 + AS4 EDI transport library for Rust.
asx-rs implements the AS2 (RFC 4130) and AS4 (OASIS ebMS3 + eDelivery) protocols — the wire formats used by PEPPOL, CEF eDelivery, and tens of thousands of EDI trading partner connections worldwide.
⚠️ Alpha quality. Core AS2 send/receive and AS4 push/pull are working and tested. See Status for known gaps before using in production.
✨ Features
📨 AS2 (RFC 4130 / RFC 5751)
- Send and receive signed payloads (CMS/S/MIME, RSA-SHA256)
- Synchronous and asynchronous MDN (Message Disposition Notification)
- MIC computation for end-to-end integrity verification
- Payload compression (RFC 5402 / zlib, enabled by default via
compression) - Configurable interop mode: strict vs. relaxed for legacy partners
- Retry classification (
SuccessConfirmed/Indeterminate/AcceptedPendingVerification)
📬 AS4 (ebMS3 + OASIS eDelivery)
- One-Way/Push send and receive
- One-Way/Pull with bounded in-memory pull store per MPC partition
- WS-Security XMLDSig signing (RSA-SHA256, Exc-C14N) and verification
- XML encryption / decryption (XMLenc11 AES-GCM + RSA-OAEP)
- Streaming receive path with bounded memory
- P-Mode registry for per-partner MEP/security configuration
- SBDH 1.3 envelope wrap/unwrap for PEPPOL and CEF eDelivery
🔒 Security & Reliability
- Type-state lifecycle machine — compiler enforces the full trust chain:
UntrustedBytes → StructurallyParsed → CryptographicallyVerified → ContentDecrypted → DomainReady. Payload bytes cannot reach application code without every gate passing. - OCSP stapling + responder-based revocation checking (
async-ocspfeature) - PKIX certificate chain validation (fail-closed on empty trust store)
- Dedup storage (
InMemoryDedupStorage,TtlDedupStorage) prevents replay - Reconciliation hooks for async delivery confirmation
🌐 HTTP Transport
- Axum 0.7 server integration (
serverfeature) — drop-inRouterfor AS2 and AS4 ingress - Async HTTP egress via
reqwest(clientfeature) - Inbound endpoint governance (
HttpEndpointPolicy) against unexpected sources
🔍 Observability
EventBuswith fan-out broadcast and ordered mpsc audit channelDurableAuditSinktrait for pluggable audit backends- Configurable back-pressure policy (
BackpressurePolicy) EventBusMetricswith lock-freeAtomicU64counters- Optional built-in Prometheus/OpenMetrics text sink (
prometheusfeature)
🧩 Interop
- Profile stacking with regional packs and per-partner overlays
interop-strict(default) andinterop-relaxedfeature-gated modes- Exception policies (
InteropExceptionPolicy) for well-known deviations - Interop matrix executor (
testingfeature) — built-in fixture-based conformance runner
🚀 Quick Start
Add to Cargo.toml:
[]
# AS2 client + server with OCSP
= { = "0.3", = ["as2", "client", "server", "async-ocsp"] }
# AS4 only
= { = "0.3", = ["as4", "client", "server", "async-ocsp"] }
# Both protocols with compression (default)
= { = "0.3", = ["as2", "as4", "compression", "client", "server", "async-ocsp"] }
as2andas4are not enabled by default — add them explicitly.
📖 Examples
AS2 — Send a signed message
use ;
use SessionContext;
use ;
let policy = As2SendPolicy ;
let creds = As2SendCredentials ;
let session = new?;
let bus = new_with_config_and_mode?;
let output = send_sync?;
// output.mime.body — body bytes to POST to partner's AS2 URL
// output.mime.content_type — HTTP Content-Type header value
// output.http_headers — required AS2 HTTP headers (AS2-From, AS2-To, etc.)
AS2 — Receive and verify
use ;
use SessionContext;
let session = new?;
let verifier = CmsSmimeTrustVerifier;
let trusted = receive_sync?;
println!;
AS4 — Send a push message
use ;
use SessionContext;
use ;
let = new
.signing_cert_pem
.signing_key_pem
.build?;
let session = new?;
let bus = new_with_config_and_mode?;
let output = send_sync?;
// output.soap_envelope.body -> multipart/related bytes ready to POST
AS4 — Axum server (ingress)
use Arc;
use Router;
use async_trait;
use ;
use As4HttpIngress;
;
async
Strict production startup validation
use Arc;
use ;
use ;
use ;
In non-testing builds, strict interop protocol entry points fail closed unless
startup validation is bound to the session by explicitly applying
asx_rs::presets::session_with_strict_runtime_bootstrap_token(...).
For AS2 HTTP server flows, bind a strict session once with
session_with_strict_runtime_bootstrap_token(...) and then call
As2HttpIngress::receive_and_generate_mdn(...) or
As2HttpIngress::receive_and_generate_mdn_with_signing(...).
For AS4 HTTP server flows, bind a strict session once with
session_with_strict_runtime_bootstrap_token(...) and then call
As4HttpIngress::receive_push_with_dedup_sync(...) with an optional receipt payload.
SBDH — PEPPOL / CEF eDelivery envelope
use ;
let doc = StandardBusinessDocument ;
let wrapped = doc.wrap?;
// wrapped → send via AS4 push to PEPPOL access point
🎛️ Feature Flags
| Flag | Enables | Default |
|---|---|---|
as2 |
AS2 send/receive free functions (as2::send_sync, as2::receive_sync) |
❌ |
as4 |
AS4 send/receive free functions (as4::send_sync, as4::receive_push_with_dedup_sync) and As4PullStore |
❌ |
client |
HTTP egress via reqwest (As2HttpTransport, As4HttpTransport) |
❌ |
server |
Axum 0.7 router integration (as2_router, as4_router) |
❌ |
compression |
Zlib/GZIP compression via flate2 |
✅ |
async-ocsp |
Async OCSP responder fetching via reqwest |
✅ |
interop-strict |
Strict interop mode as default | ✅ |
interop-relaxed |
Relaxed mode helpers for legacy partners | ❌ |
trace |
tracing instrumentation stubs |
❌ |
prometheus |
Built-in PrometheusMetricsSink adapter for MetricsSink |
❌ |
postgres-storage |
PostgreSQL-backed durable, cluster-safe dedup/reconciliation storage | ❌ |
testing |
Exposes fixture catalog and matrix executor | ❌ |
🏗️ Architecture
asx-rs
├── as2/ AS2 send, receive, MDN handling
├── as4/ AS4 push/pull, P-Mode registry, pull store
│ ├── pmode.rs P-Mode registry + resolution
│ ├── parser.rs ebMS3 UserMessage XML parser
│ └── pull_store Bounded in-memory pull queue
├── crypto/
│ ├── as2_smime CMS/S/MIME signing + verification
│ ├── wssec WS-Security (XMLDSig, XMLenc, OCSP, Exc-C14N)
│ └── soap_builder SOAP envelope construction
├── transport/
│ ├── ingress HTTP request normalisation
│ ├── egress HTTP send with endpoint governance
│ └── server Axum router builders (server feature)
├── lifecycle Type-state trust transition machine
├── reliability Retry classification, dedup, reconciliation
├── storage/ DedupStorage + ReconciliationStorage traits + in-memory impls
├── observability/ EventBus, audit sink, back-pressure policy
├── interop Profile stacks, regional packs, exception policies
├── sbdh UN/CEFACT SBDH 1.3 wrap/unwrap
├── wire Bounded stream reading, MIME utilities
└── core Error types, SessionContext, shared utilities
🔐 Trust lifecycle
Every inbound byte travels a compiler-enforced path before reaching your application:
UntrustedBytes
│ structural parse (MIME / SOAP envelope)
▼
StructurallyParsed
│ cryptographic verify (S/MIME or XMLDSig)
▼
CryptographicallyVerified
│ decrypt (S/MIME EnvelopedData or XMLenc)
▼
ContentDecrypted
│ dedup check + domain validation
▼
DomainReady ← your application code starts here
🔒 Security Notes
InsecureBypassTrustVerifierskips all cryptographic verification. It is intended exclusively for testing. Never use it in production.- PKIX chain validation is fail-closed: an empty
trust_anchor_pemsstore rejects every certificate. - OCSP checking is opt-in via
OcspModeinCertHandle. The default isOcspMode::Disabled— setOcspMode::ResponderOnlyorOcspMode::StapledAndResponderin production. - Outbound HTTP egress validates URL scheme and blocks private/loopback/link-local targets (including DNS-rebinding to private addresses).
📊 Status
asx-rs is alpha quality. Core AS2 send/receive and AS4 push/pull flows are implemented and tested, but the crate is not yet production-hardened.
Current constraints to evaluate before production rollout:
- Core send/receive entry points are synchronous, but async-safe wrappers are now available (
as2::send_async,as2::receive_async,as4::send_async,as4::receive_push_with_dedup_async) to isolate blocking work on Tokio blocking threads. - Production persistence adapters (Redis/PostgreSQL/DynamoDB) are trait-based and not yet shipped in-tree; deployers must provide backend implementations.
📜 License
Licensed under either of:
at your option.