//! Synor Hosting - Decentralized Web Hosting //! //! Provides subdomain-based hosting for web applications on Synor Storage. //! //! # Components //! //! - **Name Registry**: On-chain mapping of names to CIDs //! - **Domain Verification**: Custom domain ownership proof //! - **Gateway Router**: Host-based routing to content //! //! # Example //! //! ```rust,ignore //! use synor_hosting::{NameRegistry, HostingGateway}; //! //! // Register a name //! registry.register("myapp", cid, owner)?; //! //! // Resolve name to CID //! let cid = registry.resolve("myapp")?; //! //! // Gateway routes myapp.synor.network to CID //! gateway.handle_request("myapp.synor.network", "/").await?; //! ``` pub mod compute; pub mod config; pub mod domain; pub mod error; pub mod registry; pub mod router; #[cfg(feature = "server")] pub mod server; pub use registry::{NameRegistry, NameRecord, RegistrationRequest}; pub use domain::{DomainVerifier, DomainRecord, VerificationMethod}; pub use router::{HostingRouter, RouteConfig}; pub use config::SynorJson; pub use error::{Error, Result}; #[cfg(feature = "server")] pub use server::{HostingGateway, GatewayConfig}; /// Reserved names that cannot be registered pub const RESERVED_NAMES: &[&str] = &[ "synor", "admin", "api", "gateway", "www", "mail", "ftp", "ssh", "cdn", "static", "assets", "app", "web", "blog", "docs", "help", "support", "status", "system", "root", "test", "dev", "staging", "prod", "production", "null", "undefined", "localhost", "local", ]; /// Validate a name according to registry rules pub fn validate_name(name: &str) -> Result<()> { // Length check if name.len() < 3 { return Err(Error::InvalidName("Name must be at least 3 characters".into())); } if name.len() > 63 { return Err(Error::InvalidName("Name must be at most 63 characters".into())); } // Character check if !name.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') { return Err(Error::InvalidName( "Name must contain only lowercase letters, numbers, and hyphens".into(), )); } // Cannot start or end with hyphen if name.starts_with('-') || name.ends_with('-') { return Err(Error::InvalidName("Name cannot start or end with hyphen".into())); } // Cannot have consecutive hyphens if name.contains("--") { return Err(Error::InvalidName("Name cannot contain consecutive hyphens".into())); } // Reserved name check if RESERVED_NAMES.contains(&name) { return Err(Error::ReservedName(name.to_string())); } Ok(()) } /// Check if a name looks confusingly similar to another pub fn is_confusable(name: &str, other: &str) -> bool { // Simple homoglyph detection let normalize = |s: &str| -> String { s.chars() .map(|c| match c { '0' => 'o', '1' | 'l' => 'i', '5' => 's', '3' => 'e', '4' => 'a', '7' => 't', '8' => 'b', '9' => 'g', _ => c, }) .collect() }; normalize(name) == normalize(other) } #[cfg(test)] mod tests { use super::*; #[test] fn test_validate_name_valid() { assert!(validate_name("myapp").is_ok()); assert!(validate_name("my-app").is_ok()); assert!(validate_name("app123").is_ok()); assert!(validate_name("123app").is_ok()); assert!(validate_name("a-b-c").is_ok()); } #[test] fn test_validate_name_invalid() { assert!(validate_name("ab").is_err()); // Too short assert!(validate_name("MyApp").is_err()); // Uppercase assert!(validate_name("-myapp").is_err()); // Starts with hyphen assert!(validate_name("myapp-").is_err()); // Ends with hyphen assert!(validate_name("my--app").is_err()); // Consecutive hyphens assert!(validate_name("my app").is_err()); // Space assert!(validate_name("my_app").is_err()); // Underscore } #[test] fn test_reserved_names() { assert!(validate_name("synor").is_err()); assert!(validate_name("admin").is_err()); assert!(validate_name("api").is_err()); assert!(validate_name("gateway").is_err()); } #[test] fn test_confusable_names() { assert!(is_confusable("g00gle", "google")); assert!(is_confusable("paypa1", "paypal")); assert!(is_confusable("fac3book", "facebook")); assert!(!is_confusable("myapp", "yourapp")); } }