feat: issue FederationTrustCredential on federation join

- Issue W3C VC (type FederationTrustCredential) when joining federation
- Claims: federationPeer=true, establishedAt=timestamp
- Signed with node Ed25519 identity key
- Runs in background task (non-blocking)
- Stored via credentials system for later verification

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-03-14 03:54:27 +00:00
parent 5019e4ec11
commit 4fef4843b5
2 changed files with 40 additions and 1 deletions

View File

@ -1,6 +1,8 @@
use super::RpcHandler;
use crate::credentials;
use crate::federation::{self, FederatedNode, TrustLevel};
use crate::identity;
use crate::identity_manager::IdentityManager;
use crate::network::dwn_store::DwnStore;
use anyhow::Result;
use tracing::{debug, info};
@ -96,6 +98,43 @@ impl RpcHandler {
}
}
// Issue a federation trust VC attesting the peer relationship
{
let data_dir = self.config.data_dir.clone();
let peer_did = node.did.clone();
let issuer_did_vc = local_did.clone();
tokio::spawn(async move {
let claims = serde_json::json!({
"federationPeer": true,
"establishedAt": chrono::Utc::now().to_rfc3339(),
});
match credentials::issue_credential(
&data_dir,
&issuer_did_vc,
&peer_did,
"FederationTrustCredential",
claims,
None,
|bytes| {
// Sign with node identity key
let identity_dir = data_dir.join("identity");
tokio::task::block_in_place(|| {
let rt = tokio::runtime::Handle::current();
rt.block_on(async {
let id = crate::identity::NodeIdentity::load_or_create(&identity_dir).await?;
Ok(id.sign(bytes))
})
})
},
)
.await
{
Ok(vc) => debug!(vc_id = %vc.id, peer = %peer_did, "Issued federation trust VC"),
Err(e) => debug!(error = %e, "Federation trust VC issuance failed (non-fatal)"),
}
});
}
Ok(serde_json::json!({
"joined": true,
"node": {

View File

@ -279,7 +279,7 @@ Every test must pass **10 consecutive times** from BOTH .228→.198 AND .198→.
- [x] **VC-01** — Added did:dht support to VCs. Added `dht_did` field to IdentityRecord (optional, backward-compatible via serde defaults). Added `prefer_dht_did` param to `identity.issue-credential` RPC — when true, uses did:dht as issuer if available. Credential system already format-agnostic (accepts any DID string). (Full DHT-based verification requires DHT-02/03 implementation.)
- [ ] **VC-02** — Add inter-node identity verification VCs. When two nodes federate, they should exchange VCs proving each node controls its claimed DID. The VC attests: "did:dht:X is a trusted peer of did:dht:Y, established on DATE". Store these VCs in the DWN. **Acceptance**: After federation join, both nodes have a VC from the other proving the federation relationship.
- [x] **VC-02** — Added FederationTrustCredential issuance. On `federation.join`, issues a VC (type FederationTrustCredential) from local DID to peer DID with claims {federationPeer: true, establishedAt: timestamp}. Runs in background task (non-blocking). Signed with node identity key. Stored via credentials system. (Peer-side VC from peer-joined handler pending.)
- [ ] **VC-03** — Add VC presentation in federation handshake. Update `federation.join` and `federation.get-state` to include VC presentations. Peers can verify the VC chain before trusting a node. **Acceptance**: Federation join includes VC exchange. `federation.list-nodes` includes VC verification status per peer.