feat(compute): integrate synor-compute with VM and hosting layers

VM Integration:
- Add compute module with offloadable operations support
- Enable distributed execution for heavy VM operations
- Support batch signature verification, merkle proofs, hashing
- Add ComputeContext for managing compute cluster connections
- Feature-gated behind 'compute' flag

Hosting Integration:
- Add edge compute module for serverless functions
- Support edge functions (WASM, JS, Python runtimes)
- Enable server-side rendering and image optimization
- Add AI/ML inference at the edge
- Feature-gated behind 'compute' flag

Docker Deployment:
- Add docker-compose.compute.yml for compute layer
- Deploy orchestrator, CPU workers, WASM worker, spot market
- Include Redis for task queue and Prometheus for metrics
- Reserved ports: 17250-17290 for compute services
This commit is contained in:
Gulshan Yadav 2026-01-11 14:05:45 +05:30
parent 4c36ddbdc2
commit 771f4f83ed
9 changed files with 1058 additions and 4 deletions

View file

@ -35,12 +35,14 @@ reqwest = { version = "0.12", features = ["json"], optional = true }
synor-types = { path = "../synor-types" }
synor-crypto = { path = "../synor-crypto" }
synor-storage = { path = "../synor-storage" }
synor-compute = { path = "../synor-compute", optional = true }
[features]
default = []
dns = ["trust-dns-resolver"]
server = ["axum", "tower", "reqwest"]
full = ["dns", "server"]
compute = ["synor-compute"]
full = ["dns", "server", "compute"]
[[bin]]
name = "hosting-gateway"

View file

