syntaxis/docs/provision/solution-architecture-correct.md
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
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)
2025-12-26 18:36:23 +00:00

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:

  1. CAPA 1 (provisioning): TaskService KCL (como postgres)
  2. CAPA 2 (provctl): Config TOML (como minimal-app)
  3. 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")