asx-rs 0.14.0

AS2 and AS4 B2B messaging library for Rust โ€” signing, encryption, MDN, and ebMS3/AS4 profile support
Documentation
# asx-rs

**AS2 and AS4 B2B messaging for Rust โ€” async-native, memory-safe, and fail-closed by default.**

[![Crates.io](https://img.shields.io/crates/v/asx-rs.svg)](https://crates.io/crates/asx-rs)
[![Docs.rs](https://docs.rs/asx-rs/badge.svg)](https://docs.rs/asx-rs)
[![License](https://img.shields.io/crates/l/asx-rs.svg)](#license)

`asx-rs` implements [AS2 (RFC 4130)](https://www.rfc-editor.org/rfc/rfc4130) and
[AS4 (OASIS ebMS3 / eDelivery)](https://docs.oasis-open.org/ebxml-msg/ebms/v3.0/profiles/AS4-profile/v1.0/)
โ€” 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](https://hupe1980.github.io/asx-rs)** ยท
๐Ÿฆ€ **[API reference](https://docs.rs/asx-rs)** ยท
๐Ÿ“‹ **[Changelog](CHANGELOG.md)**

---

## Install

```bash
cargo add asx-rs --features as4,client,server
```

`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 `testing` feature raises a `compile_error!` in release builds. It cannot
> reach a production binary by accident.

## Example

Send a signed AS4 message and verify the counterparty's receipt:

```rust
use asx_rs::as4::{send_async, As4ReceiptPolicy, As4SendPolicyBuilder, As4SendRequest};
use asx_rs::transport::egress::{As4HttpTransport, TransportConfig};

let (policy, credentials) = As4SendPolicyBuilder::new()
    .action("urn:example:action")
    .service("urn:example:service", "")
    .signing_cert_pem(cert_pem)
    .signing_key_pem(key_pem)
    .build()?;

let sent = send_async(&session, &bus, As4SendRequest {
    message_id: message_id.clone(),
    payload,
    policy,
    credentials: Some(credentials),
    payload_filename: None,
}).await?;

let outcome = As4HttpTransport::new(TransportConfig::default())?
    .send_and_verify(&url, &session, &bus, &sent, &As4ReceiptPolicy::regulated())
    .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](https://hupe1980.github.io/asx-rs/docs/getting-started/)**.

## 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](https://hupe1980.github.io/asx-rs/docs/security/)** 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:PartInfo` attachment 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]https://hupe1980.github.io/asx-rs/docs/conformance/.
- **No storage backends ship in-tree.** `DedupStorage`, `ReconciliationStorage`
  and `DurableAuditSink` are trait-defined; you supply the implementation โ€”
  and `asx_rs::storage::conformance` is the suite that proves yours is durable
  and its `first_seen` atomic, rather than taking your word for it.

## Development

```bash
just ci             # fast pre-push subset: lint, check, test, feature matrix
just release-gate   # every mandatory gate (slow)
just --list         # all recipes
```

Recipes mirror [`.github/workflows/ci.yml`](.github/workflows/ci.yml) and
[`RELEASING.md`](RELEASING.md); when a gate changes in one, change it in the
other.

The documentation site is built with [Zola](https://www.getzola.org):

```bash
zola --root site serve   # http://127.0.0.1:1111
zola --root site build   # also fails on broken links and dangling anchors
```

## Versioning

Pre-1.0, breaking changes ship in minor versions. The
[changelog](CHANGELOG.md) 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](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option.