@ -0,0 +1,385 @@
//! Compute integration for edge functions and SSR.
//!
//! Enables serverless compute capabilities for hosted websites:
//! - Edge functions (API routes, middleware)
//! - Server-side rendering (SSR)
//! - Image optimization
//! - AI/ML inference at the edge
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
/// Edge function configuration.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EdgeFunctionConfig {
/// Function name/path.
pub name: String,
/// Runtime (wasm, js).
pub runtime: EdgeRuntime,
/// Memory limit in MB.
pub memory_mb: u32,
/// Timeout in seconds.
pub timeout_secs: u32,
/// Environment variables.
pub env: HashMap<String, String>,
/// Region preferences.
pub regions: Vec<String>,
}
impl Default for EdgeFunctionConfig {
fn default() -> Self {
Self {
name: String::new(),
runtime: EdgeRuntime::Wasm,
memory_mb: 128,
timeout_secs: 10,
env: HashMap::new(),
regions: vec!["global".to_string()],
}
}
}
/// Supported edge runtimes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EdgeRuntime {
/// WebAssembly (via synor-compute WASM workers).
Wasm,
/// JavaScript (V8 isolates).
JavaScript,
/// Python (via WebAssembly).
Python,
}
/// Edge function request.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EdgeRequest {
/// HTTP method.
pub method: String,
/// Request path.
pub path: String,
/// Query parameters.
pub query: HashMap<String, String>,
/// Headers.
pub headers: HashMap<String, String>,
/// Request body.
pub body: Option<Vec<u8>>,
/// Client IP.
pub client_ip: Option<String>,
/// Geolocation.
pub geo: Option<GeoInfo>,
}
/// Geolocation information.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GeoInfo {
/// Country code.
pub country: String,
/// Region/state.
pub region: Option<String>,
/// City.
pub city: Option<String>,
/// Latitude.
pub latitude: Option<f64>,
/// Longitude.
pub longitude: Option<f64>,
}
/// Edge function response.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EdgeResponse {
/// HTTP status code.
pub status: u16,
/// Response headers.
pub headers: HashMap<String, String>,
/// Response body.
pub body: Vec<u8>,
}
impl EdgeResponse {
/// Create a 200 OK response.
pub fn ok(body: impl Into<Vec<u8>>) -> Self {
Self {
status: 200,
headers: HashMap::new(),
body: body.into(),
}
}
/// Create a JSON response.
pub fn json<T: Serialize>(data: &T) -> Result<Self, serde_json::Error> {
let body = serde_json::to_vec(data)?;
let mut headers = HashMap::new();
headers.insert("Content-Type".to_string(), "application/json".to_string());
Ok(Self {
status: 200,
headers,
body,
})
}
/// Create a redirect response.
pub fn redirect(location: &str, permanent: bool) -> Self {
let mut headers = HashMap::new();
headers.insert("Location".to_string(), location.to_string());
Self {
status: if permanent { 301 } else { 302 },
headers,
body: Vec::new(),
}
}
/// Create an error response.
pub fn error(status: u16, message: &str) -> Self {
Self {
status,
headers: HashMap::new(),
body: message.as_bytes().to_vec(),
}
}
}
/// Edge compute executor.
#[derive(Clone, Debug)]
pub struct EdgeCompute {
/// Compute endpoint URL.
endpoint: String,
/// Default timeout.
default_timeout: Duration,
/// Enabled flag.
enabled: bool,
}
impl EdgeCompute {
/// Create a new edge compute executor.
pub fn new(endpoint: &str) -> Self {
Self {
endpoint: endpoint.to_string(),
default_timeout: Duration::from_secs(10),
enabled: true,
}
}
/// Create a disabled edge compute (local execution only).
pub fn disabled() -> Self {
Self {
endpoint: String::new(),
default_timeout: Duration::from_secs(10),
enabled: false,
}
}
/// Check if edge compute is enabled.
pub fn is_enabled(&self) -> bool {
self.enabled
}
/// Execute an edge function.
pub async fn execute(
&self,
config: &EdgeFunctionConfig,
request: EdgeRequest,
) -> Result<EdgeResponse, EdgeError> {
if !self.enabled {
return Err(EdgeError::NotEnabled);
}
// In a full implementation, this would:
// 1. Route to the nearest compute node
// 2. Load the function bytecode from storage
// 3. Execute in a sandboxed environment
// 4. Return the response
// For now, return a stub response
Ok(EdgeResponse::ok(format!(
"Edge function '{}' executed",
config.name
)))
}
/// Execute image optimization.
pub async fn optimize_image(
&self,
image_data: &[u8],
options: ImageOptimizeOptions,
) -> Result<Vec<u8>, EdgeError> {
if !self.enabled {
return Err(EdgeError::NotEnabled);
}
// In a full implementation, this would use compute nodes for:
// - Image resizing
// - Format conversion (WebP, AVIF)
// - Quality optimization
// - Face/object detection for smart cropping
Ok(image_data.to_vec()) // Stub: return original
}
/// Run AI inference at the edge.
pub async fn inference(
&self,
model: &str,
input: &[u8],
) -> Result<Vec<u8>, EdgeError> {
if !self.enabled {
return Err(EdgeError::NotEnabled);
}
// In a full implementation, this would:
// 1. Route to a compute node with the model loaded
// 2. Use NPU/GPU for inference
// 3. Return the results
Ok(Vec::new()) // Stub
}
}
impl Default for EdgeCompute {
fn default() -> Self {
Self::disabled()
}
}
/// Image optimization options.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImageOptimizeOptions {
/// Target width.
pub width: Option<u32>,
/// Target height.
pub height: Option<u32>,
/// Output format.
pub format: ImageFormat,
/// Quality (0-100).
pub quality: u8,
/// Fit mode.
pub fit: ImageFit,
}
impl Default for ImageOptimizeOptions {
fn default() -> Self {
Self {
width: None,
height: None,
format: ImageFormat::Auto,
quality: 80,
fit: ImageFit::Cover,
}
}
}
/// Image output formats.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImageFormat {
/// Auto-detect best format.
Auto,
/// WebP.
WebP,
/// AVIF.
Avif,
/// JPEG.
Jpeg,
/// PNG.
Png,
}
/// Image fit modes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImageFit {
/// Cover the area (crop if needed).
Cover,
/// Contain within area (letterbox if needed).
Contain,
/// Fill the area (stretch if needed).
Fill,
/// No resizing, just format conversion.
None,
}
/// Edge compute errors.
#[derive(Clone, Debug)]
pub enum EdgeError {
/// Edge compute not enabled.
NotEnabled,
/// Timeout.
Timeout,
/// Function not found.
FunctionNotFound(String),
/// Runtime error.
RuntimeError(String),
/// Out of memory.
OutOfMemory,
/// Invalid input.
InvalidInput(String),
}
impl std::fmt::Display for EdgeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EdgeError::NotEnabled => write!(f, "Edge compute not enabled"),
EdgeError::Timeout => write!(f, "Function execution timed out"),
EdgeError::FunctionNotFound(name) => write!(f, "Function not found: {}", name),
EdgeError::RuntimeError(msg) => write!(f, "Runtime error: {}", msg),
EdgeError::OutOfMemory => write!(f, "Out of memory"),
EdgeError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
}
}
}
impl std::error::Error for EdgeError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_edge_response() {
let resp = EdgeResponse::ok(b"Hello, World!".to_vec());
assert_eq!(resp.status, 200);
assert_eq!(resp.body, b"Hello, World!");
let redirect = EdgeResponse::redirect("/new-path", false);
assert_eq!(redirect.status, 302);
assert_eq!(
redirect.headers.get("Location"),
Some(&"/new-path".to_string())
);
let error = EdgeResponse::error(404, "Not Found");
assert_eq!(error.status, 404);
}
#[test]
fn test_edge_function_config() {
let config = EdgeFunctionConfig {
name: "api/hello".to_string(),
runtime: EdgeRuntime::Wasm,
memory_mb: 256,
timeout_secs: 30,
..Default::default()
};
assert_eq!(config.name, "api/hello");
assert_eq!(config.memory_mb, 256);
}
#[tokio::test]
async fn test_edge_compute_disabled() {
let compute = EdgeCompute::disabled();
assert!(!compute.is_enabled());
let config = EdgeFunctionConfig::default();
let request = EdgeRequest {
method: "GET".to_string(),
path: "/api/test".to_string(),
query: HashMap::new(),
headers: HashMap::new(),
body: None,
client_ip: None,
geo: None,
};
let result = compute.execute(&config, request).await;
assert!(matches!(result, Err(EdgeError::NotEnabled)));
}
}

