Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
20 KiB
20 KiB
✅ SOLUCIÓN: Syntaxis como TaskService Integrado
Fecha: 2025-11-20 Estado: Arquitectura correcta basada en patrones existentes Referencia: taskservs (databases), provctl (stage1-extended-definitions)
🎯 El Problema (Versión Final)
¿Por qué Dockerfile o docker-compose NO son suficientes?
Dockerfile/docker-compose definen:
✅ Cómo empacar (imagen + dependencias)
✅ Qué puertos exponer
✅ Qué variables de entorno
✅ Cómo iniciar el servicio
Pero NO definen:
❌ Cómo GESTIONAR el ciclo de vida (systemd, provctl)
❌ Cómo VALIDAR salud del servicio
❌ Cómo ORQUESTAR múltiples máquinas
❌ Cómo INTEGRAR con provisioning
❌ Cómo ESCALAR en diferentes contextos (local, dev, prod)
❌ Requisitos de INFRA (networking, storage, secrets)
La Solución: Tres capas coherentes
┌─────────────────────────────────────────────────────────────┐
│ CAPA 1: DEFINICIÓN (taskserv de syntaxis) │
│ Dónde: /provisioning/extensions/taskservs/development/ │
│ syntaxis/kcl/syntaxis.k │
│ │
│ Define: Qué es syntaxis, sus servicios, requisitos │
│ Lenguaje: KCL (como postgres, redis, kubernetes) │
│ Consumidor: provisioning (como taskserv externo) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CAPA 2: GESTIÓN LOCAL (provctl config) │
│ Dónde: /provisioning/extensions/provctl/syntaxis.toml │
│ o generado desde taskserv │
│ │
│ Define: Cómo ejecutar syntaxis localmente (systemd, etc) │
│ Lenguaje: TOML (como minimal-app, containerized-web-app) │
│ Consumidor: provctl (para control local/remoto) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CAPA 3: PRESETS/TEMPLATES (syntaxis installer) │
│ Dónde: syntaxis/configs/deployment-presets.kcl │
│ │
│ Define: Qué servicios en cada contexto (local, dev, prod) │
│ Lenguaje: KCL o YAML (readable, maintainable) │
│ Consumidor: syntaxis installer (para ofrecerpresets) │
└─────────────────────────────────────────────────────────────┘
INTEGRACIÓN:
Capa 1 → provisioning la ve como taskserv importable
Capa 2 → provctl la puede gestionar localmente
Capa 3 → installer ofrece presets (local/dev/prod)
📐 CAPA 1: TaskService en provisioning
Estructura
/Users/Akasha/project-provisioning/provisioning/extensions/taskservs/
└── development/
└── syntaxis/
├── kcl/
│ ├── syntaxis.k ← Schema principal
│ ├── dependencies.k ← Requisitos
│ ├── kcl.mod ← Module definition
│ └── kcl.mod.lock
├── default/
│ ├── install-syntaxis.sh
│ ├── env-syntaxis.j2 ← Tera template para vars
│ └── config.toml ← Config ejemplo
└── README.md
Archivo: syntaxis.k (Schema)
Basado en patrón real de n8n, pero adaptado a servicios de syntaxis:
# /provisioning/extensions/taskservs/development/syntaxis/kcl/syntaxis.k
# Syntaxis Project Management Platform - Task Service Definition
# Patrón: Similar a n8n, postgres, etc.
import regex
schema Database:
"""Database backend configuration for syntaxis"""
typ: "sqlite" | "surrealdb" | "postgres" = "sqlite"
host?: str = "127.0.0.1:5432" if typ == "postgres" else Undefined
port?: int = 5432 if typ == "postgres" else Undefined
name: str = "syntaxis"
user?: str
password?: str
path?: str = "/var/lib/syntaxis/syntaxis.db" if typ == "sqlite" else Undefined
check:
len(name) > 0, "Database name required"
typ == "sqlite" or (user != Undefined and len(user) > 0), "User required for postgres/surrealdb"
schema Service:
"""Individual service configuration within syntaxis"""
name: str
type: "binary" | "service" | "web"
enabled: bool = False
port?: int
protocol?: str = "http"
check:
len(name) > 0, "Service name required"
schema Syntaxis:
"""
Syntaxis Project Management Platform
Pattern: Like n8n, but for project management
"""
# Identity
name: str = "syntaxis"
version: str
app_name: str = "syntaxis Project Manager"
# System paths
work_path: str = "/var/lib/syntaxis"
etc_path: str = "/etc/syntaxis"
log_path: str = "/var/log/syntaxis"
bin_path: str = "/usr/local/bin/syntaxis"
# HTTP/Network Configuration
protocol: "http" | "https" = "http"
http_addr: str = "127.0.0.1"
http_port: int = 3000
domain: str = "localhost"
# Services (CLI, TUI, API, Dashboard)
cli: Service = {
name = "syntaxis-cli"
type = "binary"
enabled = True
}
tui: Service = {
name = "syntaxis-tui"
type = "binary"
enabled = False
}
api: Service = {
name = "syntaxis-api"
type = "service"
enabled = False
port = 3000
protocol = "http"
}
dashboard: Service = {
name = "syntaxis-dashboard"
type = "web"
enabled = False
port = 8080
protocol = "http"
}
# Database configuration
db: Database = {typ = "sqlite"}
# Dependencies (other taskservs)
dependencies: [str] = [] # e.g., ["surrealdb", "nats"]
# Application settings
timezone: str = "UTC"
language: str = "en"
log_level: "debug" | "info" | "warn" | "error" = "info"
# Validation
check:
len(domain) > 0, "domain is required"
1 <= http_port <= 65535, "http_port must be between 1 and 65535"
regex.match(domain, r"^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$"), "Invalid domain format"
# Extended configurations for different modes
schema Syntaxis_Dev(Syntaxis):
"""Development configuration"""
protocol = "http"
http_addr = "127.0.0.1"
http_port = 3000
api.enabled = True
dashboard.enabled = True
tui.enabled = True
db = {
typ = "sqlite"
path = "/var/lib/syntaxis/syntaxis-dev.db"
}
dependencies = ["surrealdb"]
schema Syntaxis_Production(Syntaxis):
"""Production HA configuration"""
protocol = "https"
http_addr = "0.0.0.0"
http_port = 3000
api.enabled = True
dashboard.enabled = False # No dashboard in prod
cli.enabled = True
db = {
typ = "postgres"
host = "localhost:5432"
name = "syntaxis_prod"
user = "syntaxis"
}
dependencies = ["postgres", "nats"]
check:
protocol == "https", "Production must use HTTPS"
db.typ == "postgres", "Production must use PostgreSQL"
Archivo: dependencies.k (Requisitos)
# /provisioning/extensions/taskservs/development/syntaxis/kcl/dependencies.k
schema SyntaxisRequirements:
"""System and service requirements for syntaxis"""
system:
min_memory_gb: int = 2
min_disk_gb: int = 5
min_cpu_cores: int = 1
# Optional external services
optional_services: [str] = [
"surrealdb", # For persistence
"nats", # For messaging
]
ports:
api: int = 3000
dashboard: int = 8080
check:
min_memory_gb > 0, "Min memory must be > 0"
min_disk_gb > 0, "Min disk must be > 0"
Uso en provisioning
# En /provisioning/kcl/services.k o similar
import syntaxis_module = .extensions.taskservs.development.syntaxis
syntaxis_service: syntaxis_module.Syntaxis = {
name = "syntaxis"
version = "0.1.0"
deployment_mode = "prod"
cli.enabled = True
api.enabled = True
dashboard.enabled = True
database.enabled = True
database.type = "postgres" # Use provisioned postgres
dependencies = ["postgres"] # Require postgres taskserv
}
🔧 CAPA 2: Gestión Local con provctl
Estructura
/Users/Akasha/Development/provctl/
├── examples/
│ └── stage1-extended-definitions/
│ └── syntaxis-local-dev.toml ← NEW: Syntaxis config
├── provisioning/
│ └── extensions/
│ └── syntaxis/
│ └── syntaxis.toml ← NEW: provctl-friendly config
Archivo: syntaxis-local-dev.toml (provctl config)
# /provctl/examples/stage1-extended-definitions/syntaxis-local-dev.toml
# Syntaxis local development configuration for provctl
[metadata]
name = "syntaxis-local-dev"
description = "Syntaxis local development with all services"
version = "0.1.0"
# Main API service
[service]
name = "syntaxis-api"
binary = "/usr/local/bin/syntaxis-api"
args = ["--bind", "127.0.0.1:3000"]
working_dir = "/opt/syntaxis"
[service.env_vars]
RUST_LOG = "debug"
DATABASE_URL = "sqlite:///var/lib/syntaxis/syntaxis.db"
ENVIRONMENT = "development"
# Optional: TUI service
[[services]]
name = "syntaxis-tui"
binary = "/usr/local/bin/syntaxis-tui"
enabled = false # Off by default
# Optional: Dashboard service
[[services]]
name = "syntaxis-dashboard"
type = "web"
port = 8080
enabled = false
# Health check for API
[[health_checks]]
name = "api-health"
type = "http"
endpoint = "http://127.0.0.1:3000/health"
interval_seconds = 10
timeout_seconds = 5
[[health_checks]]
name = "api-ready"
type = "http"
endpoint = "http://127.0.0.1:3000/api/projects"
interval_seconds = 30
timeout_seconds = 10
required = false # Optional endpoint
# Secrets management
[[secrets]]
name = "db-password"
backend = "file"
path = "/var/lib/syntaxis/.db-password"
env_var = "DATABASE_PASSWORD"
# Container variant (optional)
[container]
image = "syntaxis:latest"
registry = "docker.io"
image_pull_policy = "IfNotPresent"
[container.env_vars]
RUST_LOG = "debug"
# Database setup
[[data_services]]
name = "sqlite"
type = "sqlite"
version = "3"
path = "/var/lib/syntaxis/syntaxis.db"
auto_migrate = true
Cómo provctl lo consumiría
# Developer corre
provctl config apply --from-file /provctl/examples/syntaxis-local-dev.toml
# O desde taskserv (si se integra)
provctl config apply --from-taskserv syntaxis --mode dev
# Resultado:
# - Crea configuración local
# - Inicia servicios con systemd/launchd
# - Monitorea health
# - Proporciona CLI para control
🎯 CAPA 3: Presets en syntaxis
Estructura
syntaxis/
├── configs/
│ ├── deployment-presets.kcl ← NEW: Define presets
│ ├── services-catalog.toml ← EXISTENTE (documentación)
│ └── database.toml
└── src/
Archivo: deployment-presets.kcl
# syntaxis/configs/deployment-presets.kcl
# Deployment presets for different contexts
schema DeploymentPreset:
"""A deployment preset for syntaxis"""
name: str
description: str
services: {str: bool} # service_id: enabled
manager: str # "manual", "systemd", "provctl", "provisioning"
requirements: [str] # taskservs needed (e.g., ["surrealdb"])
preset_local: DeploymentPreset = {
name = "local"
description = "Single machine, CLI only. No services. Manual everything."
services = {
"cli": True,
"tui": False,
"api": False,
"dashboard": False,
}
manager = "manual"
requirements = []
}
preset_dev: DeploymentPreset = {
name = "dev"
description = "Local development with full stack (CLI, API, Dashboard, DB)"
services = {
"cli": True,
"tui": True,
"api": True,
"dashboard": True,
}
manager = "provctl" # provctl manages locally
requirements = ["surrealdb"] # Need database
}
preset_staging: DeploymentPreset = {
name = "staging"
description = "Staging environment (multiple machines, HA)"
services = {
"cli": True,
"api": True,
"dashboard": True,
}
manager = "provisioning" # Full provisioning management
requirements = ["postgres", "nats", "kubernetes"]
}
preset_production: DeploymentPreset = {
name = "production"
description = "Production deployment (HA, multi-region, monitoring)"
services = {
"api": True,
"dashboard": False, # No dashboard in prod
}
manager = "provisioning" # Full provisioning + monitoring
requirements = ["postgres", "nats", "kubernetes", "prometheus"]
}
all_presets = [
preset_local,
preset_dev,
preset_staging,
preset_production,
]
Uso: syntaxis installer
// syntaxis/core/crates/cli/src/installer.rs
pub fn list_presets() -> Vec<DeploymentPreset> {
// Load from configs/deployment-presets.kcl
KCL::load("configs/deployment-presets.kcl")
.all_presets
}
pub fn install(preset: &str) -> Result<()> {
let preset_config = load_preset(preset)?;
println!("📦 Installing preset: {}", preset_config.name);
println!(" {}", preset_config.description);
println!(" Services: {:?}", preset_config.services);
println!(" Manager: {}", preset_config.manager);
match preset_config.manager {
"manual" => {
println!("Manual setup. Follow these steps:");
print_manual_guide(&preset_config);
}
"provctl" => {
if detect_provctl()? {
println!("provctl detected. Configuring...");
apply_provctl_config(&preset_config)?;
} else {
println!("provctl not found. Install from: https://...");
}
}
"provisioning" => {
println!("Provisioning required. Integrating...");
integrate_with_provisioning(&preset_config)?;
}
_ => {}
}
Ok(())
}
Resultado: Coherencia entre capas
syntaxis installer
↓
Lee: deployment-presets.kcl
├─ "local" → manual setup
├─ "dev" → provctl (if available)
├─ "staging" → provisioning
└─ "production" → provisioning + monitoring
↓
Para cada preset:
├─ Lee requisitos (surrealdb, nats, etc)
├─ Genera config provctl (si manager="provctl")
├─ O integra con provisioning (si manager="provisioning")
└─ Ofrece comandos para cada paso
🔗 Cómo Se Conectan Las Tres Capas
CAPA 1: Definición en provisioning
# provisioning/extensions/taskservs/development/syntaxis/kcl/syntaxis.k
schema Syntaxis:
version: str
deployment_mode: str
database: { type: str, ... }
health_checks: { ... }
CAPA 2: Configuración en provctl
# provctl/examples/syntaxis-local-dev.toml
[service]
name = "syntaxis-api"
binary = "/usr/local/bin/syntaxis-api"
args = ["--bind", "127.0.0.1:3000"]
[[health_checks]]
type = "http"
endpoint = "http://127.0.0.1:3000/health"
CAPA 3: Presets en syntaxis
# syntaxis/configs/deployment-presets.kcl
preset_dev: DeploymentPreset = {
manager = "provctl"
requirements = ["surrealdb"]
services = { "api": True, "tui": True, ... }
}
Flujo de integración
Developer usa syntaxis installer:
↓
installer lee deployment-presets.kcl
↓
Elige preset "dev":
├─ manager = "provctl" → installer llama provctl
├─ requirements = ["surrealdb"] → verificar disponible
└─ services = { "api": True, ... } → generar config
↓
provctl aplica config (desde syntaxis-local-dev.toml)
├─ Genera config systemd/launchd
├─ Inicia servicios
├─ Monitorea health
└─ Proporciona CLI para control
↓
Si necesita provisioning (preset "prod"):
├─ installer detecta provisioning
├─ syntaxis se importa como taskserv
├─ provisioning ve requisitos (postgres, nats, etc)
├─ provisioning orchestra todo
└─ Resultado: Full stack en producción
📊 Comparación: Antes vs Después
| Aspecto | TOML Compilado | Solución (3 Capas) |
|---|---|---|
| Definición | TOML → catalog.rs → ingestionable | KCL taskserv (como postgres) |
| Gestión local | ¿Cómo ejecutar? | provctl TOML config (como minimal-app) |
| Presets | No existen | KCL en syntaxis (deployment-presets.kcl) |
| Cambios requieren | Recompilación | Solo guardar archivo |
| Consumible por | provisioning tools | provisioning, provctl, installer |
| Escalabilidad | Acoplado a Rust | Modular, extensible |
| Developer experience | "¿Qué es este TOML?" | "Elige preset: local, dev, staging, prod" |
✅ Beneficios
Para Developer
✅ Elige preset (local, dev, staging, prod)
✅ Instalador automático lo detecta todo
✅ Corre syntaxis: local manual, dev con provctl, prod con provisioning
✅ NO ve TOML compilado, ve presets legibles
Para provisioning
✅ syntaxis es un taskserv como cualquier otro (postgres, kubernetes, etc)
✅ Lo importa como dependency externo
✅ Lo orquesta en contexto de infra
✅ Sabe requisitos (qué bases de datos, etc) del schema KCL
Para provctl
✅ Lee config TOML como cualquier otro servicio
✅ Puede gestionar syntaxis localmente
✅ Puede gestionar sintaxis remotamente (SSH)
✅ Health checks automáticos
Para installer
✅ Ofrece presets claros
✅ Detección inteligente (provisioning? provctl? manual?)
✅ Configura automáticamente
✅ Fallback gracioso con guía manual
🚀 Implementación (Roadmap Mínimo)
Paso 1: Crear taskserv en provisioning
1. Crear estructura:
provisioning/extensions/taskservs/development/syntaxis/
2. Definir KCL:
- syntaxis.k (schema principal)
- dependencies.k (requisitos)
3. Agregar ejemplos:
- install-syntaxis.sh
- env-syntaxis.j2
Paso 2: Crear config provctl
1. Crear: provctl/examples/syntaxis-local-dev.toml
2. Define:
- Service principal (syntaxis-api)
- Health checks
- Variables de entorno
- Secretos
Paso 3: Crear presets en syntaxis
1. Crear: syntaxis/configs/deployment-presets.kcl
2. Define:
- preset_local
- preset_dev
- preset_staging
- preset_production
Paso 4: Integrar con installer
1. Extender syntaxis installer para:
- Leer presets
- Detectar provctl/provisioning
- Aplicar configuración automática
📌 Resumen
El problema: TOML compilado es ingestionable, requiere recompilación, no es escalable.
La solución: Tres capas coherentes:
- CAPA 1 (provisioning): TaskService KCL (como postgres)
- CAPA 2 (provctl): Config TOML (como minimal-app)
- CAPA 3 (syntaxis): Presets KCL (local/dev/staging/prod)
Resultado:
- Developer elige preset
- Installer detecta contexto
- Se configura automáticamente
- Funciona sin Docker/compose requirements adicionales
- Integrable con provisioning y provctl
Por qué funciona:
- Sigue patrones EXISTENTES en provisioning y provctl
- No inventa nuevos formatos
- Reutiliza código/infraestructura existente
- Escalable a otros proyectos (como "taskservs externo")