Changelog

Todas as mudancas notaveis neste projeto sao documentadas neste arquivo.

O formato e baseado em Keep a Changelog, e este projeto adere ao Semantic Versioning.

[2.21.0] - 2026-05-07 — Multi-app follow-ups

Resumo

5 features que completam MVPs/slots deixados em v2.20.0 + 1 UX safety net + 1 doc fix. Construído sobre o feedback de um deploy multi-service complexo em produção.

Mudança comportamento notável: runner add em repo já registrado agora falha com duplicate_repo exit 2. Workaround compatível: --force-duplicate. Documentado abaixo.

Adicionado

A — Interpolation `{{::Var}}` em `build.args:`

v2.20.0 entregou MVP literal (passa valor cru pro --build-arg). v2.21.0 fecha: agora interpola o mesmo {{::Var}} que environment: usa, resolvendo de .env e .secrets em build time.

build:
  args:
    VITE_SUPABASE_URL: "{{::SUPABASE_URL}}"
    VITE_LIVEKIT_WS_URL: "{{::LIVEKIT_WS_URL?wss://default.com}}"

Order: .secrets primeiro (mais específico), .env fallback. Suporta {{::Key?default}}. Sem TTY no build → não pede prompt; se var não resolve, erro instrutivo apontando pra runner env set.

B — Healthcheck `depends_on:` cross-app

Resolve o caso multi-app comum: meu-agent não deveria subir antes do meu-livekit estar healthy. Antes era manual (deploys sequenciais com vista nos logs).

instances:
  production:
    depends_on:
      - app: meu-livekit
        condition: healthy           # healthy | started
        timeout: 300s
      - app: meu-redis
        instance: production         # opcional, default = mesma instance
        condition: started

Comportamento: start_container polla docker inspect por cada dep até condition met OR timeout. Bypass: --insecure ignora. Erro instrutivo: dependency_timeout: <app>/<instance> not ready after Ns.

C — `traefik.external.{snippet, write_to}` — slot ocupado

v2.20.0 entregou só o bypass do write local. v2.21.0 enriquece o slot:

# /opt/runner/config.yml
traefik:
  mode: external
  external:
    write_snippet_to: "/opt/traefik-snippets/{app}.yml"
    print_snippet: true

Output em modo external:

  • print_snippet: true (default) → snippet em stderr depois do deploy, copy-pasteable
  • write_snippet_to: <path> → também salva em arquivo (Ansible sync)
  • {app} placeholder no path expande pro project slug

D — `runner add` detecta repo duplicado

UX safety net pro caso real: operador registrou 2 vezes sob nomes diferentes (meu-app-livekit + meu-livekit). Runner aceitou silenciosamente, gerando state divergente.

runner add --repo usuario/meu-app --branch main --ckey "..." ...

Error: duplicate_repo: Repo usuario/meu-app já está registrado
       como `meu-app-livekit` (branch: deploy/livekit, status: registered).

       Opções:
         --force-duplicate            permite criar SEGUNDA app pro mesmo repo
         runner unregister meu-app-livekit --force    remove a anterior

Bypass: runner add --force-duplicate ... (raro; testes A/B, multi-instance experimental).

E — `generate-config` template completo

Operador olhou runner generate-config e concluiu erradamente que ports:/secrets:/type: image não existiam. Template embedded estava incompleto. Agora mostra TODOS os campos válidos do schema, com comentários esclarecendo opcionalidade + version markers (v2.20.0+, v2.21.0+).

Validação

  • 340 unit tests pass (era 328 v2.20.0; +12 do v2.21)
  • Matriz CI/CD: 82 pass / 0 fail / 71 n/a em ~184s (9 estados × 16 ops)
  • 3 ops novas no matrix:
    • build_args_interp (state B): valida {{::Var}} resolution end-to-end
    • depends_on (state B): valida wait timeout em dep não-existente
    • duplicate_repo (state A): valida runner add --repo X re-aceito falha com duplicate_repo
  • traefik_external (já em v2.20) ganhou snippet asserts: confirma stderr emit em mode=external

Backwards compatibility

  • .deploy.yml sem depends_on: → Vec vazio, comportamento idêntico v2.20.x
  • .deploy.yml com {{::Var}} em build.args: agora interpola (era literal em v2.20 — comportamento mais correto, sem quebra esperada)
  • config.yml sem traefik.external: → sub-block opcional, só usado em mode=external
  • runner add em repo já registrado muda comportamento: era silently OK, agora duplicate_repo exit 2. Workaround: --force-duplicate

Backlog v2.22+

  • Sol 2 post_deploy_check schema (smoke pós-deploy configurável)
  • Service discovery dinâmico (env var templating tipo ${app.endpoint})
  • W6 smoke probe (rodar imagem brevemente + detect listen port real)
  • UDP-aware allocator (random strategy só pensa TCP hoje)
  • runner list --orphans (states sem deploy_count > 0)

Backlog v3.0 (acumulando breaking)

  • Compose-style multi-service em 1 manifesto
  • status --json cleanup — remover legacy top-level fields

[2.20.0] - 2026-05-06 — Multi-app polish + CCS schema

Resumo

5 features opt-in que fecham fricções identificadas em deploys multi-service reais (LiveKit + Python agent + Vite frontend) + schema split do runtime status --json pra CCS dashboard. Zero breaking changes — apps v2.19.x rodam sem mudança.

Adicionado

A — `build.args:` no `.deploy.yml` (Vite/Next/Angular zero-touch)

build:
  context: frontend
  dockerfile: Dockerfile
  args:                              # ← NOVO
    VITE_SUPABASE_URL: "https://..."
    VITE_LIVEKIT_WS_URL: "wss://..."
    BUILD_ENV: production

Cada entry vira --build-arg KEY=VAL no docker build. Mata o workaround "build local + commit dist/" pra apps com env vars compile-time. MVP v2.20.0: valores literais. Interpolation {{::Var}} (igual environment:) é follow-up — resolve_value helper público ainda não existe.

B — Auto-create docker network

Quando .deploy.yml declara networks: [meet] e a network não existe, runner cria automaticamente antes do docker run. Idempotente. Built-ins (bridge, host, none) são skipped. Consolidado num único path em containers::ensure_network.

C — `traefik.mode: external | none` (bypass)

# /opt/runner/config.yml
traefik:
  mode: external      # none | local (default) | external