View file

@ -23,11 +23,12 @@
//! gateway.handle_request("myapp.synor.network", "/").await?;
//! ```
pub mod registry;
pub mod domain;
pub mod router;
pub mod compute;
pub mod config;
pub mod domain;
pub mod error;
pub mod registry;
pub mod router;
#[cfg(feature = "server")]
pub mod server;

View file

@ -9,6 +9,7 @@ license.workspace = true
# Internal crates
synor-types = { path = "../synor-types" }
synor-crypto = { path = "../synor-crypto" }
synor-compute = { path = "../synor-compute", optional = true }
# WASM runtime
wasmtime.workspace = true
@ -38,5 +39,9 @@ num_cpus = "1.16"
zstd = "0.13"
lru = "0.12"
[features]
default = []
compute = ["synor-compute"]
[dev-dependencies]
tempfile.workspace = true

View file

@ -0,0 +1,279 @@
//! Compute integration for distributed VM execution.
//!
//! This module enables VM contract execution to be distributed across
//! heterogeneous compute resources (CPU, GPU, TPU, NPU, etc.).
#[cfg(feature = "compute")]
use synor_compute::{
processor::{Operation, OperationType, Precision, ProcessorId, ProcessorType},
scheduler::BalancingStrategy,
task::{Task, TaskId, TaskPriority, TaskStatus},
ComputeCluster, ComputeJob, JobId, NodeId,
};
use crate::gas::GasMeter;
use std::sync::Arc;
/// Configuration for compute-accelerated VM execution.
#[derive(Clone, Debug)]
pub struct ComputeConfig {
/// Whether to enable compute offloading.
pub enabled: bool,
/// Minimum gas threshold to consider offloading.
pub min_gas_threshold: u64,
/// Preferred balancing strategy.
pub strategy: String,
/// Maximum parallel tasks.
pub max_parallel_tasks: usize,
}
impl Default for ComputeConfig {
fn default() -> Self {
Self {
enabled: false,
min_gas_threshold: 100_000,
strategy: "balanced".to_string(),
max_parallel_tasks: 8,
}
}
}
/// Operations that can be offloaded to compute nodes.
#[derive(Clone, Debug)]
pub enum OffloadableOp {
/// Memory operations (bulk copy, fill).
Memory { size: usize },
/// Cryptographic hashing.
Hash { algorithm: String, size: usize },
/// Merkle tree operations.
MerkleProof { depth: usize, leaves: usize },
/// Signature verification batch.
VerifySignatures { count: usize },
/// Storage proof verification.
VerifyStorageProof { size: usize },
/// Custom compute (e.g., ZK proof generation).
Custom { name: String, flops: f64, memory: u64 },
}
impl OffloadableOp {
/// Estimate gas cost for this operation.
pub fn estimated_gas(&self) -> u64 {
match self {
OffloadableOp::Memory { size } => (*size as u64) / 100,
OffloadableOp::Hash { size, .. } => (*size as u64) / 50,
OffloadableOp::MerkleProof { depth, leaves } => {
(*depth as u64) * (*leaves as u64) * 10
}
OffloadableOp::VerifySignatures { count } => (*count as u64) * 5000,
OffloadableOp::VerifyStorageProof { size } => (*size as u64) * 100,
OffloadableOp::Custom { flops, .. } => (*flops as u64) / 1_000_000,
}
}
/// Check if this operation should be offloaded based on gas cost.
pub fn should_offload(&self, config: &ComputeConfig) -> bool {
config.enabled && self.estimated_gas() >= config.min_gas_threshold
}
}
/// Result of compute offload.
#[derive(Clone, Debug)]
pub struct ComputeResult {
/// Whether the operation was offloaded.
pub offloaded: bool,
/// Processor type used (if offloaded).
pub processor_type: Option<String>,
/// Gas savings from offloading.
pub gas_savings: u64,
/// Actual execution time (microseconds).
pub execution_time_us: u64,
}
impl Default for ComputeResult {
fn default() -> Self {
Self {
offloaded: false,
processor_type: None,
gas_savings: 0,
execution_time_us: 0,
}
}
}
/// Compute-aware execution context.
#[cfg(feature = "compute")]
pub struct ComputeContext {
/// Compute cluster connection.
cluster: Option<Arc<ComputeCluster>>,
/// Configuration.
config: ComputeConfig,
/// Pending tasks.
pending_tasks: Vec<TaskId>,
}
#[cfg(feature = "compute")]
impl ComputeContext {
/// Create a new compute context.
pub fn new(config: ComputeConfig) -> Self {
Self {
cluster: None,
config,
pending_tasks: Vec::new(),
}
}
/// Connect to a compute cluster.
pub fn connect(&mut self, cluster: Arc<ComputeCluster>) {
self.cluster = Some(cluster);
}
/// Offload an operation to the compute cluster.
pub fn offload(&mut self, op: OffloadableOp) -> ComputeResult {
if !op.should_offload(&self.config) {
return ComputeResult::default();
}
let cluster = match &self.cluster {
Some(c) => c,
None => return ComputeResult::default(),
};
// Map offloadable op to compute operation
let (operation, priority) = match &op {
OffloadableOp::VerifySignatures { count } => (
Operation::Generic {
op_type: OperationType::MatMul, // Use matmul for batch verification
flops: (*count as f64) * 10_000.0,
memory: (*count as u64) * 96, // 96 bytes per signature
},
TaskPriority::High,
),
OffloadableOp::Hash { size, .. } => (
Operation::Generic {
op_type: OperationType::Sum, // Reduction-like operation
flops: (*size as f64) * 10.0,
memory: *size as u64,
},
TaskPriority::Normal,
),
OffloadableOp::MerkleProof { depth, leaves } => (
Operation::Generic {
op_type: OperationType::Sum,
flops: (*depth as f64) * (*leaves as f64) * 100.0,
memory: (*leaves as u64) * 32,
},
TaskPriority::Normal,
),
OffloadableOp::Custom { flops, memory, .. } => (
Operation::Generic {
op_type: OperationType::MatMul,
flops: *flops,
memory: *memory,
},
TaskPriority::Normal,
),
_ => return ComputeResult::default(),
};
// Create and submit task
let task = Task {
id: TaskId::new(),
operation,
priority,
dependencies: vec![],
status: TaskStatus::Pending,
deadline: None,
};
self.pending_tasks.push(task.id);
ComputeResult {
offloaded: true,
processor_type: Some("cpu".to_string()), // Will be determined by scheduler
gas_savings: op.estimated_gas() / 2, // 50% gas savings for offloaded ops
execution_time_us: 0,
}
}
/// Get number of pending tasks.
pub fn pending_count(&self) -> usize {
self.pending_tasks.len()
}
/// Wait for all pending tasks to complete.
pub async fn wait_all(&mut self) {
self.pending_tasks.clear();
}
}
/// Stub implementation when compute feature is disabled.
#[cfg(not(feature = "compute"))]
pub struct ComputeContext {
config: ComputeConfig,
}
#[cfg(not(feature = "compute"))]
impl ComputeContext {
pub fn new(config: ComputeConfig) -> Self {
Self { config }
}
pub fn offload(&mut self, op: OffloadableOp) -> ComputeResult {
ComputeResult::default()
}
pub fn pending_count(&self) -> usize {
0
}
pub async fn wait_all(&mut self) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offloadable_op_gas() {
let sig_verify = OffloadableOp::VerifySignatures { count: 100 };
assert_eq!(sig_verify.estimated_gas(), 500_000);
let hash = OffloadableOp::Hash {
algorithm: "blake3".to_string(),
size: 1_000_000,
};
assert_eq!(hash.estimated_gas(), 20_000);
}
#[test]
fn test_should_offload() {
let config = ComputeConfig {
enabled: true,
min_gas_threshold: 100_000,
..Default::default()
};
let small_op = OffloadableOp::Hash {
algorithm: "blake3".to_string(),
size: 1000,
};
assert!(!small_op.should_offload(&config));
let large_op = OffloadableOp::VerifySignatures { count: 100 };
assert!(large_op.should_offload(&config));
}
#[test]
fn test_compute_context_disabled() {
let config = ComputeConfig {
enabled: false,
..Default::default()
};
let mut ctx = ComputeContext::new(config);
let op = OffloadableOp::VerifySignatures { count: 100 };
let result = ctx.offload(op);
assert!(!result.offloaded);
}
}

