Something went wrong. Try again.
Chris’s PDS implementation
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173//! `cpds` is a deliberately small Personal Data Server.//!//! The crate is split at the seams where a real deployment usually changes://! persistence, blob storage, OAuth clients, and event delivery. The protocol//! services depend on those seams; the HTTP layer only translates requests.
pub mod backup;pub mod blob_store;pub mod clock;pub mod config;pub mod database;pub mod domain;pub mod error;pub mod events;pub mod http;mod identity;pub mod oauth;mod private_fs;mod rate_limits;pub mod relay;pub mod repository;mod space;pub mod syntax;
use std::sync::Arc;
use blob_store::BlobStore;use config::Config;use database::Database;use domain::identity::{local_did_document, validate_local_did_document};use error::Result;use events::EventSink;use identity::IdentityService;use oauth::{OAuthService, StaticClientRegistry};use rate_limits::{PrincipalRateLimits, SubscriptionLimits};use repository::RepositoryService;use space::SpaceService;use space::auth::SpaceAuthService;use tokio::sync::watch;
/// All long-lived application services.////// Keeping this type boring is useful: tests can assemble the exact same/// application as `main`, replacing only infrastructure adapters.#[derive(Clone)]pub struct AppState { pub config: Arc<Config>, pub repository: Arc<RepositoryService>, pub oauth: Arc<OAuthService>, pub(crate) identity: Arc<IdentityService>, rate_limits: Arc<PrincipalRateLimits>, subscription_limits: Arc<SubscriptionLimits>, stream_shutdown: watch::Sender<bool>, spaces: Arc<SpaceService>, space_auth: Arc<SpaceAuthService>,}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub(crate) enum IdentityRevalidation { Unchanged, Invalidated, Recovered,}
impl AppState { /// Compose the application's protocol services from infrastructure adapters. /// /// # Errors /// /// Returns an error when the bootstrap account cannot be read or created. pub fn assemble( config: Config, database: Database, blobs: Arc<dyn BlobStore>, events: Arc<dyn EventSink>, ) -> Result<Self> { database.ensure_account( &config.account.did, &config.account.handle, &config.account.password, )?;
let clients = StaticClientRegistry::new(config.oauth_clients.clone()); let repository = RepositoryService::new( database.clone(), blobs, events, &config.account.signing_key, config.repo_event_retention, )?; let spaces = SpaceService::new( database.clone(), config.account.did.clone(), &config.account.signing_key, )?; let public_key = repository.public_key_multibase(); let document = local_did_document( &config.account.did, &config.account.handle, &config.public_origin.to_string(), public_key, ); validate_local_did_document( &document, &config.account.did, &config.account.handle, &config.public_origin.to_string(), public_key, )?; let identity = Arc::new(IdentityService::new(document, config.deployment_mode)?); let space_auth = SpaceAuthService::new( database.clone(), config.account.did.clone(), &config.account.signing_key, config.public_origin.as_url().clone(), identity.clone(), )?; let oauth = OAuthService::new( database, Arc::new(clients), config.public_origin.as_url().clone(), );
Ok(Self { config: Arc::new(config), repository: Arc::new(repository), oauth: Arc::new(oauth), identity, rate_limits: Arc::new(PrincipalRateLimits::new()), subscription_limits: SubscriptionLimits::new(), stream_shutdown: watch::channel(false).0, spaces: Arc::new(spaces), space_auth: Arc::new(space_auth), }) }
/// Verify the configured handle through public DNS or HTTPS before serving. /// /// Local mode keeps its derived identity offline. Production fails closed /// when the public handle does not resolve back to the configured DID. /// /// # Errors /// /// Returns an error when public handle or DID resolution fails, the handle /// resolves to another DID, or the DID document lacks the handle backlink. pub async fn verify_network_identity(&self) -> Result<()> { if self.config.deployment_mode == config::DeploymentMode::Production { self.identity.verify_local_handle().await?; } Ok(()) }
pub(crate) async fn revalidate_network_identity(&self) -> Result<IdentityRevalidation> { if self.config.deployment_mode == config::DeploymentMode::Local { return Ok(IdentityRevalidation::Unchanged); } let valid = self.identity.revalidate_local_handle().await?; let handle = if valid { &self.config.account.handle } else { "handle.invalid" }; let changed = self .repository .set_account_handle(&self.config.account.did, handle)?; Ok(match (valid, changed) { (_, false) => IdentityRevalidation::Unchanged, (true, true) => IdentityRevalidation::Recovered, (false, true) => IdentityRevalidation::Invalidated, }) }}