Mode Comportamento
local (default) Escreve em <traefik_dynamic_dir> (v2.19.x)
external Skip do write local — operador trata roteamento externo
none Mesmo bypass — apps internas sem HTTP público

v2.20.0 = só bypass. Slot reservado pra ações futuras (snippet output, write_to). Default local preserva backcompat 100%.

D — `status --json` schema split (aditivo)

Payload do runner status <app> --json ganha 3 blocos lógicos:

{
  "current_version": "abc1234",
  "manifest": { "project": "...", "repo": "...", "branch": "...", "ckey_fingerprint": "ck_...", ... },
  "config":   { "type": "...", "port": ..., "ports": [...], "networks": [...], "hosts": {...}, ... },
  "runtime":  { "current_version": "...", "status": "...", "network": { ... v2.19.0 } },
  "_deprecated_top_level": true,    // marker pra CCS migrar
  // legacy top-level fields preserved (project, system, app_type, instances, state)
}

CCS dashboard pode migrar gradualmente: data.manifest !== undefined → caminho novo. Cleanup de legados em v3.0 (postergado).

E — `hosts:` map (DNS aliases via `--add-host`)

# .deploy.yml
hosts:
  host.docker.internal: host-gateway
  internal-api: 10.0.0.5
  livekit-pub: 10.108.0.5

Cada entry vira --add-host KEY:VAL no docker run. Resolve host.docker.internal em Linux Docker (que não tem por default). Map (não array) — last-wins em duplicatas, override fácil em re-deploy.

Refatorado

  • containers::ensure_network ganhou skip pra docker built-ins (bridge, host, none). Single source of truth — pipeline não duplica mais a lógica.

Validação

  • 328 unit tests pass (era 309 v2.19.0; +19 do v2.20)
  • Matriz CI/CD: 79 pass / 0 fail / 47 n/a em ~170s (9 estados × 13 ops)
  • 4 ops novas no matrix:
    • build_args: valida injection em build-mode states (B/D/I)
    • network_autocreate: valida criação automática de network nova
    • traefik_external: valida que mode=external não escreve local file
    • hosts_check: valida --add-host no docker inspect
  • status_schema.py agora hard check (era permissivo) — falha se manifest/config/runtime faltarem

Backwards compatibility

  • .deploy.yml sem build.args: → BTreeMap vazio, zero --build-arg flags
  • .deploy.yml sem hosts: → BTreeMap vazio, zero --add-host flags
  • config.yml sem traefik.mode: → default local → comportamento idêntico v2.19.x
  • .deploy.yml com networks: [X] e X não existe → v2.19.x quebrava no docker run; v2.20.x cria
  • Consumer do status --json legado → continua lendo top-level (_deprecated_top_level: true marker indica caminho novo)

Nenhuma quebra. Apps registradas em v2.19.x seguem rodando.

Backlog identificado pra v2.21.0

  • Interpolation {{::Var}} em build.args: — precisa expor resolve_single_value como pub em src/environment/mod.rs
  • Healthcheck depends_on: cross-app — multi-app ordering
  • Sol 2 post_deploy_check schema (smoke pós-deploy configurável)
  • Service discovery dinâmico entre apps (env var templating)
  • Sub-bloco traefik.external.{snippet, write_to} — ações concretas em external mode
  • W6 smoke probe (rodar imagem brevemente pra detectar listen port real)

Migration path

Pra apps existentes opt-in:

# Adicionar build.args
echo "  args: { VITE_X: ... }" >> .deploy.yml/build/

# Adicionar hosts
echo -e "hosts:\n  host.docker.internal: host-gateway" >> .deploy.yml

# Mudar pra external mode
echo "traefik:\n  mode: external" >> /opt/runner/config.yml

Sem precisar runner unregister ou --force.


[2.19.0] - 2026-05-06 — Port allocation autônomo (Lote B)

Resumo

Single feature focused: eliminar a única intervenção manual restante no CI/CD do runner — escrever ports: ["VPC_IP:porta:porta"] no .deploy.yml. Runner agora aloca porta livre automaticamente, persiste sticky no state file (re-deploys reusam a mesma porta), e expõe via runner status --json pra CCS consumir.

Bonus: pre-flight de secrets (Sol 1 do feedback de prod) — deploy aborta cedo se secret declarado em .deploy.yml::secrets: tem valor vazio (a menos de --insecure). Encerra o ciclo "deploy aparentemente bem-sucedido mas funcionalmente quebrado" causado por healthchecks que retornam 200 mesmo com secrets faltando (ex: Laravel /up).

Adicionado

Port allocation (config + state)

network: block opcional em /opt/runner/config.yml:

network:
  vpc_ip: 10.108.0.5         # opcional; sem isso bind cai pra 127.0.0.1
  publish_strategy: random   # random | sequential | explicit | none
  port_range: [8000, 8999]
  port_blocklist: []

Estratégias:

  • random (recomendado): sorteia porta livre na primeira deploy, persiste sticky pra re-deploys reusarem
  • sequential: pega a primeira porta livre no range
  • explicit: lê ports: do .deploy.yml (comportamento v2.18.x)
  • none: container roda sem -p no host

Sticky lifecycle:

  1. Primeira deploy: aloca porta → persiste em state/<app>.yml::network.published_port
  2. Re-deploys (commit novo, fetch --deploy): reusa a mesma porta — Traefik externo permanece válido
  3. runner unregister X: libera porta de volta ao pool
  4. runner reset --hard X: libera state → próximo deploy realoca
  5. runner ports realloc X: força nova alocação (operador atualiza Traefik)

Resolução de bind address:

  • network.vpc_ip configurado → bind nesse IP
  • Sem vpc_ip → bind em 127.0.0.1 (safe default — Traefik no mesmo host alcança via loopback)

Comandos novos

runner ports list [--json]    # lista portas alocadas em todas apps
runner ports release <app>    # libera state.network sem parar container
runner ports realloc <app>    # limpa published_port — próxima deploy realoca
runner deploy <app> --port N  # override permanente (escreve no state, força reuso)
runner deploy <app> --no-port # desabilita port mapping (strategy=none) só desta deploy

`runner init` interativo

Quando rodado em TTY, runner init agora pergunta:

  1. "Esse server fica em uma VPC interna? [s/N]"
  2. Se sim: oferece auto-detect via ip -4 -o addr show, confirma o IP
  3. "Strategy [random/sequential/explicit] (default: random)"
  4. Persiste o bloco network: no config.yml