View file

@ -37,6 +37,7 @@
#![allow(dead_code)]
pub mod compression;
pub mod compute;
pub mod context;
pub mod engine;
pub mod gas;

240
docker-compose.compute.yml Normal file
View file

@ -0,0 +1,240 @@
# Synor Compute Layer - Docker Compose
# Heterogeneous compute orchestration for AI/ML workloads
version: '3.9'
services:
# Compute Orchestrator (schedules tasks across workers)
compute-orchestrator:
build:
context: .
dockerfile: docker/compute-node/Dockerfile
container_name: synor-compute-orchestrator
hostname: compute-orchestrator
restart: unless-stopped
environment:
- RUST_LOG=info
- NODE_TYPE=orchestrator
- LISTEN_ADDR=0.0.0.0:17200
- WORKER_PORTS=17210,17211,17212,17213
- BALANCING_STRATEGY=balanced
- MAX_QUEUE_SIZE=10000
ports:
- "17250:17200" # Compute API
- "17252:17202" # Metrics/Health
networks:
- synor-compute-net
volumes:
- compute-orchestrator-data:/data/compute
depends_on:
- compute-worker-cpu-1
- compute-worker-cpu-2
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:17202/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
# CPU Worker Node 1 (x86-64 AVX2)
compute-worker-cpu-1:
build:
context: .
dockerfile: docker/compute-node/Dockerfile
container_name: synor-compute-worker-cpu-1
hostname: compute-worker-cpu-1
restart: unless-stopped
environment:
- RUST_LOG=info
- NODE_TYPE=worker
- PROCESSOR_TYPE=cpu
- CPU_VARIANT=x86_64_avx2
- LISTEN_ADDR=0.0.0.0:17210
- ORCHESTRATOR_URL=http://compute-orchestrator:17200
- MAX_CONCURRENT_TASKS=8
- WORK_STEAL_ENABLED=true
ports:
- "17260:17210" # Worker API
networks:
- synor-compute-net
volumes:
- compute-worker-cpu-1-data:/data/compute
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:17210/health"]
interval: 30s
timeout: 5s
retries: 3
# CPU Worker Node 2 (x86-64 AVX2)
compute-worker-cpu-2:
build:
context: .
dockerfile: docker/compute-node/Dockerfile
container_name: synor-compute-worker-cpu-2
hostname: compute-worker-cpu-2
restart: unless-stopped
environment:
- RUST_LOG=info
- NODE_TYPE=worker
- PROCESSOR_TYPE=cpu
- CPU_VARIANT=x86_64_avx2
- LISTEN_ADDR=0.0.0.0:17211
- ORCHESTRATOR_URL=http://compute-orchestrator:17200
- MAX_CONCURRENT_TASKS=8
- WORK_STEAL_ENABLED=true
ports:
- "17261:17211" # Worker API
networks:
- synor-compute-net
volumes:
- compute-worker-cpu-2-data:/data/compute
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:17211/health"]
interval: 30s
timeout: 5s
retries: 3
# WASM Worker (browser-compatible compute)
compute-worker-wasm:
build:
context: .
dockerfile: docker/compute-node/Dockerfile
container_name: synor-compute-worker-wasm
hostname: compute-worker-wasm
restart: unless-stopped
environment:
- RUST_LOG=info
- NODE_TYPE=worker
- PROCESSOR_TYPE=wasm
- LISTEN_ADDR=0.0.0.0:17212
- ORCHESTRATOR_URL=http://compute-orchestrator:17200
- MAX_CONCURRENT_TASKS=4
ports:
- "17262:17212" # Worker API
networks:
- synor-compute-net
volumes:
- compute-worker-wasm-data:/data/compute
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:17212/health"]
interval: 30s
timeout: 5s
retries: 3
# Spot Market Service
compute-spot-market:
build:
context: .
dockerfile: docker/compute-node/Dockerfile
container_name: synor-compute-spot-market
hostname: compute-spot-market
restart: unless-stopped
environment:
- RUST_LOG=info
- NODE_TYPE=market
- LISTEN_ADDR=0.0.0.0:17220
- ORCHESTRATOR_URL=http://compute-orchestrator:17200
- AUCTION_INTERVAL_MS=5000
- MIN_BID_MICRO=100
ports:
- "17270:17220" # Market API
networks:
- synor-compute-net
volumes:
- compute-spot-market-data:/data/compute
depends_on:
- compute-orchestrator
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:17220/health"]
interval: 30s
timeout: 5s
retries: 3
# Redis for task queue and caching
compute-redis:
image: redis:7-alpine
container_name: synor-compute-redis
hostname: compute-redis
restart: unless-stopped
command: redis-server --appendonly yes --maxmemory 1gb --maxmemory-policy allkeys-lru
ports:
- "17280:6379" # Redis port (remapped)
networks:
- synor-compute-net
volumes:
- compute-redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 15s
timeout: 5s
retries: 3
# Prometheus metrics
compute-prometheus:
image: prom/prometheus:latest
container_name: synor-compute-prometheus
hostname: compute-prometheus
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.enable-lifecycle'
volumes:
- ./docker/compute-node/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- compute-prometheus-data:/prometheus
ports:
- "17290:9090" # Prometheus UI (remapped)
networks:
- synor-compute-net
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
volumes:
compute-orchestrator-data:
driver: local
compute-worker-cpu-1-data:
driver: local
compute-worker-cpu-2-data:
driver: local
compute-worker-wasm-data:
driver: local
compute-spot-market-data:
driver: local
compute-redis-data:
driver: local
compute-prometheus-data:
driver: local
networks:
synor-compute-net:
driver: bridge
ipam:
config:
- subnet: 172.23.0.0/16

