Skip to main content

ADR-016: CODI Rust Implementation Architecture (v4) - Part 2: Technical Implementation

Document Specification Block

Document: ADR-016-v4-codi-rust-implementation-part2
Version: 1.0.0
Purpose: Technical implementation blueprint for CODI Rust binary
Audience: AI Agents, Rust Developers, DevOps Engineers
Date Created: 2025-08-29
Date Modified: 2025-08-29
Date Released: DRAFT
QA Reviewed: Pending
Status: DRAFT

Table of Contents

8. Implementation Blueprint

8.1 Architecture Diagram

8.2 Dependencies

[package]
name = "codi"
version = "1.0.0"
edition = "2021"

[dependencies]
# CLI Framework
clap = { version = "4.5", features = ["derive", "color", "suggestions"] }

# Async Runtime
tokio = { version = "1.35", features = ["full"] }

# Web Server
actix-web = "4.5"
actix-ws = "0.2"

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

# Database
rusqlite = { version = "0.31", features = ["bundled"] }
foundationdb = "0.9"

# File Watching
notify = "6.1"

# Utilities
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1.6", features = ["v4", "serde"] }
anyhow = "1.0"
colored = "2.1"

# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["json"] }

[profile.release]
lto = true
opt-level = 3
strip = true

8.3 Core Implementation

// src/main.rs
use clap::{Parser, Subcommand};
use anyhow::Result;

#[derive(Parser)]
#[command(name = "codi", version, about)]
struct Cli {
#[command(subcommand)]
command: Commands,

#[arg(short, long, global = true)]
verbose: bool,
}

#[derive(Subcommand)]
enum Commands {
Log {
message: String,
#[arg(short, long, default_value = "INFO")]
action: String,
},
Status {
#[arg(short, long)]
detailed: bool,
},
Monitor {
#[arg(short, long)]
actor: Option<String>,
},
Serve {
#[arg(short, long, default_value = "8081")]
port: u16,
},
}

#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();

// Initialize services
let config = config::load()?;
let db = database::init(&config)?;
let logger = logging::init(&config)?;

match cli.command {
Commands::Log { message, action } => {
commands::log::execute(&db, &logger, &message, &action).await?;
}
Commands::Status { detailed } => {
commands::status::execute(&db, detailed).await?;
}
Commands::Monitor { actor } => {
commands::monitor::execute(&db, actor).await?;
}
Commands::Serve { port } => {
server::start(port, db).await?;
}
}

Ok(())
}

8.4 API Specification

// src/commands/log.rs
use crate::database::Database;
use crate::logging::Logger;
use anyhow::Result;
use chrono::Utc;
use serde_json::json;

pub async fn execute(
db: &Database,
logger: &Logger,
message: &str,
action: &str,
) -> Result<()> {
let entry = LogEntry {
timestamp: Utc::now(),
session_id: get_session_id(),
actor: Actor {
id: "codi-rust",
type_: "tool",
},
action: action.to_string(),
message: message.to_string(),
};

// Write to SQLite
db.insert_log(&entry).await?;

// Write to file
logger.log(&entry).await?;

// Sync to FoundationDB if connected
if let Some(fdb) = &db.fdb_connection {
fdb.sync_log(&entry).await?;
}

println!("✅ Logged to codi-ps.log");
Ok(())
}

8.5 Data Models

// src/models/mod.rs
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize)]
pub struct LogEntry {
pub timestamp: DateTime<Utc>,
pub session_id: String,
pub actor: Actor,
pub action: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Actor {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Session {
pub id: Uuid,
pub started_at: DateTime<Utc>,
pub ended_at: Option<DateTime<Utc>>,
pub description: Option<String>,
}

8.6 Configuration

// src/config.rs
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Debug, Serialize, Deserialize)]
pub struct CodiConfig {
pub log_path: PathBuf,
pub db_path: PathBuf,
pub fdb_cluster_file: Option<PathBuf>,
pub session_timeout: u64,
pub export_directory: PathBuf,
}

impl Default for CodiConfig {
fn default() -> Self {
Self {
log_path: dirs::home_dir()
.unwrap()
.join(".codi/logs/codi-ps.log"),
db_path: dirs::home_dir()
.unwrap()
.join(".codi/codi.db"),
fdb_cluster_file: None,
session_timeout: 3600,
export_directory: dirs::home_dir()
.unwrap()
.join(".codi/exports"),
}
}
}

8.7 Logging Requirements

// src/logging/mod.rs
use tracing::{info, warn, error};
use tracing_subscriber::fmt::format::FmtSpan;

pub fn init(config: &CodiConfig) -> Result<Logger> {
let file_appender = tracing_appender::rolling::daily(
&config.log_path.parent().unwrap(),
"codi.log"
);

let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);