Em modo não-TTY (CI, piped) skipa silenciosamente — backcompat 100%.

`runner status --json` runtime.network

Bloco novo no payload pra CCS consumir:

"runtime": {
  "network": {
    "vpc_ip": "10.108.0.5",
    "published_port": 8473,
    "endpoint": "http://10.108.0.5:8473",
    "allocation": {
      "strategy": "random",
      "allocated_at": "2026-05-06T12:30:00Z",
      "sticky": true,
      "source": "state"
    }
  }
}

Apps legacy sem state.network → runtime.network: null (key presente, valor null).

Pre-flight de secrets (Sol 1 do feedback prod)

Antes de iniciar o container, valida que cada secret declarado em .deploy.yml::secrets: tem valor não-vazio em .runner/secrets.enc (após decifrar). Se vazio:

Error: deploy_blocked: secrets declared in .deploy.yml have empty values: ["APP_KEY"]
       Bypass: rerun with --insecure (NOT recommended).
       Or fill the values:
         runner env set <app> --key APP_KEY --secret --value <value>

Bypass via --insecure (operator opt-out). Resolve o caso onde Laravel /up retornava 200 mesmo com APP_KEY vazio, fazendo o runner declarar deploy bem-sucedido enquanto / retornava 500.

Corrigido

  • Sticky port resolution não filtrava containers do próprio app: sticky::resolve consultava ports_in_use (ss + docker ps) e rejeitava porta ocupada por OUTRO processo — mas não distinguia containers da MESMA app (que estão sendo substituídos). Resultado: re-deploy do mesmo app sob random/sequential strategy hitava port_in_use_by_other. Fix: pipeline filtra ports de containers {project_und}_{instance}_* antes de chamar sticky. Resolve "ou é nosso → reusa" prometido na docstring desde a v2.19.0 inicial.

Backwards compatibility

  • Apps registradas em v2.18.x continuam rodando sem mudança
  • Default de publish_strategy é Explicit quando network block ausente — comportamento idêntico ao v2.18.x (lê ports: do .deploy.yml)
  • State files antigos sem bloco network migram silenciosamente — porta re-alocada no próximo deploy quando strategy != Explicit
  • instances.X.domain continua aceito (sem mudança)

Validação

  • 309 unit tests pass (era 304 + 5 do secrets_preflight + extras de network module)
  • Matriz CI/CD: 90 cells, 73 pass, 0 fail, 17 n/a em 114s
  • 2 ops novas no matrix:
    • network_check: valida porta sticky em range 9000-9100
    • secrets_preflight: valida deploy aborta com secret vazio
  • 9 estados validados ponta-a-ponta sob random strategy: A B C D E F G H I

Backlog identificado (não bloqueia)

  • runner status --json schema ainda flat fora do novo runtime.network — refactor completo (manifest/config/runtime blocks separados) fica pra v2.20.0
  • W6 smoke probe (rodar imagem brevemente pra detectar listen port real do container)
  • Sol 2 do feedback prod (post-deploy smoke test configurável no .deploy.yml) — v2.20.0

[2.18.2] - 2026-05-06 — Wizard "auxiliar inteligente"

Resumo

5 melhorias no wizard/generator que tornam o runner add realmente zero-touch para a maioria dos perfis e auxiliam o operador quando a análise estática não tem como saber a resposta. Construído sobre uma matriz de testes CI/CD (72 cells, 9 estados × 7 ops) que cobre todos os cenários de uso do runner.

Adicionado (W1) — Warning explícito quando porta é incerta

Antes: se o Dockerfile não tinha EXPOSE, o generator silenciava com port: 8080. Para compose, pegava o mapping host:container sem avisar que o container internamente pode escutar em outra porta.

Agora:

  • Dockerfile sem EXPOSE: warning Port: Dockerfile sem EXPOSE; assumindo 8080. Se o container escuta em outra porta, ajuste port: no .deploy.yml
  • Compose com ports:: warning Port: usando porta X do mapping no compose. ATENÇÃO: este é mapping host:container — se o container escuta internamente em outra porta (nginx :80, php-fpm :9000), ajuste port:

Adicionado (W2) — Healthcheck type-aware no generator

Antes: TCP cego em todos os deploys gerados.

Agora detect_default_healthcheck() escolhe baseado no nome da imagem:

Imagem Healthcheck gerado
nginx*, caddy*, httpd*, apache*, traefik* mode: http path: /
php-fpm*, qualquer *fpm* mode: tcp
node*, python*, ruby*, openjdk*, rust* mode: tcp
Outros mode: tcp (fallback seguro)

Adicionado (W5) — Aviso quando Dockerfile + compose coexistem

Antes: silenciosamente usava o Dockerfile, ignorava compose service.

Agora: warning Source: repo tem Dockerfile + docker-compose.yml. Build do Dockerfile foi usado como source-of-truth (compose só pra env vars). Para usar a imagem do compose em vez de buildar, edite type:image + image:

Adicionado (W7) — Detecção de domínio placeholder no wizard

Após o prompt de domínio interativo, warning loud se o valor casa com:

  • *.example.com / .org / .net
  • .test, .local, .localhost, .invalid
  • Markers: placeholder, todo, fixme, xxx, changeme, yourdomain

Não bloqueia (operador pode realmente querer um staging em *.test), só nudge.

Corrigido (Bonus) — `--accept-defaults` rejeita perfil bare

Antes: runner wizard --accept-defaults em repo sem Dockerfile/compose/.env gerava silenciosamente um .deploy.yml inútil.

Agora: bail com no_deploy_profile (mesmo comportamento do runner add).

Corrigido — TCP/HTTP probe IMAGE mode usa `bash -c` no fallback

Bug: probe usava sh -c '... /dev/tcp/... ...' no fallback. Como /dev/tcp é builtin do bash (não do dash/sh), containers Debian-based sem nc falhavam silenciosamente o healthcheck. Quebrava nginx:1, php-fpm:debian, etc.

Fix: o fallback agora invoca bash -c "exec 3<>/dev/tcp/127.0.0.1/PORT" — funciona em qualquer imagem que tenha bash (a maioria das de produção).

