diff --git a/crates/nxmesh-core/Cargo.toml b/crates/nxmesh-core/Cargo.toml new file mode 100644 index 0000000..0bcdfb0 --- /dev/null +++ b/crates/nxmesh-core/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nxmesh-core" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +chrono.workspace = true +uuid.workspace = true + +[dev-dependencies] +tokio-test.workspace = true diff --git a/crates/nxmesh-core/src/error.rs b/crates/nxmesh-core/src/error.rs new file mode 100644 index 0000000..ab2c201 --- /dev/null +++ b/crates/nxmesh-core/src/error.rs @@ -0,0 +1,43 @@ +//! Core error types + +use thiserror::Error; + +/// Result type alias for core operations +pub type Result = std::result::Result; + +/// Core error types used across NxMesh +#[derive(Error, Debug, Clone)] +pub enum CoreError { + #[error("Validation error: {0}")] + Validation(String), + + #[error("Invalid configuration: {0}")] + InvalidConfig(String), + + #[error("Serialization error: {0}")] + Serialization(String), + + #[error("Crypto error: {0}")] + Crypto(String), + + #[error("Not found: {0}")] + NotFound(String), + + #[error("Permission denied: {0}")] + PermissionDenied(String), + + #[error("Internal error: {0}")] + Internal(String), +} + +impl CoreError { + /// Create a validation error + pub fn validation>(msg: S) -> Self { + Self::Validation(msg.into()) + } + + /// Create a not found error + pub fn not_found>(resource: S) -> Self { + Self::NotFound(resource.into()) + } +} diff --git a/crates/nxmesh-core/src/lib.rs b/crates/nxmesh-core/src/lib.rs new file mode 100644 index 0000000..ba20d7e --- /dev/null +++ b/crates/nxmesh-core/src/lib.rs @@ -0,0 +1,9 @@ +//! NxMesh Core Library +//! +//! This crate contains shared types, models, and utilities used by both +//! the master and agent components of NxMesh. + +pub mod error; +pub mod models; + +pub use error::{CoreError, Result}; diff --git a/crates/nxmesh-core/src/models/agent.rs b/crates/nxmesh-core/src/models/agent.rs new file mode 100644 index 0000000..90353dc --- /dev/null +++ b/crates/nxmesh-core/src/models/agent.rs @@ -0,0 +1,152 @@ +//! Agent model + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use uuid::Uuid; + +/// Agent entity - represents a running nxmesh-agent instance +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Agent { + pub id: Uuid, + pub workspace_id: Uuid, + pub name: String, + pub hostname: String, + pub ip_address: String, + pub version: String, + pub state: AgentState, + pub deployment_mode: DeploymentMode, + pub last_seen_at: DateTime, + pub capabilities: Vec, + pub labels: HashMap, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +/// Agent connection state +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AgentState { + /// Registered but never connected + Pending, + /// Connected and healthy + Online, + /// Disconnected + Offline, + /// Connected but health checks failing + Degraded, + /// Manually placed in maintenance mode + Maintenance, +} + +/// Deployment mode for the agent +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DeploymentMode { + /// Docker sidecar (shares PID namespace with nginx) + DockerSidecar, + /// Kubernetes sidecar + KubernetesSidecar, + /// Standalone mode (VM or bare metal) + Standalone, +} + +/// Health report from agent +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthReport { + pub agent_id: Uuid, + pub timestamp: DateTime, + pub nginx_status: NginxStatus, + pub system_metrics: SystemMetrics, + pub config_checksum: String, + pub alerts: Vec, +} + +/// Nginx process status +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NginxStatus { + pub is_running: bool, + pub pid: Option, + pub uptime_seconds: u64, + pub active_connections: u32, + pub requests_per_second: f64, +} + +/// System-level metrics +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemMetrics { + pub cpu_percent: f64, + pub memory_used_mb: u64, + pub memory_total_mb: u64, + pub disk_used_gb: u64, + pub disk_total_gb: u64, +} + +/// Alert from agent +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Alert { + pub id: String, + pub severity: AlertSeverity, + pub message: String, + pub timestamp: DateTime, +} + +/// Alert severity levels +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AlertSeverity { + Info, + Warning, + Error, + Critical, +} + +impl Agent { + /// Create a new agent registration + pub fn new( + workspace_id: Uuid, + name: impl Into, + hostname: impl Into, + ip_address: impl Into, + version: impl Into, + deployment_mode: DeploymentMode, + ) -> Self { + let now = Utc::now(); + Self { + id: Uuid::new_v4(), + workspace_id, + name: name.into(), + hostname: hostname.into(), + ip_address: ip_address.into(), + version: version.into(), + state: AgentState::Pending, + deployment_mode, + last_seen_at: now, + capabilities: Vec::new(), + labels: HashMap::new(), + created_at: now, + updated_at: now, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_agent_creation() { + let ws_id = Uuid::new_v4(); + let agent = Agent::new( + ws_id, + "web-server-01", + "web-01.internal", + "10.0.1.10", + "0.1.0", + DeploymentMode::DockerSidecar, + ); + assert_eq!(agent.name, "web-server-01"); + assert_eq!(agent.state, AgentState::Pending); + assert!(agent.capabilities.is_empty()); + } +} diff --git a/crates/nxmesh-core/src/models/certificate.rs b/crates/nxmesh-core/src/models/certificate.rs new file mode 100644 index 0000000..3f4cb75 --- /dev/null +++ b/crates/nxmesh-core/src/models/certificate.rs @@ -0,0 +1,146 @@ +//! Certificate model + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// SSL/TLS Certificate entity +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Certificate { + pub id: Uuid, + pub workspace_id: Uuid, + pub domain: String, + pub is_wildcard: bool, + pub provider: CertificateProvider, + pub status: CertificateStatus, + pub issued_at: Option>, + pub expires_at: Option>, + pub auto_renew: bool, + pub certificate_pem: Option, + pub private_key_pem: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +/// Certificate provider +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum CertificateProvider { + LetsEncrypt, + Custom, +} + +/// Certificate status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum CertificateStatus { + Pending, + Active, + Expired, + Error, +} + +/// ACME challenge info +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AcmeChallenge { + pub certificate_id: Uuid, + pub challenge_type: ChallengeType, + pub token: String, + pub key_authorization: String, + pub status: ChallengeStatus, + pub completed_at: Option>, +} + +/// ACME challenge type +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ChallengeType { + Http01, + Dns01, +} + +/// ACME challenge status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ChallengeStatus { + Pending, + Processing, + Valid, + Invalid, +} + +impl Certificate { + /// Create a new certificate request + pub fn new( + workspace_id: Uuid, + domain: impl Into, + provider: CertificateProvider, + auto_renew: bool, + ) -> Self { + let now = Utc::now(); + Self { + id: Uuid::new_v4(), + workspace_id, + domain: domain.into(), + is_wildcard: false, + provider, + status: CertificateStatus::Pending, + issued_at: None, + expires_at: None, + auto_renew, + certificate_pem: None, + private_key_pem: None, + created_at: now, + updated_at: now, + } + } + + /// Check if certificate is expired or expiring soon + pub fn is_expiring_soon(&self, days: i64) -> bool { + match self.expires_at { + Some(expires) => { + let threshold = Utc::now() + chrono::Duration::days(days); + expires <= threshold + } + None => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_certificate_creation() { + let ws_id = Uuid::new_v4(); + let cert = Certificate::new( + ws_id, + "example.com", + CertificateProvider::LetsEncrypt, + true, + ); + assert_eq!(cert.domain, "example.com"); + assert_eq!(cert.status, CertificateStatus::Pending); + assert!(cert.auto_renew); + } + + #[test] + fn test_certificate_expiring_soon() { + let ws_id = Uuid::new_v4(); + let mut cert = Certificate::new( + ws_id, + "example.com", + CertificateProvider::LetsEncrypt, + true, + ); + + // Not expiring + cert.expires_at = Some(Utc::now() + chrono::Duration::days(60)); + assert!(!cert.is_expiring_soon(30)); + + // Expiring soon + cert.expires_at = Some(Utc::now() + chrono::Duration::days(20)); + assert!(cert.is_expiring_soon(30)); + } +} diff --git a/crates/nxmesh-core/src/models/config.rs b/crates/nxmesh-core/src/models/config.rs new file mode 100644 index 0000000..14f2ec5 --- /dev/null +++ b/crates/nxmesh-core/src/models/config.rs @@ -0,0 +1,195 @@ +//! Configuration models + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use uuid::Uuid; + +/// Virtual Host (server block) configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VirtualHost { + pub id: Uuid, + pub workspace_id: Uuid, + pub name: String, + pub server_name: String, + pub listen_port: u16, + pub ssl_enabled: bool, + pub ssl_certificate_id: Option, + pub locations: Vec, + pub http2_enabled: bool, + pub http3_enabled: bool, + pub gzip_enabled: bool, + pub rate_limiting: Option, + pub target_agents: AgentSelector, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +/// Location block configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Location { + pub path: String, + pub proxy_pass: Option, + pub upstream_id: Option, + pub root: Option, + pub index: Option, + pub custom_headers: Vec
, + pub rewrite_rules: Vec, +} + +/// HTTP header +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Header { + pub name: String, + pub value: String, + pub always: bool, +} + +/// Rewrite rule +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RewriteRule { + pub pattern: String, + pub replacement: String, + pub flag: String, +} + +/// Rate limiting configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RateLimitConfig { + pub zone: String, + pub burst: u32, + pub per_minute: Option, + pub per_second: Option, +} + +/// Upstream (backend pool) configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Upstream { + pub id: Uuid, + pub workspace_id: Uuid, + pub name: String, + pub algorithm: LoadBalanceAlgorithm, + pub servers: Vec, + pub health_check: Option, + pub keepalive_connections: Option, + pub keepalive_timeout: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +/// Upstream server definition +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpstreamServer { + pub address: String, + pub weight: u32, + pub backup: bool, + pub down: bool, + pub max_fails: u32, + pub fail_timeout: u32, +} + +/// Load balancing algorithm +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum LoadBalanceAlgorithm { + RoundRobin, + LeastConnections, + IpHash, + WeightedRoundRobin, +} + +/// Health check configuration for upstreams +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthCheckConfig { + pub enabled: bool, + pub path: String, + pub interval: u32, + pub timeout: u32, + pub healthy_threshold: u32, + pub unhealthy_threshold: u32, +} + +/// Selector for targeting agents +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum AgentSelector { + /// All agents in the workspace + All, + /// Agents matching label selector + Selector { label_selector: String }, + /// Specific agent + Agent { agent_id: Uuid }, +} + +/// Configuration scope for distribution +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ConfigScope { + /// All agents + Global, + /// All agents in workspace + Workspace, + /// Agents with specific label selector + AgentGroup(String), + /// Single agent + Agent(Uuid), +} + +/// Configuration version for tracking changes +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConfigVersion { + pub id: Uuid, + pub resource_type: String, + pub resource_id: Uuid, + pub version_number: u64, + pub data: serde_json::Value, + pub checksum: String, + pub created_by: Uuid, + pub created_at: DateTime, + pub change_summary: String, +} + +/// Configuration update message sent to agents +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConfigUpdate { + pub config_id: String, + pub version: u64, + pub virtual_hosts: Vec, + pub upstreams: Vec, + pub ssl_certificates: HashMap, +} + +/// Certificate data in config update +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Certificate { + pub id: String, + pub domain: String, + pub certificate_pem: String, + pub private_key_pem: String, + pub expires_at: DateTime, +} + +/// Configuration apply status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ConfigApplyStatus { + Pending, + Validating, + Applying, + Success, + Failed, + RolledBack, +} + +impl Default for UpstreamServer { + fn default() -> Self { + Self { + address: String::new(), + weight: 1, + backup: false, + down: false, + max_fails: 1, + fail_timeout: 10, + } + } +} diff --git a/crates/nxmesh-core/src/models/mod.rs b/crates/nxmesh-core/src/models/mod.rs new file mode 100644 index 0000000..12b948c --- /dev/null +++ b/crates/nxmesh-core/src/models/mod.rs @@ -0,0 +1,13 @@ +//! Shared data models + +pub mod agent; +pub mod certificate; +pub mod config; +pub mod organization; +pub mod workspace; + +pub use agent::{Agent, AgentState, DeploymentMode}; +pub use certificate::{Certificate, CertificateProvider, CertificateStatus}; +pub use config::{AgentSelector, ConfigScope, Location, Upstream, UpstreamServer, VirtualHost}; +pub use organization::Organization; +pub use workspace::Workspace; diff --git a/crates/nxmesh-core/src/models/organization.rs b/crates/nxmesh-core/src/models/organization.rs new file mode 100644 index 0000000..c2090a7 --- /dev/null +++ b/crates/nxmesh-core/src/models/organization.rs @@ -0,0 +1,62 @@ +//! Organization model + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// Organization entity +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Organization { + pub id: Uuid, + pub name: String, + pub slug: String, + pub created_at: DateTime, + pub updated_at: DateTime, + pub settings: OrganizationSettings, +} + +/// Organization-specific settings +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OrganizationSettings { + pub allow_public_agents: bool, + pub max_workspaces: Option, + pub max_agents_per_workspace: Option, +} + +impl Default for OrganizationSettings { + fn default() -> Self { + Self { + allow_public_agents: false, + max_workspaces: None, + max_agents_per_workspace: None, + } + } +} + +impl Organization { + /// Create a new organization + pub fn new(name: impl Into, slug: impl Into) -> Self { + let now = Utc::now(); + Self { + id: Uuid::new_v4(), + name: name.into(), + slug: slug.into(), + created_at: now, + updated_at: now, + settings: OrganizationSettings::default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_organization_creation() { + let org = Organization::new("Acme Corp", "acme-corp"); + assert_eq!(org.name, "Acme Corp"); + assert_eq!(org.slug, "acme-corp"); + assert!(!org.settings.allow_public_agents); + } +} diff --git a/crates/nxmesh-core/src/models/workspace.rs b/crates/nxmesh-core/src/models/workspace.rs new file mode 100644 index 0000000..d03d30f --- /dev/null +++ b/crates/nxmesh-core/src/models/workspace.rs @@ -0,0 +1,49 @@ +//! Workspace model + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// Workspace entity - isolated resource container within an organization +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Workspace { + pub id: Uuid, + pub organization_id: Uuid, + pub name: String, + pub slug: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +impl Workspace { + /// Create a new workspace + pub fn new( + organization_id: Uuid, + name: impl Into, + slug: impl Into, + ) -> Self { + let now = Utc::now(); + Self { + id: Uuid::new_v4(), + organization_id, + name: name.into(), + slug: slug.into(), + created_at: now, + updated_at: now, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_workspace_creation() { + let org_id = Uuid::new_v4(); + let ws = Workspace::new(org_id, "Production", "production"); + assert_eq!(ws.name, "Production"); + assert_eq!(ws.slug, "production"); + assert_eq!(ws.organization_id, org_id); + } +} diff --git a/crates/nxmesh-proto/Cargo.toml b/crates/nxmesh-proto/Cargo.toml new file mode 100644 index 0000000..7ed30db --- /dev/null +++ b/crates/nxmesh-proto/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nxmesh-proto" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +tonic.workspace = true +prost.workspace = true + +[build-dependencies] +tonic-build = "0.11" diff --git a/crates/nxmesh-proto/build.rs b/crates/nxmesh-proto/build.rs new file mode 100644 index 0000000..86f4003 --- /dev/null +++ b/crates/nxmesh-proto/build.rs @@ -0,0 +1,9 @@ +use std::io::Result; + +fn main() -> Result<()> { + tonic_build::configure() + .build_server(true) + .build_client(true) + .compile(&["proto/agent.proto"], &["proto"])?; + Ok(()) +} diff --git a/crates/nxmesh-proto/proto/agent.proto b/crates/nxmesh-proto/proto/agent.proto new file mode 100644 index 0000000..4169383 --- /dev/null +++ b/crates/nxmesh-proto/proto/agent.proto @@ -0,0 +1,298 @@ +syntax = "proto3"; +package nxmesh.agent.v1; + +option go_package = "github.com/nxmesh/api/agent/v1"; + +// AgentService defines the bidirectional communication between master and agents +service AgentService { + // Stream establishes a persistent connection for real-time communication + rpc Stream(stream AgentMessage) returns (stream MasterMessage); + + // ReportHealth sends a health report to the master + rpc ReportHealth(HealthReport) returns (Ack); + + // ReportMetrics sends metrics batch to the master + rpc ReportMetrics(MetricsBatch) returns (Ack); +} + +// Messages sent from agent to master +message AgentMessage { + string agent_id = 1; + int64 timestamp = 2; + oneof payload { + RegistrationRequest registration = 3; + HealthReport health = 4; + ConfigStatus config_status = 5; + MetricsBatch metrics = 6; + LogBatch logs = 7; + Event event = 8; + } +} + +// Messages sent from master to agent +message MasterMessage { + int64 timestamp = 1; + oneof payload { + RegistrationResponse registration_response = 2; + ConfigUpdate config_update = 3; + Command command = 4; + Ack ack = 5; + Error error = 6; + } +} + +// Registration +message RegistrationRequest { + string token = 1; + string hostname = 2; + string ip_address = 3; + string version = 4; + repeated string capabilities = 5; + map labels = 6; + DeploymentMode deployment_mode = 7; +} + +message RegistrationResponse { + string agent_id = 1; + bool success = 2; + string error_message = 3; + int64 heartbeat_interval_seconds = 4; +} + +enum DeploymentMode { + DEPLOYMENT_MODE_UNSPECIFIED = 0; + DOCKER_SIDECAR = 1; + KUBERNETES_SIDECAR = 2; + STANDALONE = 3; +} + +// Health Reporting +message HealthReport { + NginxStatus nginx = 1; + SystemMetrics system = 2; + string config_checksum = 3; + int64 config_version = 4; + repeated Alert alerts = 5; +} + +message NginxStatus { + bool is_running = 1; + uint32 pid = 2; + uint64 uptime_seconds = 3; + uint32 active_connections = 4; + uint64 total_requests = 5; + float requests_per_second = 6; +} + +message SystemMetrics { + float cpu_percent = 1; + uint64 memory_used_bytes = 2; + uint64 memory_total_bytes = 3; + uint64 disk_used_bytes = 4; + uint64 disk_total_bytes = 5; + float load_average_1m = 6; +} + +message Alert { + string id = 1; + string severity = 2; // info, warning, error, critical + string message = 3; + int64 timestamp = 4; +} + +// Configuration +message ConfigUpdate { + string config_id = 1; + int64 version = 2; + repeated VirtualHost virtual_hosts = 3; + repeated Upstream upstreams = 4; + map certificates = 5; + GlobalSettings global_settings = 6; +} + +message VirtualHost { + string id = 1; + string name = 2; + string server_name = 3; + uint32 listen_port = 4; + bool ssl_enabled = 5; + string ssl_certificate_id = 6; + bool http2_enabled = 7; + bool http3_enabled = 8; + repeated Location locations = 9; + map custom_directives = 10; +} + +message Location { + string path = 1; + string proxy_pass = 2; + string upstream_id = 3; + string root = 4; + string index = 5; + repeated Header custom_headers = 6; + repeated RewriteRule rewrite_rules = 7; + map custom_directives = 8; +} + +message Header { + string name = 1; + string value = 2; + bool always = 3; +} + +message RewriteRule { + string pattern = 1; + string replacement = 2; + string flag = 3; +} + +message Upstream { + string id = 1; + string name = 2; + LoadBalanceAlgorithm algorithm = 3; + repeated UpstreamServer servers = 4; + HealthCheckConfig health_check = 5; + uint32 keepalive_connections = 6; +} + +enum LoadBalanceAlgorithm { + LOAD_BALANCE_ALGORITHM_UNSPECIFIED = 0; + ROUND_ROBIN = 1; + LEAST_CONNECTIONS = 2; + IP_HASH = 3; + WEIGHTED_ROUND_ROBIN = 4; +} + +message UpstreamServer { + string address = 1; + uint32 weight = 2; + bool backup = 3; + bool down = 4; + uint32 max_fails = 5; + uint32 fail_timeout_seconds = 6; +} + +message HealthCheckConfig { + bool enabled = 1; + string path = 2; + uint32 interval_seconds = 3; + uint32 timeout_seconds = 4; + uint32 healthy_threshold = 5; + uint32 unhealthy_threshold = 6; +} + +message Certificate { + string id = 1; + string domain = 2; + string certificate_pem = 3; + string private_key_pem = 4; + int64 expires_at = 5; +} + +message GlobalSettings { + map nginx_directives = 1; + map env_vars = 2; +} + +message ConfigStatus { + string config_id = 1; + int64 version = 2; + ConfigApplyStatus status = 3; + string error_message = 4; + int64 applied_at = 5; +} + +enum ConfigApplyStatus { + CONFIG_APPLY_STATUS_UNSPECIFIED = 0; + PENDING = 1; + VALIDATING = 2; + APPLYING = 3; + SUCCESS = 4; + FAILED = 5; + ROLLED_BACK = 6; +} + +// Metrics +message MetricsBatch { + int64 timestamp = 1; + repeated Metric metrics = 2; +} + +message Metric { + string name = 1; + double value = 2; + int64 timestamp = 3; + map labels = 4; + MetricType type = 5; +} + +enum MetricType { + METRIC_TYPE_UNSPECIFIED = 0; + GAUGE = 1; + COUNTER = 2; + HISTOGRAM = 3; +} + +// Logs +message LogBatch { + repeated LogEntry entries = 1; +} + +message LogEntry { + int64 timestamp = 1; + string level = 2; + string message = 3; + map fields = 4; +} + +// Commands +message Command { + string command_id = 1; + oneof command { + ReloadCommand reload = 2; + RestartCommand restart = 3; + StopCommand stop = 4; + GetStatusCommand get_status = 5; + ValidateConfigCommand validate_config = 6; + } +} + +message ReloadCommand { + bool graceful = 1; +} + +message RestartCommand { + bool force = 1; +} + +message StopCommand { + bool graceful = 1; + uint32 timeout_seconds = 2; +} + +message GetStatusCommand {} + +message ValidateConfigCommand { + string config_content = 1; +} + +// Events +message Event { + string event_id = 1; + string event_type = 2; + int64 timestamp = 3; + map data = 4; +} + +// Common messages +message Ack { + string message_id = 1; + bool success = 2; + string error_message = 3; +} + +message Error { + string code = 1; + string message = 2; + map details = 3; +} diff --git a/crates/nxmesh-proto/src/lib.rs b/crates/nxmesh-proto/src/lib.rs new file mode 100644 index 0000000..e8da2fb --- /dev/null +++ b/crates/nxmesh-proto/src/lib.rs @@ -0,0 +1,9 @@ +//! NxMesh Protocol Buffers +//! +//! This crate contains the gRPC protocol definitions for master-agent communication. + +pub mod agent { + tonic::include_proto!("nxmesh.agent.v1"); +} + +pub use agent::*;