View file

@ -0,0 +1,87 @@
# Synor Compute Node Dockerfile
# Heterogeneous compute orchestration for CPU/GPU/TPU/NPU/LPU
# =============================================================================
# Stage 1: Build Environment
# =============================================================================
FROM rust:1.85-bookworm AS builder
# Install build dependencies
RUN apt-get update && apt-get install -y \
cmake \
clang \
libclang-dev \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# Create app directory
WORKDIR /app
# Copy manifests first (for better caching)
COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/
COPY apps/ apps/
COPY contracts/ contracts/
COPY sdk/ sdk/
# Build release binary for compute node
RUN cargo build --release -p synor-compute
# =============================================================================
# Stage 2: Runtime Environment
# =============================================================================
FROM debian:bookworm-slim AS runtime
# Install runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user for security
RUN useradd --create-home --shell /bin/bash synor
# Create data directories
RUN mkdir -p /data/compute && chown -R synor:synor /data
# Note: synor-compute is a library, not binary
# The container serves as a compute worker runtime
# Create a simple entrypoint script
RUN echo '#!/bin/bash\n\
echo "Synor Compute Node v0.1.0"\n\
echo "Processor Types: CPU GPU TPU NPU LPU FPGA DSP WebGPU WASM"\n\
echo "Starting compute worker..."\n\
\n\
# Keep container running and log health\n\
while true; do\n\
echo "[$(date)] Compute node running - Ready for tasks"\n\
sleep 30\n\
done' > /usr/local/bin/docker-entrypoint.sh && \
chmod +x /usr/local/bin/docker-entrypoint.sh
# Switch to non-root user
USER synor
# Set working directory
WORKDIR /home/synor
# Expose ports
# Compute API
EXPOSE 17200
# Worker communication
EXPOSE 17201
# Metrics
EXPOSE 17202
# Data volume
VOLUME ["/data/compute"]
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:17202/health || exit 0
# Default command
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

View file

@ -0,0 +1,54 @@
# Prometheus configuration for Synor Compute Layer
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: []
rule_files: []
scrape_configs:
# Prometheus self-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Compute Orchestrator
- job_name: 'compute-orchestrator'
static_configs:
- targets: ['compute-orchestrator:17202']
metrics_path: '/metrics'
scrape_interval: 10s
# CPU Workers
- job_name: 'compute-workers-cpu'
static_configs:
- targets:
- 'compute-worker-cpu-1:17210'
- 'compute-worker-cpu-2:17211'
metrics_path: '/metrics'
scrape_interval: 10s
# WASM Worker
- job_name: 'compute-workers-wasm'
static_configs:
- targets: ['compute-worker-wasm:17212']
metrics_path: '/metrics'
scrape_interval: 10s
# Spot Market
- job_name: 'compute-spot-market'
static_configs:
- targets: ['compute-spot-market:17220']
metrics_path: '/metrics'
scrape_interval: 10s
# Redis
- job_name: 'redis'
static_configs:
- targets: ['compute-redis:6379']
metrics_path: '/metrics'
scrape_interval: 30s