Validação

  • 284 unit tests pass (era 283; +1 do placeholder_domain)
  • Matriz CI/CD: 72 cells, 63 pass, 0 fail, 9 n/a em 110s
  • 9 estados validados: A (.deploy.yml), B (Dockerfile), C (compose), D (Laravel), E (bare → falha esperada), F (image-only), G (sem EXPOSE), H (IP-restricted), I (secrets em env)

Backlog identificado pela matriz (não bloqueia)

  1. runner status --json ainda flat — refactor para separar manifest / config / runtime blocks (input pra integração CCS)
  2. W6 (smoke probe pra detectar porta real do container) — alta complexidade
  3. W3 (version source detection: file vs git-commit) — baixa prioridade
  4. W4 (git ls-remote --heads pra listar branches no wizard) — baixa prioridade
  5. W8 (presets por stack: Laravel/React/Flask) — média prioridade

[2.18.1] - 2026-05-05 — Hotfix (6 bugs do loop e simulation)

Corrigido (P0)

  • Pipeline lê repo do state file (Bug E real em prod): migration v2.16.0 era parcial — pipeline ainda exigia github.repo no .deploy.yml e quebrava deploys de apps migrados. Agora resolve do state primeiro, fallback pro manifest legacy. Erro novo no_repo_configured é instrutivo.
  • Wizard --answers.instance_branch propaga até o YAML: era parseado mas hardcoded como branch: main. Agora InstanceSection.branch chega ao render.
  • Wizard recusa sobrescrever .deploy.yml existente (perfil A): bail com manifest_already_exists. Antes gerava defaults silenciosos.
  • Validator aceita type: OR app_type:: antes pedia só app_type: mas o parser canonical e o wizard usam type:.

Corrigido (P1)

  • Mensagem de erro no_deploy_yml: no_deploy_profile: redundante: strip do prefixo interno do generator.
  • runner status --json top-level current_version: campo era NULL no top, populado em 3 lugares diferentes do JSON. Agora atalho do state.current_version.
  • crypto.rs/vault/ckeys.rs aceitam RUNNER_KEYS_DIR env var: paths hardcoded /opt/runner/.keys exigiam root. Agora override opcional pra rootless test/CI; default inalterado pra prod.

Testes

36 unit tests no wizard module + 283 na suite total. Loop validation script: 6/6 pass do zero, incluindo cenário adversarial sem github: no manifest.


[2.18.0] - 2026-05-05 — Lote A (feedback de producao)

Atendendo feedback do deploy real do middleware-241 (Laravel 11). 6 itens.

