CLI
Tres subcomandos. La config por defecto es condor.yaml en el cwd; se cambia con -c/--config.
$ condor check -c condor.yaml # valida y sale (exit 0 si es válida) $ condor start -c condor.yaml # inicia listeners + endpoints de gestión $ condor reload -c condor.yaml # POST /reload al admin_addr de la config
El reload también se dispara con SIGHUP al proceso (systemctl reload condor).
global
Ajustes de proceso: nivel y formato de log, y direcciones de los endpoints de gestión.
global: log_level: info # debug | info | warn | error log_format: json # json | pretty metrics_addr: "0.0.0.0:9090" # /health, /metrics (scrape público) admin_addr: "127.0.0.1:9091" # + /config, /reload (solo local)
RUST_LOGsobreescribelog_levelsi está presente.
listeners & TLS
Cada listener enlaza una dirección. La presencia de la clave tls activa HTTPS. Sin ella, es HTTP en claro. El listener detecta HTTP/1.1 y HTTP/2 automáticamente.
listeners: - addr: "0.0.0.0:80" # HTTP en claro - addr: "0.0.0.0:443" tls: mode: file # file | acme | self_signed cert: /etc/condor/tls/fullchain.pem key: /etc/condor/tls/privkey.pem
mode: file— cargacert+keyen PEM. Soporta SNI multi-dominio.mode: self_signed— genera un cert autofirmado en memoria (dev / interno).mode: acme— emite con Let's Encrypt (ver abajo).
ACME / Let's Encrypt
Con mode: acme, Condor obtiene y renueva certificados vía desafío HTTP-01. Requiere un listener en el puerto 80 accesible públicamente (sirve /.well-known/acme-challenge/) y DNS apuntando al host.
tls: mode: acme acme_email: ops@falp.cl acme_domains: ["api.falp.cl", "www.falp.cl"] cache_dir: /var/lib/condor/certs # account.json + cert.pem + key.pem
El certificado se cachea en cache_dir y se renueva en segundo plano antes de expirar. El resolver SNI se actualiza en caliente cuando llega el cert nuevo.
routes
Las rutas se resuelven por especificidad: host exacto → prefijo de path (el más largo gana) → host wildcard (*.dominio) → catch-all.
routes: - name: api match_host: api.falp.cl # opcional; admite *.falp.cl match_path: /hercules/* # exacto (/health) o prefijo (/x/*) upstream: hercules-pool # debe existir en upstreams middleware: [ ... ]
Al reenviar, Condor agrega X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host y X-Request-Id (ULID), y elimina los headers hop-by-hop.
middleware
Lista ordenada por ruta. Un middleware que rechaza corta el flujo con el status correspondiente.
middleware: - type: auth mode: jwt # jwt | api_key jwks_url: "https://auth.falp.cl/.well-known/jwks.json" # mode: api_key → api_keys: ["k1", "k2"] (header X-Api-Key) - type: ratelimit requests_per_second: 500 burst: 100 # token bucket por IP → 429 + Retry-After - type: audit # registra la petición (ver audit)
auth jwt— valida el Bearer contra el JWKS (refrescado en background); elsubqueda comouser_iden el audit. Falla → 401/403.auth api_key— compara el headerX-Api-Keycontra la lista.ratelimit— token bucket por IP de origen; excedido → 429.
upstreams
Pool de targets con estrategia de balanceo, health checks y circuit breaker.
upstreams: - name: hercules-pool strategy: round_robin # round_robin (ponderado) | least_conn | random | ip_hash http2: false # true = h2c prior-knowledge hacia el upstream targets: - { addr: "10.0.1.10:8080", weight: 1 } - { addr: "10.0.1.11:8080", weight: 2 } health_check: path: /health interval_secs: 5 timeout_ms: 2000 healthy_threshold: 2 unhealthy_threshold: 3 circuit_breaker: failure_threshold: 50 # % de error en la ventana para abrir recovery_secs: 30 # tiempo en Open antes de HalfOpen
audit
Log append-only en JSONL, una línea por petición. Nunca modifica líneas existentes (compatible con sinks WORM).
audit: enabled: true path: /var/log/condor/audit.jsonl rotate: daily # daily | never → audit-YYYY-MM-DD.jsonl fields: [timestamp, request_id, client_ip, method, host, path, status, upstream, duration_ms, user_id]
Cada línea, con claves compactas:
{"ts":"2026-05-24T03:14:15.926Z","rid":"01J...","cip":"1.2.3.4",
"m":"GET","host":"api.falp.cl","path":"/hercules/patients",
"st":200,"up":"hercules-pool","dur_ms":12,"uid":"rvielma"}
endpoints de gestión
GET /health— estado y salud de upstreams (JSON). Enmetrics_addryadmin_addr.GET /metrics— formato de texto Prometheus.GET /config— config actual sin secretos. Solo enadmin_addr.POST /reload— dispara hot reload. Solo enadmin_addr.
métricas
condor_requests_total{route,upstream,status,method}
condor_request_duration_seconds{route,upstream}
condor_upstream_health{upstream,target}
condor_circuit_state{upstream} # 0=closed 1=open 2=half
condor_tls_handshakes_total{listener,result}
condor_ratelimit_rejected_total{route}
condor_upstream_pool_size{upstream}
deploy
Compilá un binario estático y desplegalo con systemd:
$ cargo zigbuild --release --target x86_64-unknown-linux-musl --bin condor $ scp target/x86_64-unknown-linux-musl/release/condor servidor:/tmp/ $ sudo install -m755 /tmp/condor /usr/local/bin/condor $ sudo install -m644 deploy/condor.service /etc/systemd/system/ $ sudo systemctl enable --now condor
El hot reload aplica cambios de rutas, upstreams y middleware en caliente. Cambiar direcciones de listener o la config TLS requiere reiniciar el servicio.