Skip to main content
For backends that need minimal allocations + RFC-8032 strict-mode verification. Full source (verbatim): docs/examples/webhook-receivers/rust-axum/src/main.rs

Cargo.toml

src/main.rs

Run

Behind a TLS-terminating reverse proxy (Caddy, nginx, Cloudflare). Or use axum-server with rustls for direct TLS.

Why verify_strict

verify_strict enforces RFC-8032 strict-mode rules:
  • Rejects malleable signatures (signatures whose s value is not in canonical form)
  • Rejects public keys with point-at-infinity edge cases
Pegana signs with libsodium-style strict semantics, so strict verification round-trips correctly. If you used the non-strict verify you’d accept some technically-valid Ed25519 signatures that the original spec rejects — a defense-in-depth issue, not a correctness one for our payloads.

Body extraction order

axum’s Bytes extractor reads the raw body before any deserialization. Do not put a Json<T> extractor before Bytes — that would consume the body and you’d sign over an empty input. If you need typed access to the body, parse after verification:

Idempotency

The reference receiver above does NOT enforce idempotency. For production, persist alert_id in your DB or Redis with a 24h TTL:

Deploy

For a hobby endpoint:
For production:
  • Run as a systemd unit
  • Set PEGANA_PUB_KEY_B64 from a secret store
  • Front with TLS + rate-limiting reverse proxy
  • Ship tracing to your log aggregator

Why ed25519-dalek

  • Pure Rust, no system OpenSSL dependency
  • Audited
  • Constant-time by construction
  • Available in no_std mode for embedded use
For FIPS-required environments, swap to ring — same algorithm, different crate.

Next

TypeScript example

Web Crypto API for edge runtimes.

Python example

FastAPI + cryptography.