1320 lines
42 KiB
Markdown
1320 lines
42 KiB
Markdown
# Portfolio IA: Especificaciones Técnicas para Desarrolladores
|
|
|
|
## Arquitectura del Ecosistema
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ CAPA DE PRESENTACIÓN │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ Leptos WASM (Vapora UI) │ Ratatui TUI │ Axum REST │ CLI (clap) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ CAPA DE ORQUESTACIÓN │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ Vapora Coordinator │ TypeDialog Backends │ Provisioning Orchestrator │
|
|
│ (NATS JetStream) │ (BackendFactory) │ (Rust/Nushell hybrid) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ CAPA DE CONOCIMIENTO │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ Kogral Knowledge Graph │ Vapora Learning Profiles │ Provisioning RAG │
|
|
│ (6 node types) │ (expertise + recency) │ (1200+ docs) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ CAPA DE PERSISTENCIA │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ SurrealDB (multi-tenant scopes) │ Filesystem (git-native markdown) │
|
|
│ NATS JetStream (mensajería) │ Redis (vector stores opcionales) │
|
|
│ etcd (SecretumVault HA) │ PostgreSQL (vault enterprise) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Vapora: Especificaciones
|
|
|
|
### Workspace Structure
|
|
|
|
```text
|
|
crates/
|
|
├── vapora-shared/ # Core: models, errors, types
|
|
├── vapora-backend/ # Axum REST API (40+ endpoints)
|
|
├── vapora-agents/ # Agent orchestration + learning
|
|
├── vapora-llm-router/ # Multi-provider routing + budget
|
|
├── vapora-swarm/ # Swarm coordination + metrics
|
|
├── vapora-knowledge-graph/# Temporal KG + learning curves
|
|
├── vapora-frontend/ # Leptos WASM UI
|
|
├── vapora-mcp-server/ # MCP protocol gateway
|
|
├── vapora-tracking/ # Task/project storage
|
|
├── vapora-telemetry/ # OpenTelemetry integration
|
|
├── vapora-analytics/ # Event pipeline
|
|
├── vapora-worktree/ # Git worktree management
|
|
└── vapora-doc-lifecycle/ # Documentation management
|
|
```
|
|
|
|
### Core Types
|
|
|
|
```rust
|
|
// vapora-shared/src/models.rs
|
|
pub struct Agent {
|
|
pub id: String,
|
|
pub role: AgentRole, // 12 roles disponibles
|
|
pub status: AgentStatus, // Ready | Busy | Offline
|
|
pub provider: LLMProvider, // Claude | OpenAI | Gemini | Ollama
|
|
pub last_heartbeat: DateTime<Utc>,
|
|
}
|
|
|
|
pub enum AgentRole {
|
|
Architect, Developer, CodeReviewer, Tester,
|
|
Documenter, Marketer, Presenter, DevOps,
|
|
Monitor, Security, ProjectManager, DecisionMaker,
|
|
}
|
|
|
|
// vapora-agents/src/learning_profile.rs
|
|
pub struct ExpertiseProfile {
|
|
pub task_type: String,
|
|
pub success_rate: f64,
|
|
pub avg_duration: Duration,
|
|
pub execution_count: u32,
|
|
pub recent_weight: f64, // 3x for last 7 days
|
|
pub confidence: f64, // prevents overfitting on small samples
|
|
}
|
|
|
|
// Scoring formula
|
|
fn calculate_score(load: f64, expertise: f64, confidence: f64) -> f64 {
|
|
0.3 * load + 0.5 * expertise + 0.2 * confidence
|
|
}
|
|
```
|
|
|
|
### LLM Router Configuration
|
|
|
|
```rust
|
|
// vapora-llm-router/src/config.rs
|
|
pub struct RoutingRule {
|
|
pub pattern: String, // regex para task type
|
|
pub provider: LLMProvider,
|
|
pub model: String,
|
|
pub fallback_chain: Vec<LLMProvider>,
|
|
}
|
|
|
|
pub struct BudgetConfig {
|
|
pub role: AgentRole,
|
|
pub monthly_limit_cents: u32,
|
|
pub weekly_limit_cents: Option<u32>,
|
|
pub enforcement: BudgetEnforcement, // Normal | NearThreshold | Exceeded
|
|
}
|
|
|
|
// Cost tracking per request
|
|
pub struct CostRecord {
|
|
pub provider: LLMProvider,
|
|
pub model: String,
|
|
pub input_tokens: u32,
|
|
pub output_tokens: u32,
|
|
pub cost_cents: f64,
|
|
pub task_type: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
```
|
|
|
|
### API Endpoints (Axum)
|
|
|
|
```rust
|
|
// vapora-backend/src/api/mod.rs
|
|
Router::new()
|
|
// Projects
|
|
.route("/projects", get(list_projects).post(create_project))
|
|
.route("/projects/:id", get(get_project).put(update_project).delete(delete_project))
|
|
|
|
// Tasks
|
|
.route("/tasks", get(list_tasks).post(create_task))
|
|
.route("/tasks/:id/assign", post(assign_to_agent))
|
|
|
|
// Agents
|
|
.route("/agents", get(list_agents))
|
|
.route("/agents/:id/health", get(agent_health))
|
|
.route("/agents/:role/expertise", get(role_expertise))
|
|
|
|
// LLM Router
|
|
.route("/llm/route", post(route_request))
|
|
.route("/llm/budget/:role", get(get_budget).put(set_budget))
|
|
.route("/llm/costs", get(cost_report))
|
|
|
|
// Swarm
|
|
.route("/swarm/assign", post(assign_task))
|
|
.route("/swarm/status", get(swarm_status))
|
|
|
|
// Metrics
|
|
.route("/metrics", get(prometheus_metrics))
|
|
```
|
|
|
|
### NATS Message Types
|
|
|
|
```rust
|
|
// vapora-agents/src/messages.rs
|
|
#[derive(Serialize, Deserialize)]
|
|
pub enum AgentMessage {
|
|
TaskAssignment {
|
|
task_id: String,
|
|
agent_id: String,
|
|
task_type: String,
|
|
payload: serde_json::Value,
|
|
},
|
|
TaskResult {
|
|
task_id: String,
|
|
agent_id: String,
|
|
status: TaskStatus,
|
|
output: Option<String>,
|
|
duration_ms: u64,
|
|
tokens_used: u32,
|
|
},
|
|
Heartbeat {
|
|
agent_id: String,
|
|
status: AgentStatus,
|
|
current_load: f64,
|
|
},
|
|
}
|
|
|
|
// Subjects
|
|
const TASK_ASSIGNMENT: &str = "vapora.tasks.assign";
|
|
const TASK_RESULTS: &str = "vapora.tasks.results";
|
|
const AGENT_HEARTBEAT: &str = "vapora.agents.heartbeat";
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Kogral: Especificaciones
|
|
|
|
### Workspace Structure
|
|
|
|
```
|
|
crates/
|
|
├── kogral-core/ # Core library (models, storage, query)
|
|
├── kogral-cli/ # CLI (13 commands)
|
|
└── kogral-mcp/ # MCP server for Claude Code
|
|
```
|
|
|
|
### Node Types
|
|
|
|
```rust
|
|
// kogral-core/src/models.rs
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum NodeType {
|
|
Note, // General notes
|
|
Decision, // ADRs (Architectural Decision Records)
|
|
Guideline, // Team/org standards
|
|
Pattern, // Reusable solutions
|
|
Journal, // Daily development log
|
|
Execution, // Agent execution records
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum RelationType {
|
|
RelatesTo,
|
|
DependsOn,
|
|
Implements,
|
|
Extends,
|
|
Supersedes,
|
|
Explains,
|
|
}
|
|
|
|
pub struct Node {
|
|
pub id: String,
|
|
pub node_type: NodeType,
|
|
pub title: String,
|
|
pub content: String, // Markdown body
|
|
pub metadata: HashMap<String, String>,
|
|
pub tags: Vec<String>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
}
|
|
|
|
pub struct Edge {
|
|
pub source: String, // Node ID
|
|
pub target: String, // Node ID
|
|
pub relation: RelationType,
|
|
pub weight: f64, // Relationship strength
|
|
}
|
|
```
|
|
|
|
### Storage Backends
|
|
|
|
```rust
|
|
// kogral-core/src/storage/mod.rs
|
|
#[async_trait]
|
|
pub trait Storage: Send + Sync {
|
|
async fn create_node(&self, node: &Node) -> Result<String>;
|
|
async fn get_node(&self, id: &str) -> Result<Option<Node>>;
|
|
async fn update_node(&self, node: &Node) -> Result<()>;
|
|
async fn delete_node(&self, id: &str) -> Result<()>;
|
|
async fn list_nodes(&self, filter: NodeFilter) -> Result<Vec<Node>>;
|
|
|
|
async fn create_edge(&self, edge: &Edge) -> Result<()>;
|
|
async fn get_edges(&self, node_id: &str) -> Result<Vec<Edge>>;
|
|
async fn delete_edge(&self, source: &str, target: &str) -> Result<()>;
|
|
|
|
async fn search(&self, query: &str, limit: usize) -> Result<Vec<Node>>;
|
|
async fn semantic_search(&self, embedding: &[f32], limit: usize) -> Result<Vec<Node>>;
|
|
}
|
|
|
|
// Implementations
|
|
pub struct FilesystemStorage { /* .kogral/ directory */ }
|
|
pub struct SurrealDbStorage { /* SurrealDB connection */ }
|
|
pub struct MemoryStorage { /* DashMap for testing */ }
|
|
```
|
|
|
|
### Embedding Configuration
|
|
|
|
```rust
|
|
// kogral-core/src/embeddings.rs
|
|
pub enum EmbeddingProvider {
|
|
FastEmbed {
|
|
model: String, // "BAAI/bge-small-en-v1.5"
|
|
dimensions: usize, // 384
|
|
},
|
|
OpenAI {
|
|
model: String, // "text-embedding-3-small"
|
|
api_key: String,
|
|
},
|
|
Ollama {
|
|
model: String,
|
|
url: String,
|
|
},
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait Embedder: Send + Sync {
|
|
async fn embed(&self, text: &str) -> Result<Vec<f32>>;
|
|
async fn embed_batch(&self, texts: &[String]) -> Result<Vec<Vec<f32>>>;
|
|
fn dimensions(&self) -> usize;
|
|
}
|
|
```
|
|
|
|
### MCP Server Tools
|
|
|
|
```rust
|
|
// kogral-mcp/src/tools.rs
|
|
pub const MCP_TOOLS: &[Tool] = &[
|
|
Tool {
|
|
name: "search",
|
|
description: "Search knowledge graph by text or semantic similarity",
|
|
parameters: json!({
|
|
"query": { "type": "string" },
|
|
"node_type": { "type": "string", "optional": true },
|
|
"limit": { "type": "integer", "default": 10 }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "add_note",
|
|
description: "Add a new note to the knowledge graph",
|
|
parameters: json!({
|
|
"title": { "type": "string" },
|
|
"content": { "type": "string" },
|
|
"tags": { "type": "array", "items": { "type": "string" } }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "add_decision",
|
|
description: "Record an architectural decision (ADR)",
|
|
parameters: json!({
|
|
"title": { "type": "string" },
|
|
"context": { "type": "string" },
|
|
"decision": { "type": "string" },
|
|
"consequences": { "type": "string" }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "link",
|
|
description: "Create relationship between nodes",
|
|
parameters: json!({
|
|
"source_id": { "type": "string" },
|
|
"target_id": { "type": "string" },
|
|
"relation": { "type": "string", "enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"] }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "get_guidelines",
|
|
description: "Get applicable guidelines for a topic",
|
|
parameters: json!({
|
|
"topic": { "type": "string" },
|
|
"include_shared": { "type": "boolean", "default": true }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "list_graphs",
|
|
description: "List available knowledge graphs",
|
|
parameters: json!({}),
|
|
},
|
|
Tool {
|
|
name: "export",
|
|
description: "Export knowledge graph to format",
|
|
parameters: json!({
|
|
"format": { "type": "string", "enum": ["markdown", "json", "yaml"] },
|
|
"filter": { "type": "object", "optional": true }
|
|
}),
|
|
},
|
|
];
|
|
```
|
|
|
|
### CLI Commands
|
|
|
|
```bash
|
|
# kogral-cli commands
|
|
kogral init # Initialize .kogral/ directory
|
|
kogral add note <title> # Add note interactively
|
|
kogral add decision <title> # Add ADR with guided prompts
|
|
kogral search <query> # Text search
|
|
kogral search --semantic <q> # Semantic search
|
|
kogral link <src> <dst> <rel> # Create relationship
|
|
kogral list [--type <type>] # List nodes
|
|
kogral show <id> # Display node details
|
|
kogral delete <id> # Remove node
|
|
kogral graph # Visualize graph (DOT format)
|
|
kogral sync # Sync filesystem ↔ SurrealDB
|
|
kogral serve # Start MCP server
|
|
kogral import <path> # Import from Logseq/markdown
|
|
kogral export <format> # Export to markdown/json
|
|
kogral config # Show/edit configuration
|
|
```
|
|
|
|
---
|
|
|
|
## 3. TypeDialog: Especificaciones
|
|
|
|
### Workspace Structure
|
|
|
|
```text
|
|
crates/
|
|
├── typedialog-core/ # Core (forms, backends, validation)
|
|
├── typedialog/ # CLI binary
|
|
├── typedialog-tui/ # TUI binary (ratatui)
|
|
├── typedialog-web/ # Web binary (axum)
|
|
├── typedialog-ai/ # AI backend (RAG, embeddings)
|
|
├── typedialog-agent/
|
|
│ ├── typedialog-ag-core/ # Agent runtime
|
|
│ └── typedialog-ag/ # Agent CLI
|
|
└── typedialog-prov-gen/ # IaC generation
|
|
```
|
|
|
|
### Form Definition (TOML)
|
|
|
|
```toml
|
|
# employee_onboarding.toml
|
|
[form]
|
|
id = "employee_onboarding"
|
|
version = "1.0.0"
|
|
title = "Employee Onboarding"
|
|
description = "New employee registration form"
|
|
|
|
[[sections]]
|
|
id = "personal"
|
|
title = "Personal Information"
|
|
|
|
[[sections.fields]]
|
|
id = "full_name"
|
|
type = "text"
|
|
label = "Full Name"
|
|
required = true
|
|
validation.min_length = 2
|
|
validation.max_length = 100
|
|
|
|
[[sections.fields]]
|
|
id = "department"
|
|
type = "select"
|
|
label = "Department"
|
|
required = true
|
|
options = [
|
|
{ value = "engineering", label = "Engineering" },
|
|
{ value = "product", label = "Product" },
|
|
{ value = "design", label = "Design" },
|
|
]
|
|
|
|
[[sections.fields]]
|
|
id = "skills"
|
|
type = "multi-select"
|
|
label = "Skills"
|
|
display_mode = "grid"
|
|
options = [
|
|
{ value = "rust", label = "Rust" },
|
|
{ value = "typescript", label = "TypeScript" },
|
|
{ value = "python", label = "Python" },
|
|
]
|
|
|
|
[[sections.fields]]
|
|
id = "start_date"
|
|
type = "date"
|
|
label = "Start Date"
|
|
default = "today"
|
|
|
|
[output]
|
|
format = "json"
|
|
validation = "nickel://schemas/employee.ncl"
|
|
```
|
|
|
|
### Backend Trait
|
|
|
|
```rust
|
|
// typedialog-core/src/backend.rs
|
|
#[async_trait]
|
|
pub trait Backend: Send + Sync {
|
|
fn name(&self) -> &str;
|
|
|
|
async fn execute(&self, form: &Form) -> Result<FormResponse>;
|
|
|
|
async fn render_field(&self, field: &Field, value: Option<&Value>) -> Result<Value>;
|
|
|
|
fn supports_streaming(&self) -> bool { false }
|
|
}
|
|
|
|
pub struct BackendFactory;
|
|
|
|
impl BackendFactory {
|
|
pub fn create(backend_type: BackendType) -> Box<dyn Backend> {
|
|
match backend_type {
|
|
BackendType::Cli => Box::new(CliBackend::new()),
|
|
BackendType::Tui => Box::new(TuiBackend::new()),
|
|
BackendType::Web => Box::new(WebBackend::new()),
|
|
BackendType::Ai => Box::new(AiBackend::new()),
|
|
BackendType::Agent => Box::new(AgentBackend::new()),
|
|
BackendType::ProvGen => Box::new(ProvGenBackend::new()),
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Agent MDX Format
|
|
|
|
```mdx
|
|
---
|
|
name: code_reviewer
|
|
version: "1.0"
|
|
provider: claude
|
|
model: claude-sonnet-4-20250514
|
|
temperature: 0.3
|
|
max_tokens: 4096
|
|
---
|
|
|
|
# Code Review Agent
|
|
|
|
## System Prompt
|
|
|
|
You are an expert code reviewer. Review the following code for:
|
|
|
|
- Security vulnerabilities
|
|
- Performance issues
|
|
- Code style and best practices
|
|
- Potential bugs
|
|
|
|
## Template Variables
|
|
|
|
- `{{language}}`: Programming language
|
|
- `{{code}}`: Code to review
|
|
- `{{guidelines}}`: Project-specific guidelines
|
|
|
|
## User Prompt
|
|
|
|
Review this {{language}} code:
|
|
|
|
` ` `{{language}}
|
|
{{code}}
|
|
` ` `
|
|
|
|
Project guidelines:
|
|
|
|
{{guidelines}}
|
|
|
|
Provide a structured review with severity levels (critical, warning, info).
|
|
|
|
## Output Validation
|
|
|
|
format: json
|
|
schema: |
|
|
{
|
|
"issues": [{
|
|
"severity": "critical | warning | info",
|
|
"line": number,
|
|
"message": string,
|
|
"suggestion": string
|
|
}],
|
|
"summary": string
|
|
}
|
|
```
|
|
|
|
### Nickel Contract Integration
|
|
|
|
```rust
|
|
// typedialog-core/src/nickel.rs
|
|
pub struct NickelValidator {
|
|
runtime: nickel_lang_core::eval::Runtime,
|
|
}
|
|
|
|
impl NickelValidator {
|
|
pub fn validate(&self, data: &Value, contract_path: &str) -> Result<ValidationResult> {
|
|
let contract = self.runtime.load(contract_path)?;
|
|
let result = self.runtime.eval_with_contract(data, contract)?;
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn extract_schema(&self, contract_path: &str) -> Result<FormSchema> {
|
|
// Parse Nickel contract and generate form schema
|
|
let contract = self.runtime.load(contract_path)?;
|
|
FormSchema::from_nickel_contract(&contract)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Prov-Gen Output
|
|
|
|
```rust
|
|
// typedialog-prov-gen/src/generator.rs
|
|
pub enum CloudProvider {
|
|
Aws,
|
|
Gcp,
|
|
Azure,
|
|
Hetzner,
|
|
UpCloud,
|
|
Lxd,
|
|
}
|
|
|
|
pub struct InfrastructureConfig {
|
|
pub provider: CloudProvider,
|
|
pub region: String,
|
|
pub resources: Vec<Resource>,
|
|
pub networking: NetworkConfig,
|
|
pub security: SecurityConfig,
|
|
}
|
|
|
|
pub struct Generator {
|
|
templates: tera::Tera,
|
|
validators: Vec<Box<dyn Validator>>, // 7-layer validation
|
|
}
|
|
|
|
impl Generator {
|
|
pub async fn generate(&self, config: &InfrastructureConfig) -> Result<GeneratedIaC> {
|
|
// 1. Validate input config
|
|
self.validate_config(config)?;
|
|
|
|
// 2. Load provider-specific templates
|
|
let template = self.templates.get_template(&format!("{}.ncl.tera", config.provider))?;
|
|
|
|
// 3. Render Nickel configuration
|
|
let nickel_code = template.render(&config)?;
|
|
|
|
// 4. Validate generated Nickel
|
|
self.validate_nickel(&nickel_code)?;
|
|
|
|
Ok(GeneratedIaC {
|
|
provider: config.provider,
|
|
code: nickel_code,
|
|
files: self.split_to_files(&nickel_code)?,
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Provisioning: Especificaciones
|
|
|
|
### Directory Structure
|
|
|
|
```text
|
|
provisioning/
|
|
├── core/
|
|
│ ├── cli/ # Main CLI (211 lines)
|
|
│ ├── nulib/ # Nushell libraries (476+ accessors)
|
|
│ └── scripts/ # Utility scripts
|
|
├── extensions/
|
|
│ ├── providers/ # AWS, UpCloud, Local
|
|
│ ├── taskservs/ # 50+ infrastructure services
|
|
│ ├── clusters/ # Deployment templates
|
|
│ └── workflows/ # Automation workflows
|
|
├── platform/
|
|
│ ├── orchestrator/ # Workflow execution (Rust)
|
|
│ ├── control-center/ # Backend (Axum + RBAC)
|
|
│ ├── control-center-ui/ # Web dashboard (Leptos)
|
|
│ ├── installer/ # Multi-mode installer
|
|
│ ├── mcp-server/ # MCP server (Rust)
|
|
│ ├── ai-service/ # AI operations
|
|
│ ├── rag/ # RAG system
|
|
│ ├── vault-service/ # Secrets management
|
|
│ └── detector/ # Anomaly detection
|
|
└── schemas/ # Nickel IaC schemas
|
|
```
|
|
|
|
### Nickel IaC Schema
|
|
|
|
```nickel
|
|
# schemas/server.ncl
|
|
let Server = {
|
|
name | String,
|
|
provider | [ | 'aws, 'upcloud, 'local |],
|
|
|
|
spec | {
|
|
cpu | Number | default = 2,
|
|
memory_gb | Number | default = 4,
|
|
disk_gb | Number | default = 50,
|
|
|
|
os | {
|
|
family | [ | 'ubuntu, 'debian, 'rocky |],
|
|
version | String,
|
|
},
|
|
},
|
|
|
|
networking | {
|
|
vpc | String | optional,
|
|
subnet | String | optional,
|
|
public_ip | Bool | default = false,
|
|
security_groups | Array String | default = [],
|
|
},
|
|
|
|
tags | { _ : String } | default = {},
|
|
}
|
|
in Server
|
|
```
|
|
|
|
### Orchestrator API
|
|
|
|
```rust
|
|
// platform/orchestrator/src/lib.rs
|
|
pub struct Orchestrator {
|
|
state: StateManager,
|
|
executor: WorkflowExecutor,
|
|
scheduler: Scheduler,
|
|
}
|
|
|
|
impl Orchestrator {
|
|
pub async fn execute_workflow(&self, workflow: Workflow) -> Result<ExecutionResult> {
|
|
// 1. Resolve dependencies (topological sort)
|
|
let ordered_tasks = self.resolve_dependencies(&workflow)?;
|
|
|
|
// 2. Create execution checkpoints
|
|
let checkpoint = self.state.create_checkpoint(&workflow)?;
|
|
|
|
// 3. Execute tasks with retry logic
|
|
for task in ordered_tasks {
|
|
match self.executor.run(&task).await {
|
|
Ok(result) => {
|
|
self.state.record_success(&task, &result)?;
|
|
}
|
|
Err(e) => {
|
|
// Exponential backoff retry
|
|
if let Some(result) = self.retry_with_backoff(&task).await? {
|
|
self.state.record_success(&task, &result)?;
|
|
} else {
|
|
// Rollback to checkpoint
|
|
self.state.rollback(&checkpoint)?;
|
|
return Err(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(ExecutionResult::from_state(&self.state))
|
|
}
|
|
}
|
|
```
|
|
|
|
### MCP Tools
|
|
|
|
```rust
|
|
// platform/mcp-server/src/tools.rs
|
|
pub const MCP_TOOLS: &[Tool] = &[
|
|
Tool {
|
|
name: "query_infrastructure",
|
|
description: "Query infrastructure state using natural language",
|
|
parameters: json!({
|
|
"query": { "type": "string" },
|
|
"provider": { "type": "string", "optional": true }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "generate_config",
|
|
description: "Generate Nickel configuration from description",
|
|
parameters: json!({
|
|
"description": { "type": "string" },
|
|
"provider": { "type": "string" },
|
|
"resource_type": { "type": "string" }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "validate_config",
|
|
description: "Validate Nickel configuration",
|
|
parameters: json!({
|
|
"config": { "type": "string" },
|
|
"strict": { "type": "boolean", "default": true }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "estimate_cost",
|
|
description: "Estimate monthly cost for configuration",
|
|
parameters: json!({
|
|
"config": { "type": "string" },
|
|
"region": { "type": "string", "optional": true }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "check_compliance",
|
|
description: "Check configuration against compliance rules",
|
|
parameters: json!({
|
|
"config": { "type": "string" },
|
|
"framework": { "type": "string", "enum": ["soc2", "hipaa", "gdpr", "pci"] }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "plan_migration",
|
|
description: "Generate migration plan between configurations",
|
|
parameters: json!({
|
|
"current": { "type": "string" },
|
|
"target": { "type": "string" }
|
|
}),
|
|
},
|
|
Tool {
|
|
name: "execute_workflow",
|
|
description: "Execute provisioning workflow",
|
|
parameters: json!({
|
|
"workflow_id": { "type": "string" },
|
|
"dry_run": { "type": "boolean", "default": true }
|
|
}),
|
|
},
|
|
];
|
|
```
|
|
|
|
### RAG Configuration
|
|
|
|
```rust
|
|
// platform/rag/src/config.rs
|
|
pub struct RagConfig {
|
|
pub embedding_model: String, // "text-embedding-3-small"
|
|
pub embedding_dimensions: usize, // 1536
|
|
pub chunk_size: usize, // 512 tokens
|
|
pub chunk_overlap: usize, // 50 tokens
|
|
pub top_k: usize, // 5 results
|
|
pub min_similarity: f32, // 0.7
|
|
pub reranker: Option<RerankerConfig>,
|
|
}
|
|
|
|
pub struct RagService {
|
|
embedder: Box<dyn Embedder>,
|
|
vector_store: Box<dyn VectorStore>,
|
|
keyword_index: tantivy::Index,
|
|
}
|
|
|
|
impl RagService {
|
|
pub async fn query(&self, question: &str) -> Result<Vec<Document>> {
|
|
// 1. Generate embedding for question
|
|
let embedding = self.embedder.embed(question).await?;
|
|
|
|
// 2. Vector similarity search
|
|
let vector_results = self.vector_store.search(&embedding, self.config.top_k).await?;
|
|
|
|
// 3. BM25 keyword search
|
|
let keyword_results = self.keyword_search(question)?;
|
|
|
|
// 4. Hybrid ranking (RRF)
|
|
let merged = self.reciprocal_rank_fusion(vector_results, keyword_results);
|
|
|
|
// 5. Optional reranking
|
|
if let Some(reranker) = &self.reranker {
|
|
return reranker.rerank(&merged, question).await;
|
|
}
|
|
|
|
Ok(merged)
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. SecretumVault: Especificaciones
|
|
|
|
### Arquitectura General
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ SecretumVault (~11K LOC, 50+ tests) │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
|
│ │ CLI │ │ REST API │ │ Secrets Engines │ │
|
|
│ │ (clap) │ │ (Axum) │ │ KV/Transit/PKI/DB │ │
|
|
│ └──────┬──────┘ └──────┬──────┘ └────────────┬────────────┘ │
|
|
│ │ │ │ │
|
|
│ ┌──────┴────────────────┴──────────────────────┴─────────────┐ │
|
|
│ │ VaultCore │ │
|
|
│ │ Seal (Shamir) │ TokenManager │ Cedar ABAC │ Metrics │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────────┴───────────────────────────────────┐ │
|
|
│ │ Crypto Backends │ │
|
|
│ │ OpenSSL │ OQS (PQC) │ AWS-LC │ RustCrypto │ │
|
|
│ └───────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────────┴───────────────────────────────────┐ │
|
|
│ │ Storage Backends │ │
|
|
│ │ Filesystem │ etcd │ SurrealDB │ PostgreSQL │ │
|
|
│ └───────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Core Types
|
|
|
|
```rust
|
|
// src/core/vault.rs
|
|
pub struct VaultCore {
|
|
pub engines: HashMap<String, Box<dyn Engine>>,
|
|
pub storage: Arc<dyn StorageBackend>,
|
|
pub crypto: Arc<dyn CryptoBackend>,
|
|
pub seal: Arc<tokio::sync::Mutex<SealMechanism>>,
|
|
pub token_manager: Arc<TokenManager>,
|
|
pub metrics: Arc<Metrics>,
|
|
}
|
|
|
|
// src/crypto/mod.rs
|
|
#[async_trait]
|
|
pub trait CryptoBackend: Send + Sync {
|
|
async fn generate_keypair(&self, algorithm: KeyAlgorithm) -> CryptoResult<KeyPair>;
|
|
async fn sign(&self, key: &PrivateKey, data: &[u8]) -> CryptoResult<Vec<u8>>;
|
|
async fn verify(&self, key: &PublicKey, data: &[u8], sig: &[u8]) -> CryptoResult<bool>;
|
|
async fn encrypt(&self, plaintext: &[u8]) -> CryptoResult<Vec<u8>>;
|
|
async fn decrypt(&self, ciphertext: &[u8]) -> CryptoResult<Vec<u8>>;
|
|
|
|
// Post-Quantum (OQS backend)
|
|
async fn kem_encapsulate(&self, public_key: &[u8]) -> CryptoResult<KemResult>;
|
|
async fn kem_decapsulate(&self, ciphertext: &[u8]) -> CryptoResult<Vec<u8>>;
|
|
}
|
|
|
|
// src/storage/mod.rs
|
|
#[async_trait]
|
|
pub trait StorageBackend: Send + Sync {
|
|
async fn store_secret(&self, path: &str, data: &EncryptedData) -> StorageResult<()>;
|
|
async fn get_secret(&self, path: &str) -> StorageResult<EncryptedData>;
|
|
async fn delete_secret(&self, path: &str) -> StorageResult<()>;
|
|
async fn list_secrets(&self, prefix: &str) -> StorageResult<Vec<String>>;
|
|
}
|
|
```
|
|
|
|
### Crypto Backends
|
|
|
|
```rust
|
|
// src/crypto/backends/
|
|
pub enum CryptoBackendType {
|
|
OpenSSL, // RSA, ECDSA, AES-256-GCM
|
|
Oqs, // ML-KEM-768, ML-DSA-65 (NIST FIPS 203/204)
|
|
AwsLc, // RSA, ECDSA (experimental PQC)
|
|
RustCrypto, // AES-GCM, ChaCha20-Poly1305 (testing)
|
|
}
|
|
|
|
// OQS Post-Quantum (production-ready)
|
|
pub struct OqsBackend {
|
|
kem_algorithm: oqs::kem::Algorithm, // MlKem768
|
|
sig_algorithm: oqs::sig::Algorithm, // MlDsa65
|
|
}
|
|
|
|
impl OqsBackend {
|
|
pub async fn kem_keygen(&self) -> CryptoResult<KemKeyPair> {
|
|
// ML-KEM-768: 1088 bytes ciphertext, 32 bytes shared secret
|
|
let kem = oqs::kem::Kem::new(self.kem_algorithm)?;
|
|
let (pk, sk) = kem.keypair()?;
|
|
Ok(KemKeyPair { public_key: pk, secret_key: sk })
|
|
}
|
|
|
|
pub async fn sign(&self, sk: &[u8], message: &[u8]) -> CryptoResult<Vec<u8>> {
|
|
// ML-DSA-65 signatures
|
|
let sig = oqs::sig::Sig::new(self.sig_algorithm)?;
|
|
let signature = sig.sign(message, sk)?;
|
|
Ok(signature.into_vec())
|
|
}
|
|
}
|
|
```
|
|
|
|
### Secrets Engines
|
|
|
|
```rust
|
|
// src/engines/mod.rs
|
|
pub trait Engine: Send + Sync {
|
|
fn name(&self) -> &str;
|
|
fn engine_type(&self) -> &str;
|
|
async fn read(&self, path: &str) -> Result<Option<Value>>;
|
|
async fn write(&self, path: &str, data: &Value) -> Result<()>;
|
|
async fn delete(&self, path: &str) -> Result<()>;
|
|
async fn list(&self, prefix: &str) -> Result<Vec<String>>;
|
|
}
|
|
|
|
// Engines disponibles
|
|
pub struct KvEngine { /* Versioned secret storage */ }
|
|
pub struct TransitEngine { /* Encryption-as-a-service */ }
|
|
pub struct PkiEngine { /* X.509 certificates */ }
|
|
pub struct DatabaseEngine { /* Dynamic credentials */ }
|
|
```
|
|
|
|
### Seal Mechanism (Shamir)
|
|
|
|
```rust
|
|
// src/core/seal.rs
|
|
pub struct SealMechanism {
|
|
state: SealState,
|
|
shares: Vec<SecretShare>,
|
|
threshold: u8,
|
|
total_shares: u8,
|
|
}
|
|
|
|
pub enum SealState {
|
|
Sealed,
|
|
Unsealing { collected: usize },
|
|
Unsealed { master_key: Vec<u8> },
|
|
}
|
|
|
|
impl SealMechanism {
|
|
pub fn init(&mut self, shares: u8, threshold: u8) -> Result<Vec<SecretShare>> {
|
|
// Generate master key and split with Shamir
|
|
let master_key = generate_random_bytes(32)?;
|
|
let sharks = Sharks(threshold);
|
|
let dealer = sharks.dealer(&master_key);
|
|
let shares: Vec<_> = dealer.take(shares as usize).collect();
|
|
self.state = SealState::Sealed;
|
|
Ok(shares)
|
|
}
|
|
|
|
pub fn unseal(&mut self, share: SecretShare) -> Result<UnsealProgress> {
|
|
// Collect shares until threshold met
|
|
self.shares.push(share);
|
|
if self.shares.len() >= self.threshold as usize {
|
|
let sharks = Sharks(self.threshold);
|
|
let master_key = sharks.recover(&self.shares)?;
|
|
self.state = SealState::Unsealed { master_key };
|
|
return Ok(UnsealProgress::Complete);
|
|
}
|
|
Ok(UnsealProgress::NeedMore { collected: self.shares.len() })
|
|
}
|
|
}
|
|
```
|
|
|
|
### Authorization (Cedar ABAC)
|
|
|
|
```rust
|
|
// src/auth/cedar.rs
|
|
pub struct CedarAuthorizer {
|
|
engine: cedar_policy::Authorizer,
|
|
policies: cedar_policy::PolicySet,
|
|
}
|
|
|
|
impl CedarAuthorizer {
|
|
pub fn authorize(&self, request: &AuthzRequest) -> Result<Decision> {
|
|
let principal = self.build_principal(&request.user)?;
|
|
let action = self.build_action(&request.action)?;
|
|
let resource = self.build_resource(&request.resource)?;
|
|
|
|
let decision = self.engine.is_authorized(
|
|
&principal,
|
|
&action,
|
|
&resource,
|
|
&self.policies,
|
|
)?;
|
|
|
|
Ok(decision)
|
|
}
|
|
}
|
|
```
|
|
|
|
### API Endpoints
|
|
|
|
```rust
|
|
// src/api/routes.rs
|
|
Router::new()
|
|
// System
|
|
.route("/v1/sys/health", get(health_check))
|
|
.route("/v1/sys/init", post(initialize_vault))
|
|
.route("/v1/sys/seal", post(seal_vault))
|
|
.route("/v1/sys/unseal", post(unseal_vault))
|
|
.route("/v1/sys/mounts", get(list_mounts))
|
|
|
|
// Secrets (dynamic routing by engine)
|
|
.route("/v1/*path", get(read_secret)
|
|
.post(write_secret)
|
|
.delete(delete_secret))
|
|
|
|
// Metrics
|
|
.route("/metrics", get(prometheus_metrics))
|
|
```
|
|
|
|
### Configuration (TOML)
|
|
|
|
```toml
|
|
# svault.toml
|
|
[vault]
|
|
crypto_backend = "oqs" # openssl | oqs | aws-lc | rustcrypto
|
|
|
|
[server]
|
|
address = "0.0.0.0:8200"
|
|
tls_cert = "/path/to/cert.pem"
|
|
tls_key = "/path/to/key.pem"
|
|
|
|
[storage]
|
|
backend = "etcd" # filesystem | etcd | surrealdb | postgresql
|
|
|
|
[storage.etcd]
|
|
endpoints = ["http://localhost:2379"]
|
|
|
|
[seal.shamir]
|
|
shares = 5
|
|
threshold = 3
|
|
|
|
[auth]
|
|
token_ttl = "24h"
|
|
```
|
|
|
|
### CLI Commands
|
|
|
|
```bash
|
|
# Server
|
|
svault server --config svault.toml
|
|
|
|
# Operator
|
|
svault operator init --shares 5 --threshold 3
|
|
svault operator unseal --share <share>
|
|
svault operator seal
|
|
svault operator status
|
|
|
|
# Secrets
|
|
svault secret read secret/myapp
|
|
svault secret write secret/myapp key=value
|
|
svault secret delete secret/myapp
|
|
svault secret list secret/
|
|
```
|
|
|
|
### Feature Flags
|
|
|
|
```toml
|
|
# Cargo.toml features
|
|
[features]
|
|
default = ["openssl", "filesystem", "server", "pqc"]
|
|
|
|
# Crypto backends
|
|
openssl = ["dep:openssl"]
|
|
aws-lc = ["dep:aws-lc-rs"]
|
|
pqc = ["dep:oqs"]
|
|
rustcrypto = ["dep:aes-gcm", "dep:chacha20poly1305"]
|
|
|
|
# Storage backends
|
|
filesystem = []
|
|
surrealdb-storage = ["dep:surrealdb"]
|
|
etcd-storage = ["dep:etcd-client"]
|
|
postgresql-storage = ["dep:sqlx"]
|
|
|
|
# Components
|
|
server = ["dep:axum", "dep:rustls"]
|
|
cli = ["dep:clap"]
|
|
cedar = ["dep:cedar-policy"]
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Integración entre Proyectos
|
|
|
|
### Diagrama de Dependencias
|
|
|
|
```text
|
|
┌───────────────────┐
|
|
│ Kogral │
|
|
│ (Knowledge Graph) │
|
|
└─────────┬─────────┘
|
|
│
|
|
MCP (guidelines, patterns, decisions)
|
|
│
|
|
┌─────────────────────────┼─────────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ Vapora │ │ TypeDialog │ │ Provisioning │
|
|
│(Orchestrate)│ │ (Forms/UI) │ │ (IaC) │
|
|
└──────┬──────┘ └────────┬────────┘ └────────┬────────┘
|
|
│ │ │
|
|
│ ┌─────────────┴─────────────┐ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ SecretumVault │ │
|
|
│ │ (Secrets + PQC Crypto) │ │
|
|
│ └───────────────────────────────┘ │
|
|
│ │ │
|
|
└──────────────────┼────────────────────┘
|
|
│
|
|
┌─────────┴─────────┐
|
|
│ SurrealDB │
|
|
│ (Shared State) │
|
|
└───────────────────┘
|
|
```
|
|
|
|
### Shared Dependencies (Cargo.toml)
|
|
|
|
```toml
|
|
# Dependencias comunes a todos los proyectos
|
|
[dependencies]
|
|
# Runtime
|
|
tokio = { version = "1.48", features = ["full"] }
|
|
|
|
# Serialization
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
|
|
# Database
|
|
surrealdb = "2.3"
|
|
|
|
# Web
|
|
axum = "0.8"
|
|
|
|
# LLM
|
|
rig-core = "0.15"
|
|
|
|
# Config
|
|
nickel-lang-core = "1.15"
|
|
|
|
# Logging
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
|
|
# Error handling
|
|
anyhow = "1.0"
|
|
thiserror = "2.0"
|
|
```
|
|
|
|
### SurrealDB Schema Compartido
|
|
|
|
```sql
|
|
-- Namespace compartido para cross-project state
|
|
DEFINE NAMESPACE portfolio;
|
|
|
|
-- Scope para cada proyecto
|
|
DEFINE DATABASE vapora;
|
|
DEFINE DATABASE kogral;
|
|
DEFINE DATABASE typedialog;
|
|
DEFINE DATABASE provisioning;
|
|
DEFINE DATABASE secretumvault;
|
|
|
|
-- Tabla compartida para execution records
|
|
DEFINE TABLE executions SCHEMAFULL;
|
|
DEFINE FIELD project ON executions TYPE string;
|
|
DEFINE FIELD task_type ON executions TYPE string;
|
|
DEFINE FIELD agent_id ON executions TYPE string;
|
|
DEFINE FIELD status ON executions TYPE string;
|
|
DEFINE FIELD duration_ms ON executions TYPE int;
|
|
DEFINE FIELD tokens_used ON executions TYPE int;
|
|
DEFINE FIELD cost_cents ON executions TYPE float;
|
|
DEFINE FIELD created_at ON executions TYPE datetime DEFAULT time::now();
|
|
|
|
-- Índices para queries cross-project
|
|
DEFINE INDEX idx_executions_project ON executions FIELDS project;
|
|
DEFINE INDEX idx_executions_agent ON executions FIELDS agent_id;
|
|
```
|
|
|
|
### Ejemplo de Integración: Feature Development
|
|
|
|
```rust
|
|
// Flujo integrado de desarrollo de feature
|
|
async fn develop_feature(feature_spec: &str) -> Result<FeatureResult> {
|
|
// 1. Kogral: Obtener contexto del proyecto
|
|
let kogral_client = KogralMcpClient::connect().await?;
|
|
let guidelines = kogral_client.call("get_guidelines", json!({
|
|
"topic": feature_spec,
|
|
"include_shared": true
|
|
})).await?;
|
|
|
|
let patterns = kogral_client.call("search", json!({
|
|
"query": feature_spec,
|
|
"node_type": "pattern",
|
|
"limit": 5
|
|
})).await?;
|
|
|
|
// 2. TypeDialog: Capturar configuración adicional
|
|
let typedialog = TypeDialog::new(BackendType::Cli);
|
|
let config = typedialog.execute_form("feature_config.toml").await?;
|
|
|
|
// 3. Vapora: Orquestar agentes
|
|
let vapora_client = VaporaClient::new("http://localhost:8001");
|
|
|
|
// Crear tarea con contexto
|
|
let task = vapora_client.create_task(TaskRequest {
|
|
title: format!("Implement: {}", feature_spec),
|
|
context: json!({
|
|
"guidelines": guidelines,
|
|
"patterns": patterns,
|
|
"config": config,
|
|
}),
|
|
task_type: "feature_implementation",
|
|
}).await?;
|
|
|
|
// Ejecutar pipeline
|
|
let pipeline = vec![
|
|
("architect", "Design feature architecture"),
|
|
("developer", "Implement feature"),
|
|
("reviewer", "Review implementation"),
|
|
("tester", "Write and run tests"),
|
|
];
|
|
|
|
for (role, description) in pipeline {
|
|
vapora_client.assign_task(&task.id, role, description).await?;
|
|
vapora_client.wait_for_completion(&task.id).await?;
|
|
}
|
|
|
|
// 4. Kogral: Registrar decisión
|
|
kogral_client.call("add_decision", json!({
|
|
"title": format!("Feature: {}", feature_spec),
|
|
"context": &task.context,
|
|
"decision": &task.result,
|
|
"consequences": "Implementation completed"
|
|
})).await?;
|
|
|
|
// 5. Provisioning: Desplegar si es necesario
|
|
if config.requires_infra {
|
|
let prov_client = ProvisioningMcpClient::connect().await?;
|
|
prov_client.call("execute_workflow", json!({
|
|
"workflow_id": config.deployment_workflow,
|
|
"dry_run": false
|
|
})).await?;
|
|
}
|
|
|
|
Ok(FeatureResult {
|
|
task_id: task.id,
|
|
status: task.status,
|
|
})
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Métricas de Calidad
|
|
|
|
| Proyecto | Tests | Cobertura | Clippy | Unsafe | Doc Coverage |
|
|
| ---------- | ------- | ----------- | -------- | -------- | -------------- |
|
|
| Vapora | 218 | ~70% | 0 warnings | 0 | 100% public |
|
|
| Kogral | 56 | ~80% | 0 warnings | 0 | 100% public |
|
|
| TypeDialog | 3,818 | ~85% | 0 warnings | 0 | 100% public |
|
|
| Provisioning | 218 | ~65% | 0 warnings | 0 | 100% public |
|
|
| SecretumVault | 50+ | ~75% | 0 warnings | 0 | 100% public |
|
|
|
|
### Comandos de Verificación
|
|
|
|
```bash
|
|
# Por proyecto
|
|
cargo clippy --all-targets --all-features -- -D warnings
|
|
cargo test --workspace
|
|
cargo doc --no-deps
|
|
|
|
# Coverage (requiere tarpaulin)
|
|
cargo tarpaulin --workspace --out Html
|
|
|
|
# Benchmarks
|
|
cargo bench --workspace
|
|
```
|
|
|
|
---
|
|
|
|
*Documento generado: 2026-01-22*
|
|
*Tipo: info (especificaciones técnicas)*
|