Corrigido

  • --branch propaga pra instances.X.source.branch (#4): antes, runner add --branch dev clonava de dev mas a instance ficava com main do .deploy.ymlrunner fetch posterior puxava da branch errada
  • runner add --deploy realmente deploya (#5): antes era silenciosamente ignorado com mensagem Auto-deploy requested but disabled. Agora invoca pipeline::deploy() na sequencia

Wizard polish

  • Curadoria de .env.example (#6): detecta APP_ENV=local, APP_DEBUG=true, LOG_LEVEL=debug, NODE_ENV=development → oferece "perfil de producao" (production, false, info); pruning automatico de placeholders comuns (AWS_*, MAIL_*, MEMCACHED_*, PUSHER_*)
  • Deteccao Laravel via composer.json (#7): identifica laravel/framework, analisa routes/api.php + resources/views/ para distinguir system: api vs sys; com Dockerfile -> type: docker-build
  • runner wizard --answers <file> (#8): substitui prompts interativos por respostas em JSON. Habilita IA/script/CI gerar manifesto valido sem TTY. Suporta override de campos do manifesto, env_overrides, skip_env (glob), secret_keys/non_secret_keys (forca classificacao)
  • ckey display por fingerprint (#10): default agora mostra apenas ck_xxxx…xxxx (ckey fica encriptada em state/<app>.yml); flag --ckey-show opt-in para exibir plaintext

Adiado para Lote B/C/D

Itens P0 do feedback (architectural changes):

  • VPC port allocation (network.{vpc_ip, publish_strategy, port_range})entregue na v2.19.0
  • traefik.mode: external + remocao de URL fantasia no output (planejado, não implementado)
  • Backup automatico no unregister (planejado, não implementado)

Detalhes

  • 7 unit tests novos (281 total na suite, 0 falhas)
  • Sem breaking change

[2.17.5] - 2026-05-05

  • gitops fix attempt #5: migra extra_artifacts -> scripts: v3.0 section. Tool web em runner.ccs.systems/tools/secrets.html segue aguardando atualizacao do gitops worker em producao

[2.17.4] - 2026-05-05

  • gitops fix attempt #4: arquivo tools/secrets/index.html na raiz do repo (igual init.sh)

[2.17.3] - 2026-05-05

Corrigido (gitops)

[2.17.2] - 2026-05-05

Corrigido

  • gitops: type: html em extra_artifacts era silenciosamente ignorado. Trocado para type: script

[2.17.1] - 2026-05-05 — F3 Tool web

[2.17.0] - 2026-05-05 — F1 Wizard CLI

  • `runner wizard` — wizard interativo para gerar .deploy.yml (e opcionalmente .runner/secrets.enc)
  • Auto-trigger no runner add em modo TTY quando perfil E (cru)
  • Detecta linguagem (next/react/flask/django/celery/rust/php) → infere system/type
  • Multi-service compose: pergunta qual e o app principal, marca redis/postgres/etc como assets
  • Subcomando runner template inspeciona schema embutido (debug)

[2.16.0] - 2026-05-05 — Lote 5 (#6, #4)

  • Generator de .deploy.yml — detecta perfil A-E e gera manifesto proporcional
  • AppState.repo migrado para state file (deprecates github: no .deploy.yml)
  • Migration silenciosa para apps existentes

[2.15.0] - 2026-05-05 — Lote 4 (#7)

  • runner add nao clona mais o repo na raiz do app dir (causava duplicacao)
  • Apenas .deploy.yml + .runner/state-marker na raiz; codigo vive em pull/ e src/{instance}/{ver}/
  • runner cleanup --reset-root <app> opt-in para apps existentes

[2.14.0] - 2026-05-05 — Lote 3 (#8)

  • runner add/deploy BLOQUEIA secrets em environment: sem ckey configurada
  • Flag global `--insecure` suprime TODAS as travas (banner vermelho + audit log JSONL)

[2.13.0] - 2026-05-05 — Lote 2 (#3)

  • `runner tokens` — list/test agrupado por fingerprint (sem revelar plaintext)
  • runner add --token-from <other-app> reusa token encriptado entre apps

[2.12.0] - 2026-05-05 — Lote 1 (#1, #2, #5)

  • --ckey sem valor gera ckey random; com valor aceita literal
  • --repo aceita URLs (https/ssh/git@) e normaliza para user/repo
  • --branch agora obrigatorio sempre (sem default dist)

[2.11.6] - 2026-05-04

  • Logs vao para stderr (stdout limpo para piping/JSON)
  • Handler SIGPIPE evita panic ao piping para head/jq

[2.11.5] - 2026-05-04

  • self-update lida com chattr +i (immutable bit) automaticamente

[2.11.0-2.11.4] - 2026-04-29 a 2026-05-03

  • v2.11.4: docker run env injection via --env-file
  • v2.11.3: provisionamento de .env/.secrets no bootstrap
  • v2.11.2: bug fix app_has_ckey (secrets nao injetados em redeploys)
  • v2.11.0: vault completo (mkey + ckeys + config-backups + secrets), Ed25519 minisign

[2.10.0] - 2026-04-25

  • runner debug test — validacao end-to-end (config, vault, deploys, validators)
  • Coverage 100% de --json em todos os comandos

[2.9.0] - 2026-04-22

  • CCS Data Contract — alinhamento de output runner snapshot --json para integracao com dashboard CCS

[2.8.0] - 2026-04-20

  • Master key backup: runner mkey export/import para disaster recovery

[2.7.0] - 2026-04-19

  • init.sh — instalador unificado (arch-aware, --rc flag para release candidates)

[2.6.0] - 2026-04-18

  • mode: exit no healthcheck — apps CLI/scanners/jobs one-shot (sem container permanente)

[2.5.0] - 2026-04-18

  • runner init interativo — wizard de setup inicial com --set e --mkey

[2.4.2] - 2026-04-17

  • Deteccao automatica de secrets — Aho-Corasick multi-pattern matching com 3 niveis de confianca (HIGH 95%, MEDIUM 75%, LOW 50%)
  • runner env set inteligente — auto-detecta secrets pelo nome, encripta com ckey ou avisa se plaintext
  • --force no env set — forca como secret sem questionamento (requer ckey)
  • .secrets so com encriptacao — sem ckey o pipeline ignora .secrets, --secret sem ckey retorna erro
  • regenerate fallback — procura key no .env se .secrets nao existe
  • Filtro de falsos positivoskeyboard, primary_key, max_tokens, cache_key, public_key, etc.

[2.4.1] - 2026-04-17

  • runner cleanup --images — remove imagens Docker nao utilizadas de projetos gerenciados pelo runner
  • runner cleanup --build-cache [FILTER] — prune do Docker buildkit cache com filtro de idade (default: 72h, aceita 24h, 168h, all)
  • Limpeza automatica de imagens no pipelinecleanup_old_versions agora remove a imagem Docker de versoes que excedem keep_versions
  • docker image prune -f — executado automaticamente apos cleanup de imagens (dangling layers)

[2.3.0] - 2026-04-16

  • mTLS com tokio-rustls — API HTTP requer client certificates quando habilitado
  • Hot-reload de CA cert — mtime-based, CCS rotaciona cert via SCP sem restart
  • TLS proxy architecture — tokio-rustls acceptor + warp backend ephemeral

[2.2.0] - 2026-04-14

  • runner service — lifecycle CLI para systemd (init, start, stop, restart, status, config, uninstall)
  • runner signal — sinais externos para CI/CD: send (ok/warn/fail + action), clear, list
  • runner serve --service — service mode com API HTTP + scheduler generico
  • API HTTP autenticada — 5 endpoints REST com X-API-Key (/health, /api/v1/status, signal, deploy, apps)
  • API Key com fingerprint — formato rk_{SHA256(hostname+MAC+machine-id)[..8]}_{random}
  • Scheduler generico — trait-based: CanaryJob, TtlCleanupJob, SignalCheckJob
  • service.yml — configuracao separada do service mode (API, scheduler, webhook, mTLS)

[2.1.0] - 2026-04-07

  • Healthcheck honra HEALTHCHECK do Dockerfile — nao sobrescreve com --health-cmd
  • Healthcheck modos tcp: e cmd: — prioridade cmd > tcp > path
  • depends_on: entre apps — topo sort em runner deploy --all, cycle detection
  • Validacao semantica em runner add — build context, Dockerfile, port, healthcheck
  • image_mode: dockerfile aceito como alias de self-contained
  • start_period configuravel no .deploy.yml (default 30s)
  • wait_for_healthy tolerante — nao aborta em Unhealthy transitorio
  • Self-update multi-arch — detecta x86_64/aarch64 e baixa binario correto do CDN
  • Lock file PID-alive — remove locks orfaos automaticamente
  • Mensagens de erro melhoradas — YAML com linha/coluna, token expirado, rollback em 1a deploy

[1.5.0] - 2026-03-17

Adicionado

  • --instance no runner add: Define qual instancia do .deploy.yml esta maquina deve deployar
  • --instance no runner edit: Altera a instancia a qualquer momento, com validacao contra o .deploy.yml
  • Campo instance no state: Persistido no state file, usado pelo auto-deploy
  • Guia Multi-Ambiente: Documentacao completa sobre deploy em multiplas maquinas

Alterado

  • Auto-deploy resolve instancia por prioridade: state.instance > default_instance_name() > "production"
  • runner edit --show: Agora mostra a instancia configurada

[1.4.1] - 2026-03-13

Adicionado

  • max_deploy_retries: Limita re-tentativas de deploy para o mesmo commit que falhou (padrao: 3). Evita loops infinitos em builds quebrados
  • Rastreamento de falhas no estado: Campos last_failed_commit e deploy_retry_count no arquivo de estado da app
  • Rollback automatico em falha de start: Quando o container antigo e parado por conflito de porta e o novo falha ao iniciar, o anterior e reiniciado automaticamente

Melhorado

  • Mensagens de erro nas notificacoes: Extrai a linha de erro real de outputs verbosos (Docker BuildKit, npm, Rust) em vez de truncar no inicio. Limite de 800 caracteres
  • Notificacao de falha mostra rollback: Indica qual versao ficou ativa apos o rollback

[1.4.0] - 2026-03-12

Adicionado

  • runner notify setup: Setup interativo de notificacoes Telegram e Discord
  • runner notify test: Envia notificacao de teste para todos os canais configurados
  • runner notify status: Mostra status dos canais
  • StructuredLogChannel: Audit trail em deploy-events.jsonl
  • NotifyManager v2: Substitui funcoes legacy por multi-channel com suporte a Telegram, Discord e structured logging

Alterado

  • Pipeline usa NotifyManager v2: Substitui chamadas legacy por NotifyManager.send()
  • Config consolidado: GlobalConfig removido, tudo via RunnerConfig

Removido

  • Codigo legacy de notificacoes (send_telegram, send_discord, notify_deploy_*)

[1.2.3] - 2026-03-01

Modificado

Modos de Deploy Simplificados

O Runner agora opera em dois modos exclusivos, determinados pela presenca de image: ou build: no .deploy.yml:

  • Modo IMAGE (image: xxx): usa imagem pronta do registry, monta source via volume
  • Modo BUILD (build: {dockerfile: Dockerfile}): docker build com Dockerfile do repositorio

Isso substitui a logica anterior de image_mode (bind-mount/self-contained) e app_type: docker-build.

A geracao automatica de Dockerfile por stack (generate_dockerfile) foi deprecada. Projetos devem fornecer seu proprio Dockerfile.

Retrocompatibilidade

Configs antigas continuam funcionando:

  • image_mode: self-contained -> tratado como modo BUILD com Dockerfile default
  • app_type: docker-build -> tratado como modo BUILD
  • image_mode: bind-mount + image: -> modo IMAGE (sem mudanca)

Adicionado

  • Deploy lock: Previne deploys concorrentes no mesmo app via .deploy.lock
  • Audit log no pipeline: Deploy registra sucesso e falha no deploy-audit.json com versao, duracao e erro
  • Output estruturado: Modulo output/ com envelope JSON para integracao programatica
  • Campo source.directory: Permite especificar subdiretorio do repo para deploy (ex: directory: . usa raiz inteira)
  • Deteccao de VOLUME em imagens: Override automatico com bind mount explicito para evitar overlay de anonymous volumes
  • Metodo is_build_mode() para deteccao do modo de deploy
  • Metodo is_image_mode() para deteccao do modo de deploy
  • Metodo validate_deploy_mode() para validar configuracao
  • Metodo effective_build_config() para obter config de build efetiva
  • Auto-mount de data/, logs/, keys/ em ambos os modos

Corrigido

  • Audit log backward compat: Campo instance em DeployAuditEntry com serde(default) para logs antigos
  • source.directory ignorado: Campo nao existia em InstanceSourceConfig, serde ignorava silenciosamente
  • Read-only filesystem: Mount de source como :ro impedia Docker de criar mountpoints para VOLUMEs
  • Health check timeout: Usava healthcheck.timeout (per-check) como tempo total de espera. Agora calcula corretamente: start_period + (interval + timeout) * retries + buffer
  • prepare() em build mode: Usa diretorio completo do pull/ ao inves de auto-detectar subdiretorios

Deprecado

  • generate_dockerfile() — projetos devem fornecer Dockerfile
  • write_dockerfile() — nao utilizado pelo pipeline
  • Campo image_mode — usar image: ou build: diretamente
  • app_type: docker-build — usar secao build:

[1.2.2] - 2026-02-19

Adicionado

Comando `runner edit`

Novo comando para editar configuracao de aplicacoes registradas:

# Ver configuracao atual
runner edit runner-docs/front --show

# Mudar branch trackeada
runner edit runner-docs/front --branch dev

O comando inclui validacoes automaticas:

  • Verifica se repositorio git existe
  • Verifica se branch existe no remote
  • Verifica se branch contem .deploy.yml

Melhorias no Cleanup

  • Output colorido com destaque para nomes de containers
  • Flag --verbose para logs detalhados
  • Deteccao de conflitos quando multiplas configs matcham mesmo container

Self-Update com URL

Suporte para atualizar para versao especifica via URL:

runner self-update --url https://runner.ccs.systems/rc/v1.2.2-rc.8/runner

CLI Padronizado

Todos os comandos agora aceitam app como argumento posicional:

# Antes (verbose)
runner fetch --app runner-docs/front

# Agora (direto)
runner fetch runner-docs/front
runner health runner-docs/front
runner logs tail runner-docs/front
runner env status runner-docs/front

Preparacao de Conteudo Melhorada

A funcao prepare() agora suporta repositorios com conteudo na raiz:

  • Detecta automaticamente diretorios: dist/, guides/, content/, docs/, public/, src/
  • Fallback para raiz do projeto quando nenhum diretorio especifico existe
  • Identifica corretamente src/ como estrutura de versoes vs codigo fonte

Corrigido

  • Duplicatas na deteccao de containers orfaos
  • Comparacao de versao usando SHA256 quando versoes sao iguais
  • Conteudo na raiz (ex: guides/) nao era copiado para diretorio de versao
  • Comandos fetch, health, logs, env exigiam flag --app desnecessariamente

[1.2.0] - 2026-02-16

Adicionado

Novos Source Types

Tres novos tipos de fonte para instancias:

Tipo Descricao Uso
tag Deploy de tags Git Releases (v*)
local Deploy de diretorio local Hotfixes, builds externos
image Deploy de imagem Docker pronta Imagens pre-built
instances:
  release:
    source:
      type: tag
      tag_pattern: "v*"

  hotfix:
    source:
      type: local
      path: /builds/hotfix/

  canary:
    source:
      type: image
      image: ghcr.io/org/app:canary

Canary Scheduler (Auto-Promocao)

Sistema de promocao automatica de canary deployments:

instances:
  production:
    canary:
      enabled: true
      auto_promote:
        enabled: true
        initial_weight: 10
        increment: 10
        interval: 5m
        final_weight: 100
        health_check_before: true
      abort_on:
        - health_check_failed
        - error_rate_above:5%
        - latency_p99_above:500ms

Novos comandos CLI:

  • runner canary status <project> -i <instance> - Ver status do scheduler
  • runner canary pause <project> -i <instance> - Pausar promocao
  • runner canary resume <project> -i <instance> - Retomar promocao
  • runner canary abort <project> -i <instance> - Abortar e rollback

Notificacoes v2

Arquitetura extensivel com trait-based channels:

HTTP Webhook - Notificacoes para qualquer endpoint HTTP:

# config.yml
integrations:
  flare:
    type: http_webhook
    url: https://flareapp.io/api/deploys
    method: POST
    auth:
      type: api_key
      key: "${FLARE_API_TOKEN}"
      location: query
      param_name: api_token

Tipos de autenticacao suportados:

  • none - Sem autenticacao
  • api_key - Token em query param ou header
  • bearer - Header Authorization: Bearer
  • oauth2 - Client credentials flow (auto-renew)
  • basic - Username:password
  • hmac - Assinatura HMAC-SHA256

Structured Log - Audit log em JSON Lines:

logs:
  deploy_events:
    type: structured_log
    path: /opt/runner/logs/deploy-events.jsonl
    format: json_lines

Comando `runner serve`

Servidor de triggers para webhooks e cron:

runner serve                    # Auto-detecta modo
runner serve --mode webhook     # Apenas webhook server
runner serve --mode cron        # Apenas cron scheduler
runner serve --mode both        # Ambos
runner serve --port 9000        # Porta customizada

O webhook server:

  • Recebe eventos GitHub/GitLab/Bitbucket
  • Verifica assinatura HMAC-SHA256
  • Aciona deploys automaticos
  • Endpoint de health em /health

CLI Filters por Labels CCS

Filtragem de containers por labels:

runner list --project=meu-app
runner list --instance=production
runner list --project=frontend-* --json

Labels CCS usadas:

  • ccs.systems/project
  • ccs.systems/instance
  • ccs.systems/version

Alterado

Deploy Output Melhorado

Novo formato de output com badges de progresso:

[1/6] [PREPARE]   Preparando deploy...
[2/6] [ARTIFACT]  Copiando artefatos...
[3/6] [ENV]       Gerando ambiente...
[4/6] [SECURITY]  Verificando segurança...
[5/6] [CONTAINER] Subindo container...
[6/6] [PROMOTE]   Promovendo versao...

═══════════════════════════════════════
  ✓ DEPLOY CONCLUIDO
  App:     projeto/production
  Versao:  v1.2.0
  URL:     https://app.projeto.com.br
═══════════════════════════════════════
  • deploy --verbose (-V): Modo verbose com logs tecnicos detalhados
  • deploy --force (-f): Forca redeploy mesmo se versao ja implantada
  • Box de resumo: Exibe app, versao e URL ao final do deploy

Fetch Output em Portugues

Status traduzidos para melhor legibilidade:

Icone Status Significado
atualizado App atualizada
desatualizado Update disponivel
baixado Artefatos baixados
erro Erro no processo
  • fetch output ordenado: Resultados ordenados alfabeticamente por projeto/sistema
  • fetch --force (-F): Nova flag para forcar git pull antes de carregar config

Corrigido

  • Recursao infinita na copia: Skip de diretorios de instancia (production, staging, pr-*) e commit hashes
  • app_type no Dockerfile: Usa config.app_type ao inves de hardcoded "sys"
  • Duracao de healthcheck: Normaliza valores numericos para formato Docker (30 → 30s)
  • Tipo "docs": Novo tipo para imagens de documentacao (MkDocs, PimDocs)

[1.1.13] - 2026-02-16

Alterado

  • fetch output colorido: Cores ANSI para facilitar leitura
    • Verde (✓) para sucesso e "up to date"
    • Amarelo (↑) para updates/pulled
    • Vermelho (✗) para erros
    • Ciano para sugestoes de solucao
    • Dim para informacoes secundarias (versoes)

[1.1.12] - 2026-02-16

Alterado

  • fetch output limpo por padrao: Output mais amigavel para usuarios
    • Logs INFO escondidos por padrao (usa nivel WARN)
    • Formato limpo com alinhamento e indicadores visuais
    • Sugestoes de fix para erros comuns
    • Nova flag --verbose para output detalhado
    • Uso: runner fetch (limpo) ou runner fetch --verbose (detalhado)

[1.1.11] - 2026-02-16

Adicionado

  • fetch --force (-F): Nova flag para forcar git pull antes de carregar config
    • Resolve problema de .deploy.yml desatualizado no servidor
    • Util para migracao de formato legado para v1.1.10
    • Uso: runner fetch --force ou runner fetch -F

[1.1.10] - 2026-02-16

Corrigido

  • Bug 11 - Auto-mount volumes: Respeita volumes explicitos do .deploy.yml
    • Se usuario configura keys:/app/keys:ro, usa essa config
    • Default para keys/ mudou de :rw para :ro (seguranca)
    • logs/ e data/ continuam como :rw
    • Evita erro de mount duplicado

[1.1.9] - 2026-02-16

Corrigido

  • app_type parsing: Alias type/app_type para compatibilidade YAML
  • env_files: Container carrega .env e .secrets automaticamente
  • volumes bind mount: Volumes relativos convertidos para paths absolutos

[1.1.8] - 2026-02-16

Corrigido

  • extract_version(): Suporte a campos nested em TOML (ex: package.version em Cargo.toml)

    • Parser agora detecta secoes [section] e busca chaves dentro delas
    • Compativel com JSON e YAML via dot notation
  • copy_dir_recursive(): Previne recursao infinita durante copia de diretorios

    • Detecta quando o destino esta dentro da origem
    • Pula diretorios de versao (v*) e symlinks
  • docker-build: Suporte completo no pipeline de deploy

    • Nova struct DeployBuildConfig para configuracao de build
    • Metodo is_docker_build() para deteccao de tipo
    • Healthcheck sem curl (usa bash /dev/tcp)
    • Build de imagem a partir do Dockerfile do projeto

[1.1.7] - 2026-02-15

Alterado

  • Storage: Default apps_path alterado de /apps para /data/apps

    • Segue estrutura de storage do ecossistema CCS
    • Compativel com migracao para Kubernetes
    • Configs existentes com apps_path: /apps continuam funcionando
  • Self-Update: Novas flags

    • --rc: Atalho para --channel rc
    • -C, --check: Apenas verifica se ha atualizacao (nao baixa)

[1.1.6] - 2026-02-14

Adicionado

  • Canais GitOps - Suporte a canais stable e rc no .gitops.yml
  • Releases estaveis na raiz: /runner
  • Release candidates em: /rc/runner
  • Versionamento automatico por tag pattern (vX.Y.Z vs vX.Y.Z-rc.N)

Alterado

  • Self-update agora suporta flag --rc para baixar release candidates
  • Estrutura de storage separada por canal

[1.1.5] - 2026-02-14

Corrigido

  • Versao interna do binario corrigida para corresponder a tag

[1.1.2] - 2026-02-13

Corrigido

  • folder_name no .gitops.yml corrigido para processar corretamente

[1.1.1] - 2026-02-13

Alterado

  • URL de download alterada para runner.ccs.systems
  • .gitops.yml atualizado para nova estrutura de storage

[1.1.0] - 2026-02-12

Adicionado

MCP Server (Model Context Protocol)

Implementacao completa do servidor MCP para integracao com Claude Code.

Comandos CLI:

runner mcp serve      # Inicia servidor MCP
runner mcp install    # Instala config MCP
runner mcp uninstall  # Remove config MCP
runner mcp status     # Mostra info do servidor

Tools disponiveis (25+):

Categoria Tools
Deploy runner_deploy, runner_rollback
Apps runner_list_apps, runner_app_status, runner_add_app
Instances runner_instances, runner_versions, runner_destroy_instance
Canary runner_weights, runner_set_weight, runner_promote
Staging runner_stage_deploy, runner_stage_list, runner_stage_destroy, runner_stage_cleanup
Environment runner_env_status, runner_env_set, runner_env_validate
Config runner_validate_config, runner_generate_config
Maintenance runner_fetch, runner_cleanup
Logs runner_logs_history, runner_logs_tail

Resources (9):

  • runner://instructions/* - Documentacao estatica
  • runner://status/* - Status dinamico (apps, staging, config)

Prompts (5):

  • setup_new_project - Setup de novo projeto
  • diagnose_deploy - Diagnostico de falhas
  • canary_workflow - Workflow de canary
  • migrate_config - Migracao para v1.0.0
  • staging_pr - Deploy de PR

Self-Update

Modulo de auto-atualizacao do binario.

runner self-update                   # Atualizar para versao estavel
runner self-update --force           # Forcar reinstalacao
runner self-update --channel rc      # Usar release candidate
runner self-update --path /opt/bin   # Caminho customizado

Features:

  • Download automatico da versao mais recente
  • Suporte a canais (stable/rc)
  • Backup automatico do binario atual
  • Verificacao de integridade

[1.0.0] - 2026-02-05

Alterado (Breaking Changes)

Esta versao remove completamente o codigo legado e padroniza a arquitetura baseada em instancias.

Novo Formato `.deploy.yml`

O novo formato usa instances{} ao inves de containers[]:

# Novo formato (v1.0.0)
project: meu-projeto
port: 8000
networks: [public, mysql]
environment:
  KEY: value
instances:
  production:
    domain: app.site.com
    source:
      type: dist
    keep_versions: 3

Comparacao de mudancas:

Legado v1.0.0
containers[] instances{}
type: front-static (enum) app_type: "front-static" (string)
versioning.keep_versions instances.X.keep_versions
environment.templates environment: {} (HashMap)
deploy.strategy Traefik dynamic config

Mantido

  • InstanceDeployConfig - Estrutura principal
  • GlobalConfig - Config global
  • RunnerConfig - Config em /opt/runner/config.yml
  • Traefik module - Geracao de YAML dinamico
  • Containers module - Docker run/build

[0.6.0] - 2026-02-04

Adicionado

Novos Modulos

  • runner/mod.rs - Configuracao global do Runner
  • traefik/mod.rs - Geracao de YAML dinamico para Traefik
  • containers/mod.rs - Operacoes Docker via docker run/build

Novos Comandos CLI

  • runner init - Inicializa ambiente Runner
  • runner instances <project> - Lista instancias
  • runner versions <project> -i <name> - Lista versoes
  • runner weights <project> -i <name> - Mostra pesos
  • runner weight <project> <inst> <ver> <weight> - Ajusta peso
  • runner promote <project> -i <name> <version> - Promove versao
  • runner destroy <project> -i <name> - Remove instancia
  • runner cleanup - Limpa versoes antigas
  • runner migrate --app <path> - Migra estrutura legada

[0.5.0] - 2026-01-28

Adicionado

  • Comando reset - Reset de deploy
  • Flag --hard - Reset incluindo .env e .secrets
  • Flag --skip-tests - Pula testes apos redeploy
  • Hook post_healthy - Comandos apos health check
  • Variaveis em hooks - ${VERSION}, ${APP_PATH}, ${CONTAINER}
  • Comando stage reset - Reset de staging

[0.4.0] - 2026-01-26

Adicionado

  • Production Compose Generation - Geracao automatica de docker-compose.yml
  • Staging de PRs - Ambientes efemeros para Pull Requests
  • Comando stage deploy - Deploy de PR
  • Comando stage list - Lista stagings
  • Comando stage destroy - Remove staging
  • Comando stage cleanup - Cleanup automatico
  • Template variables - ${PR_NUMBER}, ${REPO}, ${PROJECT}, etc.

[0.3.0] - 2026-01-26

Adicionado

  • Comando add - Registra novas apps
  • Comando fetch - Busca atualizacoes
  • Tipo docker-build - Deploy com Dockerfile customizado
  • Auto Deploy - Configuracao per-app
  • State Management - Persistencia de estado
  • Audit Log - Log global de fetch

Alterado

  • Binario renomeado de cicd-runner para runner
  • Path padrao alterado para /opt/runner/

Removido

  • Webhook server - Substituido por add/fetch

[0.2.0] - 2025-01-25

Adicionado

  • MANUAL.md - Documentacao para clients
  • Webhook server - Endpoint para GitHub push events
  • HMAC-SHA256 - Validacao de assinaturas

[0.1.0] - 2025-01-25

Adicionado

  • CI/CD Runner em Rust - Implementacao inicial
  • Blue-Green Deployment - Teste antes de promover
  • Versioned Deploys - Diretorios versionados
  • Environment Templates - Sistema de templates
  • Custom Deploy Types - wordpress-plugin, mautic, custom
  • Multi-Source Downloads - Download de multiplos repos
  • Telegram/Discord Notifications - Notificacoes
  • Playwright Integration - Testes E2E
  • Automatic Rollback - Rollback em falha
  • Health Checks - Verificacao de saude
By Borlot.com.br on 16/02/2026