12 KiB
12 KiB
Daemon-CLI Phases 3-7 Implementation Roadmap
Current Status: Phases 1-2 Complete ✅
All foundation is in place. The API server is running with stub implementations ready for ecosystem crate integration.
Phase 3: Dual-Mode CLI Client
Files to Create/Modify
src/cli/mod.rs- CLI module structuresrc/cli/client.rs- HTTP client for daemon communicationsrc/cli/offline.rs- Direct library call modesrc/cli/output.rs- Output formatting (table, JSON, YAML)src/cli/commands/daemon.rs- Daemon management commandssrc/cli/commands/valida.rs- Validation commandssrc/cli/commands/runtime.rs- Runtime commandssrc/cli/commands/encrypt.rs- Encryption commandssrc/bin/provctl.rs- Update with real implementations
Implementation Pattern
// HTTP Mode: Connect to daemon
async fn execute_validation(args: ValidaArgs) -> Result<()> {
let client = DaemonClient::new("http://localhost:9090")?;
let result = client.valida().validate(&args).await?;
print_result(result)?;
Ok(())
}
// Offline Mode: Direct library calls
async fn execute_validation_offline(args: ValidaArgs) -> Result<()> {
use valida::RuleSet;
let rules = RuleSet::load_from_file(&args.rules_file)?;
let result = rules.validate(&args.config, args.phase)?;
print_result(result)?;
Ok(())
}
// Auto-detection
async fn execute(cmd: Command) -> Result<()> {
if DaemonClient::check_available().await {
execute_with_daemon(cmd).await
} else {
execute_offline(cmd).await
}
}
Key Features
- HTTP client using reqwest
- Daemon availability detection via health check
- Graceful fallback to offline mode
- Consistent CLI interface regardless of mode
- JSON output for scripting
Phase 4: Ecosystem Crate Integration
Files to Create/Modify
src/orchestration/mod.rs- Operation registrysrc/orchestration/operation.rs- Operation traitsrc/orchestration/valida.rs- Validation operationssrc/orchestration/runtime.rs- Runtime operationssrc/orchestration/encrypt.rs- Encryption operationssrc/orchestration/init_servs.rs- Service managementsrc/api/handlers/valida.rs- Replace stubssrc/api/handlers/runtime.rs- Replace stubssrc/api/handlers/encrypt.rs- Replace stubs
Pattern Example: Valida Integration
// src/orchestration/valida.rs
#[async_trait]
impl EcosystemOperation for ValidaOperation {
async fn execute(&self, params: Value) -> Result<OperationResult> {
let config_file = params["config_file"].as_str()?;
let phase = params.get("phase").and_then(|p| p.as_str()).unwrap_or("deploy");
// Call valida crate
let rules = valida::RuleSet::from_file(config_file)?;
let validation_result = rules.validate_with_phase(config_file, phase)?;
// Cache result
let cache_key = format!("valida:{}:{}", config_file, phase);
self.cache.set_command(cache_key, serde_json::to_string(&validation_result)?).await?;
Ok(OperationResult {
status: if validation_result.passed { "success" } else { "failed" },
data: validation_result,
})
}
}
// src/api/handlers/valida.rs
pub async fn validate_config(
State(state): State<AppState>,
Json(req): Json<ValidateRequest>,
) -> impl IntoResponse {
let operation = ValidaOperation::new(&state.cache);
let params = json!({
"config_file": req.config_file,
"phase": req.phase,
});
match operation.execute(params).await {
Ok(result) => (StatusCode::OK, Json(result)).into_response(),
Err(e) => error_response(e),
}
}
Crate-by-Crate Integration
Valida
[dependencies]
valida = { path = "../valida", optional = true }
[features]
valida = ["dep:valida"]
Validate configurations and emit validation events.
Runtime
[dependencies]
runtime = { path = "../runtime", optional = true }
Detect container runtime, cache results.
Encrypt
[dependencies]
encrypt = { path = "../encrypt", optional = true }
Support multiple encryption backends (SOPS, KMS, etc.).
Init-Servs
[dependencies]
init-servs = { path = "../init-servs", optional = true }
Service installation and lifecycle management.
Observability
[dependencies]
observability = { path = "../observability", optional = true }
Health checks and metrics collection.
Phase 5: Event Bus & Syntaxis Integration
Files to Create
src/events/mod.rs- Event bus coresrc/events/types.rs- Event definitionssrc/events/bus.rs- Event publishing/subscriptionsrc/events/publisher.rs- Broadcast sendersrc/events/subscriber.rs- Broadcast receiversrc/orchestration/syntaxis.rs- Syntaxis event handler
Event Types
#[derive(Debug, Clone, Serialize)]
pub enum EcosystemEvent {
ValidationCompleted {
rule_id: String,
passed: bool,
timestamp: DateTime<Utc>,
},
RuntimeDetected {
runtime: String,
version: String,
timestamp: DateTime<Utc>,
},
EncryptionCompleted {
backend: String,
success: bool,
timestamp: DateTime<Utc>,
},
ServiceInstalled {
service_name: String,
timestamp: DateTime<Utc>,
},
// ... more events
}
Integration Pattern
// Publish event after operation
pub async fn validate_config(
State(state): State<AppState>,
Json(req): Json<ValidateRequest>,
) -> impl IntoResponse {
let operation = ValidaOperation::new(&state.cache);
let result = operation.execute(params).await?;
// Publish event
state.event_bus.publish(EcosystemEvent::ValidationCompleted {
passed: result.passed,
timestamp: Utc::now(),
}).await;
(StatusCode::OK, Json(result))
}
// Subscribe to events in Syntaxis handler
pub async fn handle_validation_event(event: ValidationCompleted) {
if !event.passed {
// Create task in Syntaxis
syntaxis::create_task(Task {
title: format!("Validation failed: {}", event.rule_id),
phase: "devel",
status: "blocked",
}).await;
}
}
Phase 6: Observability & GitOps Integration
Files to Create/Modify
src/integration/mod.rs- Integration modulesrc/integration/health_server.rs- Health server integrationsrc/integration/gitops_webhooks.rs- GitOps webhook integration
Health Server Integration
pub fn create_health_router() -> Router {
Router::new()
.route("/livez", get(liveness))
.route("/readyz", get(readiness))
.route("/healthz", get(startup))
}
// Integrate into main router
let app = Router::new()
.nest("/api/v1", api_routes(state))
.nest("/health", health_routes(state));
GitOps Webhook Integration
pub async fn handle_git_webhook(
State(state): State<AppState>,
Json(event): Json<GitEvent>,
) -> impl IntoResponse {
// Publish to event bus
state.event_bus.publish(EcosystemEvent::GitEventReceived {
event_type: event.event_type,
timestamp: Utc::now(),
}).await;
(StatusCode::ACCEPTED, Json(json!({"status": "received"})))
}
Phase 7: Configuration Rendering & Nushell Integration
Files to Create
src/rendering/mod.rs- Rendering modulesrc/rendering/tera.rs- Tera template enginesrc/rendering/kcl.rs- KCL renderer (optional feature)src/rendering/nickel.rs- Nickel renderer (optional feature)src/rendering/fluent.rs- Fluent i18n (optional feature)scripts/ecosystem_env.nu- Pre-loaded Nushell environmentscripts/daemon/valida_daemon.nu- Valida functionsscripts/daemon/runtime_daemon.nu- Runtime functions- etc.
Tera Template Rendering
pub async fn render_template(
State(state): State<AppState>,
Json(req): Json<RenderRequest>,
) -> impl IntoResponse {
// Check cache
if let Some(cached) = state.cache.get_config(&req.template).await {
return (StatusCode::OK, Json(json!({"result": cached})));
}
// Render
let mut tera = Tera::new("templates/**/*.tera")?;
let result = tera.render(&req.template, &req.context)?;
// Cache
state.cache.set_config(req.template, result.clone()).await?;
(StatusCode::OK, Json(json!({"result": result})))
}
Nushell Integration
# scripts/ecosystem_env.nu
# Pre-loaded functions for fast execution
export def valida-validate [file: string, phase: string = "deploy"] {
http post "http://localhost:9090/api/v1/valida/validate" {
config_file: $file,
phase: $phase
}
}
export def runtime-detect [] {
http get "http://localhost:9090/api/v1/runtime/detect"
}
export def cache-stats [] {
http get "http://localhost:9090/api/v1/cache/stats"
}
Feature Gating
[features]
kcl = []
nickel = []
tera-default = []
fluent-default = []
[dependencies]
tera = { version = "1.20", optional = true }
fluent = { version = "0.17", optional = true }
Implementation Order & Dependencies
Phase 4: Ecosystem Crates ◄─── Phase 3: CLI Client
▼
Phase 5: Event Bus ◄───────── Phase 4: Crate Integration
▼
Phase 6: Health/GitOps ◄───── Phase 5: Events
▼
Phase 7: Rendering/Nushell ◄─ Phase 6: Integration
Recommended Implementation Sequence
- Phase 4 First: Implement crate integration so API handlers work
- Phase 3 Second: Implement CLI that calls Phase 4 operations
- Phase 5 Third: Add event bus for coordination
- Phase 6 Fourth: Integrate with existing services
- Phase 7 Last: Add advanced features (rendering, Nushell)
Testing Strategy for Phases 3-7
Phase 3: CLI Testing
# Test daemon mode
provctl daemon status
provctl valida validate --file test.yaml
# Test offline mode (daemon down)
PROV_OFFLINE=1 provctl valida validate --file test.yaml
# Test JSON output
provctl valida validate --file test.yaml --json
Phase 4: Crate Integration Testing
# Test each integrated crate
curl -X POST http://localhost:9090/api/v1/valida/validate \
-H "Content-Type: application/json" \
-d '{"config_file": "test.yaml", "phase": "deploy"}'
curl http://localhost:9090/api/v1/runtime/detect
curl -X POST http://localhost:9090/api/v1/encrypt/encrypt \
-H "Content-Type: application/json" \
-d '{"file": "secrets.env", "backend": "sops"}'
Phase 5: Event Testing
# Subscribe to events in separate terminal
provctl events subscribe --type validation_completed
# Trigger operation to emit events
provctl valida validate --file test.yaml
# Watch for events in first terminal
Phase 6 & 7: Integration Testing
# Health checks
curl http://localhost:9090/health/livez
curl http://localhost:9090/health/readyz
# Rendering
curl -X POST http://localhost:9090/api/v1/render/tera \
-H "Content-Type: application/json" \
-d '{"template": "config.tera", "context": {}}'
# Nushell functions
nu -c "
source scripts/ecosystem_env.nu
valida-validate 'test.yaml' 'deploy'
"
Success Criteria
Phase 3 ✓
provctl daemon statusshows daemon statusprovctl valida validateworks in both modes- Graceful fallback when daemon unavailable
- JSON output works correctly
Phase 4 ✓
- All ecosystem crates integrated
- Real validation, runtime detection, encryption working
- 80%+ cache hit ratio
- <100ms end-to-end execution
Phase 5 ✓
- Events publish/subscribe working
- Syntaxis tasks created from events
- 10000+ events/sec throughput
- Optional persistence layer working
Phase 6 ✓
- Health probes respond correctly
- GitOps webhooks handled
- Unified logging across all services
- Metrics collected and accessible
Phase 7 ✓
- KCL/Nickel/Tera rendering works
- Fluent i18n translation working
- Nushell functions execute <5ms
- Scripts integrate with daemon
Conclusion
The roadmap is clear. Phases 1-2 provide a rock-solid foundation. Phases 3-7 build incrementally on that, each phase being independent enough to implement in parallel but dependent on Phase 1-2 as the base.
Key principles:
- ✅ Progressive enhancement
- ✅ No breaking changes
- ✅ Each phase is self-contained
- ✅ Feature-gated for flexibility
- ✅ Comprehensive testing at each stage
Ready to begin implementation when you are! 🚀