fix: NodeInfo 2.0 schema compliance, bump 0.4.5
Remove invalid $schema, add required services/metadata fields. Configurable via with_nodeinfo_services/with_nodeinfo_metadata. Closes GKaszewski/movies-diary#2
This commit is contained in:
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.4.5] — 2026-07-16
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Remove invalid `$schema` field from NodeInfo 2.0 response — the field is not allowed by the NodeInfo 2.0 schema (`additionalProperties: false`)
|
||||||
|
- Add missing required `services` and `metadata` fields to NodeInfo 2.0 response
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- `FederationData::with_nodeinfo_services(inbound, outbound)` — configure NodeInfo `services.inbound`/`services.outbound`
|
||||||
|
- `FederationData::with_nodeinfo_metadata(value)` — set arbitrary NodeInfo `metadata`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.4.4] — 2026-07-11
|
## [0.4.4] — 2026-07-11
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1368,7 +1368,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "k-ap"
|
name = "k-ap"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "k-ap"
|
name = "k-ap"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Generic ActivityPub protocol layer"
|
description = "Generic ActivityPub protocol layer"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
17
src/data.rs
17
src/data.rs
@@ -71,6 +71,9 @@ pub struct FederationData {
|
|||||||
pub(crate) software_name: String,
|
pub(crate) software_name: String,
|
||||||
pub(crate) event_publisher: Option<Arc<dyn EventPublisher>>,
|
pub(crate) event_publisher: Option<Arc<dyn EventPublisher>>,
|
||||||
pub(crate) actor_cache_ttl: std::time::Duration,
|
pub(crate) actor_cache_ttl: std::time::Duration,
|
||||||
|
pub(crate) nodeinfo_services_inbound: Vec<String>,
|
||||||
|
pub(crate) nodeinfo_services_outbound: Vec<String>,
|
||||||
|
pub(crate) nodeinfo_metadata: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FederationData {
|
impl FederationData {
|
||||||
@@ -110,6 +113,20 @@ impl FederationData {
|
|||||||
software_name,
|
software_name,
|
||||||
event_publisher,
|
event_publisher,
|
||||||
actor_cache_ttl,
|
actor_cache_ttl,
|
||||||
|
nodeinfo_services_inbound: vec![],
|
||||||
|
nodeinfo_services_outbound: vec![],
|
||||||
|
nodeinfo_metadata: serde_json::json!({}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_nodeinfo_services(mut self, inbound: Vec<String>, outbound: Vec<String>) -> Self {
|
||||||
|
self.nodeinfo_services_inbound = inbound;
|
||||||
|
self.nodeinfo_services_outbound = outbound;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_nodeinfo_metadata(mut self, metadata: serde_json::Value) -> Self {
|
||||||
|
self.nodeinfo_metadata = metadata;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use crate::data::FederationData;
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
const NODEINFO_2_0_REL: &str = "http://nodeinfo.diaspora.software/ns/schema/2.0";
|
const NODEINFO_2_0_REL: &str = "http://nodeinfo.diaspora.software/ns/schema/2.0";
|
||||||
const NODEINFO_2_0_SCHEMA: &str = "http://nodeinfo.diaspora.software/ns/schema/2.0#";
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct NodeInfoWellKnown {
|
pub struct NodeInfoWellKnown {
|
||||||
@@ -37,16 +36,22 @@ pub struct NodeInfoUsers {
|
|||||||
pub total: usize,
|
pub total: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct NodeInfoServices {
|
||||||
|
pub inbound: Vec<String>,
|
||||||
|
pub outbound: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
#[serde(rename = "$schema")]
|
|
||||||
pub schema: String,
|
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub software: NodeInfoSoftware,
|
pub software: NodeInfoSoftware,
|
||||||
pub protocols: Vec<String>,
|
pub protocols: Vec<String>,
|
||||||
pub usage: NodeInfoUsage,
|
pub services: NodeInfoServices,
|
||||||
pub open_registrations: bool,
|
pub open_registrations: bool,
|
||||||
|
pub usage: NodeInfoUsage,
|
||||||
|
pub metadata: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn nodeinfo_well_known_handler(
|
pub async fn nodeinfo_well_known_handler(
|
||||||
@@ -66,18 +71,22 @@ pub async fn nodeinfo_handler(data: Data<FederationData>) -> Result<Json<NodeInf
|
|||||||
let local_posts = data.content_reader.count_local_posts().await.unwrap_or(0);
|
let local_posts = data.content_reader.count_local_posts().await.unwrap_or(0);
|
||||||
|
|
||||||
Ok(Json(NodeInfo {
|
Ok(Json(NodeInfo {
|
||||||
schema: NODEINFO_2_0_SCHEMA.to_string(),
|
|
||||||
version: "2.0".to_string(),
|
version: "2.0".to_string(),
|
||||||
software: NodeInfoSoftware {
|
software: NodeInfoSoftware {
|
||||||
name: data.software_name.clone(),
|
name: data.software_name.clone(),
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
},
|
},
|
||||||
protocols: vec!["activitypub".to_string()],
|
protocols: vec!["activitypub".to_string()],
|
||||||
|
services: NodeInfoServices {
|
||||||
|
inbound: data.nodeinfo_services_inbound.clone(),
|
||||||
|
outbound: data.nodeinfo_services_outbound.clone(),
|
||||||
|
},
|
||||||
|
open_registrations: data.allow_registration,
|
||||||
usage: NodeInfoUsage {
|
usage: NodeInfoUsage {
|
||||||
users: NodeInfoUsers { total: user_count },
|
users: NodeInfoUsers { total: user_count },
|
||||||
local_posts,
|
local_posts,
|
||||||
},
|
},
|
||||||
open_registrations: data.allow_registration,
|
metadata: data.nodeinfo_metadata.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,27 +19,31 @@ fn nodeinfo_well_known_serializes_correctly() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn nodeinfo_serializes_camel_case() {
|
fn nodeinfo_serializes_camel_case() {
|
||||||
let doc = NodeInfo {
|
let doc = NodeInfo {
|
||||||
schema: "http://nodeinfo.diaspora.software/ns/schema/2.0#".to_string(),
|
|
||||||
version: "2.0".to_string(),
|
version: "2.0".to_string(),
|
||||||
software: NodeInfoSoftware {
|
software: NodeInfoSoftware {
|
||||||
name: "my-app".to_string(),
|
name: "my-app".to_string(),
|
||||||
version: "0.1.0".to_string(),
|
version: "0.1.0".to_string(),
|
||||||
},
|
},
|
||||||
protocols: vec!["activitypub".to_string()],
|
protocols: vec!["activitypub".to_string()],
|
||||||
|
services: NodeInfoServices {
|
||||||
|
inbound: vec![],
|
||||||
|
outbound: vec![],
|
||||||
|
},
|
||||||
|
open_registrations: false,
|
||||||
usage: NodeInfoUsage {
|
usage: NodeInfoUsage {
|
||||||
users: NodeInfoUsers { total: 3 },
|
users: NodeInfoUsers { total: 3 },
|
||||||
local_posts: 42,
|
local_posts: 42,
|
||||||
},
|
},
|
||||||
open_registrations: false,
|
metadata: serde_json::json!({}),
|
||||||
};
|
};
|
||||||
let json = serde_json::to_value(&doc).unwrap();
|
let json = serde_json::to_value(&doc).unwrap();
|
||||||
assert_eq!(
|
assert!(json.get("$schema").is_none());
|
||||||
json["$schema"],
|
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.0#"
|
|
||||||
);
|
|
||||||
assert_eq!(json["version"], "2.0");
|
assert_eq!(json["version"], "2.0");
|
||||||
assert_eq!(json["software"]["name"], "my-app");
|
assert_eq!(json["software"]["name"], "my-app");
|
||||||
assert_eq!(json["usage"]["users"]["total"], 3);
|
assert_eq!(json["usage"]["users"]["total"], 3);
|
||||||
assert_eq!(json["usage"]["localPosts"], 42);
|
assert_eq!(json["usage"]["localPosts"], 42);
|
||||||
assert_eq!(json["openRegistrations"], false);
|
assert_eq!(json["openRegistrations"], false);
|
||||||
|
assert_eq!(json["services"]["inbound"], serde_json::json!([]));
|
||||||
|
assert_eq!(json["services"]["outbound"], serde_json::json!([]));
|
||||||
|
assert_eq!(json["metadata"], serde_json::json!({}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user