tracing_subscriber::fmt()
.with_writer(non_blocking)
.with_span_events(FmtSpan::CLOSE)
.json()
.init();

Ok(Logger { config: config.clone() })
}

// Standard logging pattern
pub fn log_operation<T>(
component: &str,
action: &str,
user_id: Option<&str>,
result: Result<T>,
) -> Result<T> {
match result {
Ok(value) => {
info!(
component = component,
action = action,
user_id = user_id,
"Operation successful"
);
Ok(value)
}
Err(e) => {
error!(
component = component,
action = action,
user_id = user_id,
error = %e,
"Operation failed"
);
Err(e)
}
}
}

8.8 Error Handling

// src/error.rs
use thiserror::Error;

#[derive(Error, Debug)]
pub enum CodiError {
#[error("Database error: {0}")]
Database(#[from] rusqlite::Error),

#[error("FoundationDB error: {0}")]
FoundationDB(#[from] foundationdb::FdbError),

#[error("IO error: {0}")]
Io(#[from] std::io::Error),

#[error("Configuration error: {0}")]
Config(String),

#[error("Session not found: {0}")]
SessionNotFound(String),
}

// User-friendly error messages
impl CodiError {
pub fn user_message(&self) -> String {
match self {
Self::Database(_) => "Unable to access local database. Try 'codi repair'".into(),
Self::FoundationDB(_) => "Cannot connect to CODITECT server. Check your connection".into(),
Self::Io(_) => "File system error. Check permissions".into(),
Self::Config(msg) => format!("Configuration issue: {}", msg),
Self::SessionNotFound(id) => format!("Session '{}' not found. Use 'codi session list'", id),
}
}
}

9. Testing Strategy

9.1 Unit Tests

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_log_entry_serialization() {
let entry = LogEntry {
timestamp: Utc::now(),
session_id: "test-session".into(),
actor: Actor {
id: "test".into(),
type_: "test".into(),
},
action: "TEST".into(),
message: "Test message".into(),
metadata: None,
};

let json = serde_json::to_string(&entry).unwrap();
let decoded: LogEntry = serde_json::from_str(&json).unwrap();
assert_eq!(entry.session_id, decoded.session_id);
}
}

9.2 Integration Tests

#[tokio::test]
async fn test_log_command() {
let temp_dir = tempdir::TempDir::new("codi-test").unwrap();
let config = CodiConfig {
db_path: temp_dir.path().join("test.db"),
..Default::default()
};

let db = Database::init(&config).await.unwrap();
let logger = Logger::new(&config);

commands::log::execute(&db, &logger, "test", "TEST").await.unwrap();

let logs = db.get_recent_logs(1).await.unwrap();
assert_eq!(logs.len(), 1);
assert_eq!(logs[0].message, "test");
}

9.3 Test Coverage Requirements

  • Unit Test Coverage: ≥ 90%
  • Integration Test Coverage: ≥ 80%
  • Critical Path Coverage: 100%
  • Error Path Coverage: 100%

10. Security Considerations

10.1 Authentication & Authorization

  • Local SQLite database is user-readable only (0600)
  • FoundationDB connection uses secure cluster file
  • Web server binds to localhost by default
  • MCP server requires authentication tokens

10.2 Data Protection

  • Logs sanitized to prevent injection
  • No sensitive data in error messages
  • Secure temporary file handling
  • Input validation on all commands

11. Performance Characteristics

11.1 Expected Metrics

MetricTargetMeasurement
Startup Time< 50msTime to first prompt
Log Command< 10msComplete execution
Memory Usage< 100MBRSS at idle
Binary Size< 20MBStripped release build

12. Operational Considerations

12.1 Deployment

# Build release binary
cargo build --release

# Install globally
sudo cp target/release/codi /usr/local/bin/

# Or user-local
cp target/release/codi ~/.local/bin/

12.2 Monitoring

  • Built-in metrics via codi status --metrics
  • Prometheus endpoint at /metrics when server running
  • Log rotation handled automatically

13. Migration Strategy

13.1 Migration Steps

  • Step 1: Create Rust project structure (Day 1)
  • Step 2: Implement core commands (Days 2-5)
  • Step 3: Add monitoring features (Days 6-8)
  • Step 4: Implement web server (Days 9-11)
  • Step 5: Add MCP support (Days 12-14)
  • Step 6: Testing and optimization (Days 15-18)
  • Step 7: Gradual rollout (Days 19-20)

13.2 Compatibility Matrix

ComponentBackward CompatibleMigration Required
Log format✅ YesNo
Commands✅ Yes (aliases)Optional
Config⚠️ New formatAuto-migration

↑ Back to Top