0.5.1: axum-only apub_federation, rustls ring-only, install ring provider
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.5.1] — 2026-10-26
|
||||||
|
|
||||||
|
- build: axum-only activitypub_federation, rustls-ring only — drops actix-web and aws-lc from consumers' builds
|
||||||
|
|
||||||
## [0.5.0] — 2026-07-25
|
## [0.5.0] — 2026-07-25
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|||||||
669
Cargo.lock
generated
669
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "k-ap"
|
name = "k-ap"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Generic ActivityPub protocol layer"
|
description = "Generic ActivityPub protocol layer"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -18,9 +18,10 @@ thiserror = "2"
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
axum = { version = "0.8", features = ["macros"] }
|
axum = { version = "0.8", features = ["macros"] }
|
||||||
reqwest = { version = "0.13", features = ["json"] }
|
reqwest = { version = "0.13", default-features = false, features = ["json", "charset", "http2", "system-proxy", "rustls-no-provider"] }
|
||||||
|
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12", "logging"] }
|
||||||
url = { version = "2", features = ["serde"] }
|
url = { version = "2", features = ["serde"] }
|
||||||
enum_delegate = "0.2"
|
enum_delegate = "0.2"
|
||||||
activitypub_federation = "0.7.0-beta.11"
|
activitypub_federation = { version = "0.7.0-beta.11", default-features = false, features = ["axum"] }
|
||||||
zeroize = { version = "1", features = ["derive"] }
|
zeroize = { version = "1", features = ["derive"] }
|
||||||
paste = "1"
|
paste = "1"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ impl ApFederationConfig {
|
|||||||
debug: bool,
|
debug: bool,
|
||||||
signing_actor: Option<&DbActor>,
|
signing_actor: Option<&DbActor>,
|
||||||
) -> anyhow::Result<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let config = if debug {
|
let config = if debug {
|
||||||
FederationConfig::builder()
|
FederationConfig::builder()
|
||||||
.domain(&data.domain)
|
.domain(&data.domain)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod service;
|
|||||||
/// Mock builders for testing. Not behind `#[cfg(test)]` so downstream crates
|
/// Mock builders for testing. Not behind `#[cfg(test)]` so downstream crates
|
||||||
/// can use them in their own test suites.
|
/// can use them in their own test suites.
|
||||||
pub mod testing;
|
pub mod testing;
|
||||||
|
pub(crate) mod tls;
|
||||||
pub(crate) mod url_scheme;
|
pub(crate) mod url_scheme;
|
||||||
pub(crate) mod urls;
|
pub(crate) mod urls;
|
||||||
pub(crate) mod user;
|
pub(crate) mod user;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ impl ActivityPubService {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let outbox_parsed = url::Url::parse(outbox_url)?;
|
let outbox_parsed = url::Url::parse(outbox_url)?;
|
||||||
crate::security::validate_url(&outbox_parsed).await?;
|
crate::security::validate_url(&outbox_parsed).await?;
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let client = reqwest::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
.timeout(std::time::Duration::from_secs(
|
.timeout(std::time::Duration::from_secs(
|
||||||
super::HTTP_FETCH_TIMEOUT_SECS,
|
super::HTTP_FETCH_TIMEOUT_SECS,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ fn webfinger_url(handle: &ParsedHandle<'_>) -> String {
|
|||||||
async fn fetch_webfinger(url: &str) -> anyhow::Result<serde_json::Value> {
|
async fn fetch_webfinger(url: &str) -> anyhow::Result<serde_json::Value> {
|
||||||
let parsed = Url::parse(url)?;
|
let parsed = Url::parse(url)?;
|
||||||
crate::security::validate_url(&parsed).await?;
|
crate::security::validate_url(&parsed).await?;
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
Ok(reqwest::Client::new()
|
Ok(reqwest::Client::new()
|
||||||
.get(url)
|
.get(url)
|
||||||
.header("Accept", "application/jrd+json, application/json")
|
.header("Accept", "application/jrd+json, application/json")
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ async fn setup_with_blocklist(
|
|||||||
Arc::new(crate::url_scheme::DefaultUrlScheme),
|
Arc::new(crate::url_scheme::DefaultUrlScheme),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let config = FederationConfig::builder()
|
let config = FederationConfig::builder()
|
||||||
.domain(LOCAL_DOMAIN)
|
.domain(LOCAL_DOMAIN)
|
||||||
.app_data(data)
|
.app_data(data)
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ async fn check_guards_idempotency() {
|
|||||||
build_user_repo(uuid::Uuid::new_v4(), "alice"),
|
build_user_repo(uuid::Uuid::new_v4(), "alice"),
|
||||||
MockObjectHandlerBuilder::new().build(),
|
MockObjectHandlerBuilder::new().build(),
|
||||||
);
|
);
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let config = FederationConfig::builder()
|
let config = FederationConfig::builder()
|
||||||
.domain("example.com")
|
.domain("example.com")
|
||||||
.app_data(data_inner)
|
.app_data(data_inner)
|
||||||
@@ -139,6 +140,7 @@ async fn check_guards_blocks_domain() {
|
|||||||
build_user_repo(uuid::Uuid::new_v4(), "alice"),
|
build_user_repo(uuid::Uuid::new_v4(), "alice"),
|
||||||
MockObjectHandlerBuilder::new().build(),
|
MockObjectHandlerBuilder::new().build(),
|
||||||
);
|
);
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let config = FederationConfig::builder()
|
let config = FederationConfig::builder()
|
||||||
.domain("example.com")
|
.domain("example.com")
|
||||||
.app_data(data_inner)
|
.app_data(data_inner)
|
||||||
@@ -170,6 +172,7 @@ async fn extract_and_dispatch_mentions_notifies_local_users() {
|
|||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
let data_inner = make_data(None, build_user_repo(local_user_id, "alice"), handler);
|
let data_inner = make_data(None, build_user_repo(local_user_id, "alice"), handler);
|
||||||
|
crate::tls::ensure_crypto_provider();
|
||||||
let config = FederationConfig::builder()
|
let config = FederationConfig::builder()
|
||||||
.domain("example.com")
|
.domain("example.com")
|
||||||
.app_data(data_inner)
|
.app_data(data_inner)
|
||||||
|
|||||||
30
src/tls.rs
Normal file
30
src/tls.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//! rustls crypto provider setup.
|
||||||
|
//!
|
||||||
|
//! k-ap compiles rustls with the `ring` provider only (no aws-lc). reqwest 0.13
|
||||||
|
//! does not auto-select a provider in that configuration and panics
|
||||||
|
//! ("No provider set") on client construction, so we install ring as the
|
||||||
|
//! process default before any HTTP client is built. Idempotent; a provider
|
||||||
|
//! already installed by the host application wins.
|
||||||
|
|
||||||
|
pub(crate) fn ensure_crypto_provider() {
|
||||||
|
static ONCE: std::sync::Once = std::sync::Once::new();
|
||||||
|
ONCE.call_once(|| {
|
||||||
|
// Err means the application already installed a provider; keep theirs.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn installs_provider_and_client_builds() {
|
||||||
|
ensure_crypto_provider();
|
||||||
|
ensure_crypto_provider();
|
||||||
|
assert!(rustls::crypto::CryptoProvider::get_default().is_some());
|
||||||
|
reqwest::Client::builder()
|
||||||
|
.build()
|
||||||
|
.expect("reqwest client builds with ring-only rustls");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user