Mudrit
Reference

Batch & Async

sign_batch, BatchOptions, sign_pdf_async, and sign_batch_async — parallel batch signing and Tokio wrappers, with the Send/Sync bounds each needs

Part of mudrit-pdfsign

Two ways to scale beyond one call to sign_pdf: parallel batch signing across a thread pool (sign_batch), and async wrappers over Tokio's blocking pool (sign_pdf_async / sign_batch_async, feature tokio). Both share the same underlying sign_pdf — signing itself is always synchronous, CPU-bound work.

sign_batch

pub fn sign_batch<S>(
    inputs: &[Vec<u8>],
    signer: &S,
    cfg: &SignConfig,
    opts: &BatchOptions,
) -> Vec<Result<Vec<u8>>>
where
    S: Signer + Sync

Signs every PDF in inputs with one signer + cfg, in parallel, across opts.threads worker threads (work-stealing), returning a Result per input in the same order. A failing document yields an Err in its slot — the batch never aborts on one error.

use mudrit_keystore::PfxSigner;
use mudrit_pdfsign::{sign_batch, BatchOptions, SignConfig};

let signer = PfxSigner::from_file("samples/ABC12.pfx", "ABC12")?;
let cfg = SignConfig::builder().place("F", [350, 60, 560, 160])?.build();
let inputs: Vec<Vec<u8>> = vec![std::fs::read("a.pdf")?, std::fs::read("b.pdf")?];

let results = sign_batch(&inputs, &signer, &cfg, &BatchOptions::default());
for (i, r) in results.iter().enumerate() {
    match r { Ok(pdf) => println!("#{i}: {} bytes", pdf.len()), Err(e) => eprintln!("#{i}: {e}") }
}
# Ok::<(), Box<dyn std::error::Error>>(())

Signer must be Sync — and hardware tokens need threads(1)

signer is shared across worker threads, so it must be Sync (e.g. &PfxSigner). For a type-erased signer, &signer as &(dyn Signer + Sync) also satisfies the bound. Parallel signing is safe for software signers (RSA/ECDSA signing is reentrant), but a single hardware token has one PKCS#11 session that is not safe for concurrent signing — for one token use BatchOptions::threads(1) (the work becomes sequential but still returns per-item results). With several tokens, sign each token's batch on its own.

BatchOptions

Debug + Clone.

Prop

Type

Prop

Type

sign_pdf_async (feature tokio)

pub async fn sign_pdf_async<S>(input: Vec<u8>, signer: Arc<S>, cfg: SignConfig) -> Result<Vec<u8>>
where
    S: Signer + Send + Sync + 'static

Runs the blocking sign_pdf on Tokio's blocking thread-pool (spawn_blocking) so it never stalls an async runtime — the honest, correct shape for an async server (axum / actix / …). The signer is Arc<S> because the work runs on another thread, so S must be Send + Sync + 'static; for a type-erased signer use Arc<dyn Signer + Send + Sync>.

use std::sync::Arc;
use mudrit_keystore::PfxSigner;
use mudrit_pdfsign::{sign_pdf_async, SignConfig};

# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let signer = Arc::new(PfxSigner::from_file("samples/ABC12.pfx", "ABC12")?);
let cfg = SignConfig::builder().place("F", [350, 60, 560, 160])?.build();
let signed = sign_pdf_async(std::fs::read("in.pdf")?, signer, cfg).await?;
# Ok(()) }

sign_batch_async (feature tokio)

pub async fn sign_batch_async<S>(
    inputs: Vec<Vec<u8>>,
    signer: Arc<S>,
    cfg: SignConfig,
    opts: BatchOptions,
) -> Result<Vec<Result<Vec<u8>>>>
where
    S: Signer + Send + Sync + 'static

Runs the whole parallel sign_batch on the blocking pool and resolves to the per-input results. Same Send + Sync + 'static bound on S as sign_pdf_async, for the same reason — the work is offloaded to another thread.

Enable the feature

sign_pdf_async / sign_batch_async are only compiled with the tokio feature (mudrit-pdfsign = { version = "…", features = ["tokio"] }, off by default). sign_batch needs no feature — it uses std::thread directly.

Next

On this page