Promessa de estabilidade da CLI
Aplica-se a partir da v0.42.3. Escrita para o
0.x— cada quebra listada abaixo tinha de esperar por um major. A v1.0.0 é esse major: fechou a única quebra pendente (os atalhos de topo), e esta lista passa a valer como a promessa de semver real de resto em diante — um breaking change deixa de caber num1.x.
Um motor sem contrato não se automatiza. Quem escreve um Makefile, um passo de
CI ou um script de deploy precisa de saber o que pode partir num upgrade — e a
resposta «é 0.x, tudo pode partir» é verdadeira e inútil: garante que ninguém
depende de nada, o que é o mesmo que ninguém adoptar.
Isto listava o que se compromete e o que não se compromete dentro do 0.x,
que é o que falta a maior parte dos projectos nessa fase. Desde a v1.0.0 é
mais do que isso: é o contrato de semver do projecto.
Estável — não quebra sem um major
Os verbos de ciclo de vida de container, com os nomes e a semântica que Docker e Podman lhes dão:
container run create ps stop start restart kill rm exec logs attach stats
wait inspect port rename pause unpause
image pull ls remove (rm) tag save load push build (delonix build)
raiz run create ps start stop restart kill rm exec logs attach stats
pull images rmi tag save load push (aliases dos de cima)
wait (polimórfico — ver abaixo)
Concretamente, garante-se:
- O nome do comando e a ordem dos argumentos posicionais.
- As flags curtas e longas listadas acima e os seus significados — em
run:-d,-p,-v,-e,--name,--rm,--net,--restart,--memory,--cpus,--entrypoint,-w,-u,--add-host,--wait,--health-*; emexec:-i,-t,-e,-w,-u. (Esta lista andou achatada, sem dizer a qual verbo cada flag pertence —-i/-tnunca existiram emrun, só emexec; um teste emmain.rsverifica agora as duas listas contra a árvoreclapreal, em vez de confiar na leitura.) - Os códigos de saída — ver a secção «Códigos de saída» abaixo.
- A saída JSON de
inspect— campos podem ser ACRESCENTADOS, nunca removidos nem com o tipo mudado. - Os verbos de topo (
raiz) são aliases dos de grupo —delonix runÉdelonix container run: mesma struct de flags, mesmo handler, mesmos códigos de saída, por construção (não há segundo contrato a partir). Herdam a promessa acima; o que AINDA não se promete é paridade total de flags e comportamento com uma versão de referência de Docker/Podman — essa medição é trabalho em curso, fora deste conjunto.
Quebra de contrato na v1.0.0. Até à v0.69.0 os atalhos de topo (
ps,run,exec,logs,rm,images) estavam aqui, como reescrita de argv paracontainer <verbo>/image list. Saíram — corte limpo, sem alias: a grafia antiga falha comunrecognized subcommand, nunca em silêncio, a mesma regra que a reorganização da v0.30.0 já seguia. Os grupos continuam:delonix container ps,delonix container run,delonix container exec,delonix container logs,delonix container rm,delonix image list(ver a nota da v2.0.0 abaixo — essa grafia mudou outra vez). Os códigos de saída não mudam nesta versão — ver a secção abaixo.Reintrodução na v5.x. Os atalhos de topo VOLTARAM —
delonix run,create,ps,start,stop,restart,rm,exec,logs,pull— por uma razão que a nota da v1.0.0 não pesou: a familiaridade de quem vem de Docker/Podman vale mais do que a pureza de uma CLI sem sinónimos, e o objectivo declarado é trocar o executável e manter o fluxo. NÃO é a reescrita de argv de outrora (a grafia antiga, frágil, que esta mesma nota tinha razão em cortar): cada verbo de topo é agora um subcomandoclapde pleno direito que carrega a MESMA struct de argumentos e despacha para o MESMO handler do grupo — não há segundo caminho de código a divergir, e o--helpé o mesmo módulo a módulo. É por isso que entram na promessa de estabilidade acima, e não numa à parte: o quedelonix container runpromete,delonix runpromete pela mesma linha. O conjunto reintroduzido na primeira passagem foi o dos fluxos mais frequentes.Tabela completada na linha 0.1.0 (o renumber que sucede à baseline histórica v5.1.x). Os restantes verbos da tabela Docker/Podman entraram pelo mesmo mecanismo — subcomando
clapde pleno direito, MESMA struct de argumentos, MESMO handler do grupo: os de containerkill,attach,stats, e os de imagemimages(alias deimage ls),rmi(alias deimage rm),tag,save,load,push. Ficam de fora sólogin/logout— não constam da tabela pedida, e a sua forma de grupo (image login/logout) chega.Comandos genéricos: política de resolução, nunca adivinhação. Três nomes da tabela colidem com significados que este motor já tinha ou que seriam ambíguos; resolvem-se por regra escrita, não por palpite:
waité um só verbo com duas formas, escolhidas pela GRAMÁTICA. Um ou mais operandos posicionais de container ⇒ a formadocker wait(bloqueia até cada um sair e imprime o código de saída — o MESMO handler quecontainer wait). Zero operandos ⇒ a forma de manifesto (delonix wait -f <ficheiro>, a que este motor já tinha). As duas gramáticas são disjuntas; misturá-las — um operando de container junto de-f/--timeout— é RECUSADO em voz alta, nunca resolvido em silêncio. A forma de container herda a promessa do grupocontainer; a de manifesto continua classificada com o resto da superfície de manifesto (NÃO estável).inspectediffficam COM TIPO (type-scoped). Não há umdelonix inspect <nome>genérico que decida sozinho se o nome é um container ou uma imagem:container inspectresolve só containers (JSON dedocker inspect),image describesó imagens,container diffsó containers, edelonix diff/delonix wait -fna raiz são do MANIFESTO. É a mesma regra que mantém separadas operações com efeitos diferentes (remover um recurso vs limpar os não usados): um resolvedor genérico que adivinhasse o Kind é exactamente o que esta CLI recusa fazer.Quebra de contrato na próxima major (ADR-0062).
container runde uma imagem que declaraUSERpassa a correr como esse utilizador, como no Docker e no Podman; até à v4.5.0 corria como uid 0 dentro do container, declarasse a imagem o que declarasse.--user 0(ouuser: "0"num manifesto) é a forma explícita de ficar root. Uma imagem semUSERnão muda. E um container com utilizador não-root deixou de ser dono do seu rootfs e de re-apropriar bind mounts do host: escreve só onde a imagem lhe dá posse, num volume nomeado vazio, ou onde as permissões do host deixam.Quebra de contrato na v2.0.0. O B2 da reestruturação da CLI (v0.67.0) tinha renomeado
image ls/image rmparaimage list/image remove— a única excepção de nomenclatura numa CLI onde as outras 15 folhas do tipo "listar" usamls(network ls,volume ls,vm ls,pod ls,secret ls,stack ls, …), seguindo o padrão Docker/Podman que este mesmo documento já cita para os verbos de container. Revertido:image listvolta aimage ls(image removefica). Corte limpo, sem alias — a grafialistfalha comunrecognized subcommand, nunca em silêncio.
Códigos de saída
Alteração de contrato na v0.49.0. Até à v0.48.0 toda a falha do motor era
1— «não existe» e «rebentou» eram o mesmo número. Continua a valer que0é sucesso e não-zero é falha, portanto umif delonix …; thennão muda de comportamento; o que muda é para quem testa[ $? -eq 1 ]à espera de uma falha específica. Ver a nota de migração no fim desta secção.Alteração de contrato na linha 0.1.0. Com
--detailed-exitcode, umstack plancujo plano tem umConflict— um recurso com o carimbodelonix.io/stackde outra stack — respondia0, porque um conflito não é uma alteração. Passa a responder5(DX-5601), o mesmo que ostack applyrecusaria, e ganha ao2. Um gate escrito como «0passa,2é deriva, o resto é erro» fica certo sem mudança; quem só testava[ $? -eq 0 ]passa a ver vermelho exactamente onde oapplyia falhar. Sem a flag, oplancontinua a responder0.Alteração de contrato na linha 0.1.0 (2). O
stack plan(e oplan) ganhou--prune, e passou a planear com as flags doapply. Sem--prune, um recurso que esta stack possui e o manifesto já não declara continua listado (-), mas como mantido:changed: falseno-o jsone fora do--detailed-exitcode. Antes contava como alteração, e um gate de deriva sem poda respondia2para sempre, porque oapplyque o acompanha nunca o remove. Com--pruneresponde como antes: a remoção é uma alteração (2). Quem fazia o gate dar2com recursos que saíram do manifesto passa a precisar de--prune. Oactioncontinuadelete, e oplanDigestnão muda.Alteração de contrato na linha 0.1.0 (3). O
stack apply --dry-run(e odelonix apply --dry-run, o mesmo caminho) só imprimia o manifesto com os defaults e respondia0sem ler a máquina — um plano que oapplyrecusa passava na pré-visualização. Continua a imprimir o mesmo manifesto no stdout, mas agora corre tudo o que oapplycorre antes da primeira escrita e sai como ele sairia:
5(DX-5601) com um recurso que outra stack carimbou;5(DX-5390) com um--plan-digestque já não é o plano;1(DX-1000) com um Replace ou um Reboot sem--replaceou--reboot, com uma gralha nessas flags, ou com um valor que não tem a forma<Kind>/<nome>, nome ouall;77(DX-7000) quando o root do estado não se deixa ler e preparar.Nenhum recurso é criado nem mudado e nenhuma revisão é gravada. Mas o root não fica intocado: os stores criam as suas directorias e a chave do keyring dos túneis, como o
stack validatejá fazia. Quem queria só o manifesto, sem a máquina, tem odelonix manifest render, que continua a responder0.
| código | significado |
|---|---|
0 |
sucesso |
1 |
falha sem classe própria (o default de sempre) |
2 |
uso inválido (o do clap) — e, em stack plan --detailed-exitcode, «há alterações» |
3 |
o recurso existe mas não está a correr |
4 |
não existe esse recurso |
5 |
conflito — o nome já está tomado; e, em stack plan --detailed-exitcode e stack apply --dry-run, um plano com um recurso que outra stack carimbou (DX-5601) |
69 |
capacidade que este host não tem — uma ferramenta por instalar, um backend indisponível |
74 |
o sistema de ficheiros disse que não — disco cheio, caminho impossível |
77 |
permissão negada — o remédio é uma permissão, e depois repetir |
124 |
o prazo esgotou-se — stack wait --timeout, e o que vier a ter prazo |
3 e 4 não são números inventados: são os códigos de estado do LSB que o
systemctl ainda fala (3 = o programa não está a correr, 4 = não há tal
unidade). 5 não tem convenção por trás — é o número livre seguinte, abaixo da
gama que a shell usa (126/127, e 128+N para sinais).
74 e 77 saíram do balde do 1, e não de nenhuma classe publicada. Até
à v0.66.1 uma falha de I/O respondia 1, que a linha de cima descreve como
«falha sem classe própria» — dar-lhe classe é o que esta tabela existe para
fazer, e um script com *) exit 1 continua a cair no mesmo ramo. O 77 é
recortado do 74 pelo kind do erro, porque é assim que ele chega: medido
contra um state root sem bit de escrita, volumes create e secret create
vêm ambos como permissão negada. É a falha mais accionável que existe — corrige
a permissão e repete — e respondia o mesmo número que um disco cheio.
69 e 124 também não. 69 é o EX_UNAVAILABLE do sysexits.h; 124 é
o que o timeout(1) devolve quando o prazo passa, e está portanto já nos dedos
de quem embrulha um comando num. Entraram porque tinham produtores reais mal
classificados, não para completar uma tabela: um stack wait que esgotava o
tempo respondia 1 — o mesmo número de um apply rebentado, no comando cuja
função inteira é ser lido por CI — e um wg/virt-customize/ngrok em falta
respondia 1 também, indistinguível de um erro de escrita numa flag. As duas
chamadas seguintes de um reconciliador são opostas: esperar mais, ou parar e
instalar alguma coisa.
delonix stack wait -f delonix-manifest.yaml --timeout 120
case $? in
0) ;; # tudo de pé
124) exit 0 ;; # ainda a subir — o pipeline seguinte volta a tentar
69) echo "falta uma ferramenta neste nó" >&2; exit 1 ;;
*) exit 1 ;; # qualquer outra coisa: pára
esac
O número do dicionário: DX-CDNN
O código de saída e o DX_* dizem a classe. O número DX-CDNN diz qual falha
(ADR-0043): o milhar é a classe, a centena o domínio, os dois últimos a falha, e 00 a
própria classe. A CLI imprime-o na linha de erro — error[DX-4501] no such VM: dev — e
delonix explain DX-4501 diz o que significa e o que fazer (--json para scripts). O
dicionário completo está em delonix explain codes e na página
Dicionário de códigos, gerada da mesma tabela.
Um número nunca muda de significado nem é reutilizado: pode acrescentar-se, e uma falha
que deixe de existir fica com o número retirado. A mensagem ao lado pode ser reescrita e é
traduzida; o número é o contrato. Enquanto um sítio do motor ainda não tem entrada própria,
responde com a entrada genérica da sua classe (DX-4000, DX-1000, …).
A identidade textual: DX_*
O número serve quem lê $?. Quem lê texto — um cliente HTTP, um consumidor
de -o json, um pipeline de logs — tem o código DX_*, que é a mesma
classificação noutra grafia:
DX_* |
código | quando |
|---|---|---|
DX_NOT_FOUND |
4 |
não existe esse recurso |
DX_NOT_RUNNING |
3 |
existe, mas não está a correr |
DX_CONFLICT |
5 |
o nome já está tomado |
DX_UNAVAILABLE |
69 |
capacidade que este host não tem |
DX_TIMEOUT |
124 |
o prazo esgotou-se |
DX_INVALID_ARGUMENT · DX_REGISTRY · DX_SYSCALL_FAILED · DX_INVALID_STATE · DX_IO |
1 |
falhas sem número próprio |
Estes nomes são contrato: um código pode ser ACRESCENTADO; um existente nunca muda de grafia nem de significado.
A relação é assimétrica de propósito. Um DX_* mapeia sempre para UM
número — se mapeasse para dois, o $? e o texto contradiziam-se para a mesma
falha. Mas o 1 carrega VÁRIOS códigos, porque é o balde «sem classe própria» e
o texto pode dar-se ao luxo de ser mais fino: cada NÚMERO é uma promessa que tem
de valer o resto do 0.x, enquanto DX_REGISTRY ao lado de
DX_INVALID_ARGUMENT não custa nada. Há teste a exigir as duas metades desta
regra, incluindo que o balde continue a ser um balde.
Hoje o código sai na API de gestão (delonix serve api), como campo
acrescentado ao corpo de erro — {"error": "...", "code": "DX_NOT_FOUND"}. O
campo error não foi tocado: a regra do ADR-0005 (acrescentar sim, remover ou
mudar de tipo não) vale aqui como vale no -o json.
O que continua sem código próprio, e é honesto dizê-lo: permissão negada e
falha temporária foram considerados e ficaram de fora — não há hoje uma
variante de erro que os produza (as falhas de permissão chegam embrulhadas no
errno de uma syscall, e o retry que existe acontece dentro do motor e nunca
chega a quem chama). Publicar um número que nada constrói é um número que nunca
pode voltar.
O que isto resolve é concreto: um reconciliador — o Makefile, o passo de CI, o
ciclo em bash que conduz esta CLI — não conseguia separar «cria, porque falta»
de «pára, porque falhou» sem ler a MENSAGEM de erro. E a mensagem é traduzida
(--l18n=pt), portanto um script que faz grep 'no such' funciona na máquina
onde foi escrito e deixa de classificar num nó com outra locale.
delonix container inspect web >/dev/null 2>&1
case $? in
0) ;; # está lá
4) delonix container run -d --name web nginx ;; # falta — cria
3) delonix container start web ;; # existe, parado — arranca
*) exit 1 ;; # qualquer outra coisa: pára, não adivinhes
esac
O código de um run/exec continua a ser o do WORKLOAD, não o do motor.
run em primeiro plano devolve o código do processo do container, exec o do
comando, e healthcheck sai 1 quando não saudável — as três promessas de
sempre, inalteradas. A consequência a reter: um container que saia 4 sai 4,
logo o $? de um run/exec nunca se lê como uma das classes acima — esse
número foi escolhido pelo workload, não pelo motor.
O que fica de fora, e é honesto dizê-lo: uma pré-condição do host por
satisfazer (uma sessão sem delegação de cgroup, por exemplo) não tem código
próprio — o motor avisa e continua, portanto não há falha para classificar. E
os grupos ainda não estáveis (ver a lista mais abaixo) podem responder 1 onde
um comando estável já responde 4: o workload describe de um nome que não
existe é o caso conhecido.
Migrar
if delonix …; then/|| exit 1/set -e— nada muda.[ $? -eq 1 ]a testar «este comando falhou» — passa a ser[ $? -ne 0 ].[ $? -eq 1 ]a testar «não existe» — passa a ser[ $? -eq 4 ], que é o teste que antes não havia maneira de escrever.
Estável em conteúdo, não em formato
As tabelas de ls/ps. As colunas podem mudar de largura, de ordem ou
ganhar irmãs — são feitas para humanos e medem-se pelo conteúdo real. Um script
que faça awk '{print $3}' sobre elas parte, e isso não conta como quebra de
contrato.
Para automação há -o json, e é ele que é estável: um array JSON por
recurso, campos podem ser ACRESCENTADOS mas não removidos nem com o tipo mudado
(ADR-0005). Verificado a funcionar (2026-09-25, v4.4.0) em container ps,
image ls, volume ls, network ls, vm ls, secret ls, workload ls e no
verbo genérico get <plural> (get pods, get volumes, …), que é o caminho de
listagem dos Kinds sem ls próprio — o pod ls já não existe, e o grupo
storage fundiu-se no volume. Também inspect e -q/--quiet. Excepção
medida: backup ls ainda não aceita -o json.
Uma versão anterior deste documento dizia que
-o jsonestava «por fazer e é a lacuna reconhecida aqui». Estava errado — existe desde a ADR-0005. É exactamente o erro que oparidade-docker-podman.mdabre por corrigir: inferir ausência a partir de um sintoma, sem ir ver.
O schema dos manifestos — estável, e é o que mais importa
Esta secção substitui a linha que dizia que o schema «é aditivo na prática, mas não é uma promessa». Era o compromisso ao contrário: a CLI estava mais protegida que o formato declarativo, quando é o formato que as pessoas põem em git e revêem em PR. Um
delonix-manifest.yamlsó se versiona se se souber que não parte sozinho.
Para os Kinds com spec tipado — Container, Pod, Volume, Network —
garante-se, dentro do 0.x:
- Um campo nunca é removido, nem muda de tipo, nem muda de significado.
- Um campo novo é sempre opcional e tem um default que preserva o comportamento anterior. Um manifesto escrito hoje continua a fazer o mesmo amanhã.
- Um nome antigo que seja renomeado mantém-se aceite como alias (é o que já
acontece com
restart→restartPolicy,options→mountOptions,wg_ip→wgIp). apiVersion: delonix.io/v1só muda com umv2, e umv2não sai sem ov1continuar a ser aceite. Desde a v0.64.0 cada Kind tem também o grupo do seu domínio (compute.delonix.io/v1alpha1,networking.…, …) — odelonix api-resourcesdiz qual é o de cada um. As duas grafias são aceites; a de grupo é a canónica, e o Kind só aceita o grupo dele ou o legado.
A verdade não é este texto, é o schema: delonix manifest schema emite-o a
partir do próprio código (ADR-0007), e o mesmo ficheiro está publicado em
schema/v1/delonix.json. Um teste do repositório
falha se o publicado deixar de ser o gerado, precisamente para esta página não
voltar a poder mentir.
O que o schema RECUSA, e vale saber porque é o que aparece sublinhado no editor:
um campo desconhecido (um typo), um kind que o motor não conhece — um typo
no nome, ou um Kind removido — e um apiVersion que não é o grupo daquele
Kind. Até à v0.65.x o kind era uma string livre e qualquer nome passava, por
isso um kind: Contaner validava limpo e um Egress já removido também; e o
apiVersion era fixo no legado, por isso a grafia de grupo — a canónica — era
sublinhada como se fosse erro. Um validador que discorda do motor é pior do que
nenhum, porque o visto verde é o que as pessoas seguem.
Aponta o editor e escreve manifestos com completação e validação:
# yaml-language-server: $schema=https://ngolacloud.github.io/delonix/schema/v1/delonix.json
apiVersion: compute.delonix.io/v1alpha1
kind: Pod
metadata: { name: web }
spec:
containers:
- name: web
image: nginx:1.27
Há também uma extensão de VS Code que traz isto já ligado, mais um template por
Kind: angolardevops/delonix-vscode.
Aponta para o MESMO ficheiro publicado acima — buscado ao vivo, não embutido —
por isso o editor e o stack apply não podem discordar.
Para saber o que mudou entre duas versões, não há uma página escrita à mão — haveria a segunda fonte de verdade que a ADR-0007 aboliu. Há um comando:
scripts/schema-diff.sh v0.46.0 # dessa tag até à árvore actual
scripts/schema-diff.sh v0.46.0 v0.47.0 # entre duas tags
Compara campo a campo (nome e tipo), não o JSON cru, e sai 1 quando há diferenças — serve directamente como gate de CI. Um campo removido ou com o tipo mudado é assinalado como quebra de contrato, que é o que esta secção promete não acontecer.
Todos os Kinds têm hoje schema gerado. Esta secção dizia que Vm,
Cluster, ShareVolume, Image, Secret, Ingress, FirewallPolicy,
HTTPRoute, Tunnel, Workload e Stack não o tinham — deixou de ser
verdade: delonix explain <Kind> responde para cada Kind que
delonix api-resources lista (verificado a 2026-09-25, v4.4.0), com os nomes
canónicos (VirtualMachine, KubernetesCluster, NetworkPolicy, Gateway; os
nomes antigos Vm/Cluster/FirewallPolicy/Tunnel continuam a resolver como
alias). ShareVolume já não existe: é um kind: Volume com bloco share:. Se
um Kind futuro chegar sem schema, o delonix explain recusa-o a dizer que Kinds
o têm, em vez de o omitir.
Três Kinds deixaram de existir nesta série, fundidos no que já faziam:
Egress→FirewallPolicycomdirection: egress;Dependency→FirewallPolicy(açúcar, reduzido no load);Storage→kind: Volumecom um bloconfs:/cifs:/webdav:. Os nomes antigos continuam a carregar, com aviso de depreciação — a regra do «corte limpo» aplica-se a comandos, e um manifesto em git merece um degrau em vez de um erro.
O ficheiro de providers do nó — config.delonix.io/v1
O providers.yaml (ADR-0054) diz que providers o nó tem, como o motor chega a
cada um e qual serve um pedido que não nomeia nenhum. Desde o catálogo 1.1.0
(ADR-0059 F1) aceita também um appliance OPNsense (type: opnsense) e o bloco
networkDefaults, que diz que provider responde a cada papel de rede
(segment, gateway; nat, ipam e dns são recusados até existir quem os
sirva). Desde o ADR-0064 D6 aceita também type: powerdns: a credencial do
PRÓPRIO motor para um servidor PowerDNS (url, controllers, auth.keyFile,
allowPlainHttp), usada só para remover os registos de gateway que o nó
Proxmox escreve e nunca remove. O defaultProvider continua a ser só o de computação. Procura-se por
esta ordem, e o primeiro ficheiro que existe ganha, sem fusão:
DELONIX_PROVIDERS_CONFIG, $XDG_CONFIG_HOME/delonix/providers.yaml (ou
~/.config/…), /etc/delonix/providers.yaml.
É um formato publicado, com a mesma promessa que o schema dos manifestos dentro
do 0.x: uma chave nunca é removida nem muda de significado, uma chave nova é
sempre opcional, e apiVersion: config.delonix.io/v1 só muda com um v2 que
não sai sem o v1 continuar a ser lido.
A verdade é o schema, gerado dos tipos que o motor lê:
delonix provider config schema, publicado em
schema/v1/providers.json, com o mesmo teste a
falhar se o publicado deixar de ser o gerado. O schema recusa uma chave
desconhecida, outra versão, um tipo de provider que não existe e um segredo
escrito no próprio ficheiro (tokenSecret, password, secret — só por
referência: tokenSecretFile, passwordFile, secretFile, secretRef).
O schema diz se o ficheiro está bem escrito; o
delonix provider config validate diz se ele serve: um
defaultProvider ou um networkDefaults sem entrada no ficheiro, um ficheiro
de token ou de segredo legível por outros utilizadores, uma CA que não existe. Nenhum dos dois contacta um nó.
# yaml-language-server: $schema=https://ngolacloud.github.io/delonix/schema/v1/providers.json
apiVersion: config.delonix.io/v1
defaultProvider: libvirt
providers:
- type: libvirt
- type: opnsense
url: https://fw.example
auth: { keyFile: /etc/delonix/opnsense.key, secretFile: /etc/delonix/opnsense.secret }
networkDefaults:
gateway: opnsense
NÃO estável — pode mudar em qualquer versão
serve cri,serve api,serve node-api,serve docker-api— superfícies de protocolo em construção. Odocker-apipublica a sua cobertura emdelonix serve docker-api --matrix, e é essa tabela que diz o que existe hoje, não esta promessa. A API de gestão (serve api) é local (socket unix, só o próprio uid) e não tem contrato publicado: não construas automação sobre ela — para isso existe a CLI, com-o json. As rotas dela estão congeladas (só correcções) enquanto o contrato local de nó que a substitui é decidido (ADR-0040 e ADR-0041, ambos Proposed); uma promessa para esse contrato só entra nesta página quando a primeira vaga dele fechar. As rotas que mudavam a rede sem o registo do container —/v1/net/firewall/:ip,/v1/net/egress[/:bridge],/v1/net/attach-extra…eDELETE /v1/net/attach/:id/:ip— passaram a responder501comDX-6302: o reapply seguinte desfazia o que escreviam, e só cobriam o IP primário de um container com várias redes. O caminho é a CLI (net ingress,net egress,network connect/disconnect), que passa pelo registo.- O nome do executável
delonixé deste motor. O control plane deixa de produzir um binário com esse nome (ADR 0038 do control plane); umdelonixinstalado por este projecto é sempre a CLI descrita nesta página. compatibility— a mesma tabela doserve docker-api --matrix, mas como verbo de topo e com-o json. Ainda só cobredocker;compose/cri/ocisão superfícies futuras, cada uma com o seu próprio "três estados" por construir. Sem promessa de campos atécompose/cri/ocientrarem — o formato pode mudar de forma para os acomodar.provider—ls/describe/matrix: o que cada provider (libvirt, cloud-hypervisor, proxmox, linux, opnsense) declara contra o catálogo de capacidades (ADR-0050, 1.1.0 desde o ADR-0059), medido no host. Os tipos sãocompute,network,storage,imageegateway; umgatewayresponde às linhas de rede. Os NOMES das capacidades (vm.snapshot.memory,net.namespace-isolation, …) e os seis estados são o que um script lê, e seguem a versão do catálogo (catalog_versionno JSON): uma entrada nova sobe o minor, renomear ou remover sobe o major. As colunas da tabela e a forma do JSON além desses campos podem ainda mudar.cluster,vm,pod,workload,net,systemcontainer— a superfície ainda está a assentar. (O schema dekind: Podé estável, ver acima; o que não é estável é o grupo de comandosdelonix pod.)storage/sharevolumedeixaram de ser grupos (#216, «fold storage/sharevolume into volume») — esta secção continuou a nomeá-los durante um tempo depois de já não existirem, o que é exactamente o erro que a lista mais abaixo existe para impedir (um nome classificado que já não é um grupo real). A superfície deles vive hoje emdelonix volume.volume,network,secret— só a saída-o jsone (paraVolume/Network) o schema do manifesto têm promessa (ver as duas secções acima); o resto da superfície imperativa (create/rm/inspecte as suas flags) nunca foi revisto para uma promessa própria.volumeem particular acabou de absorverstorage/sharevolume(#216) — está literalmente a meio de assentar, o pior momento possível para prometer nada sobre ele.apply,plan,wait,stack,manifest,get,describe,delete,diff,drift— o ciclo de reconciliação nativo (IaC, v0.47.0+). O SCHEMA dos manifestos que estes comandos consomem já é estável (secção acima); os VERBOS e flags destes comandos em si (--detailed-exitcode,--prune,--replace, …) nunca tiveram revisão própria, e a Kind mais recente (Service) ainda está a ganhar wiring nalguns destes caminhos.migrate—migrate assesslê um ficheiro compose que já tens e diz o que este motor serve, recusa e não implementa. É um RELATÓRIO (não cria, não puxa, não corre), e a tabela por trás dele é a docompatibility compose— uma medição com data, que se move quando o motor ganha chaves.compose— suporte nativo adocker-compose.yml, ainda a ganhar chaves do Compose Spec (profiles,configs/secrets, multi-ficheiro — ver oAGENTS.mdpara a lista completa do que falta).system,dashboard,completion,init,man,config,explain,api-resources,version— utilitários/introspecção, sem promessa declarada em nenhuma versão.hosts—hosts syncpublica no/etc/hostsos nomes de serviço dos containers expostos (ADR-0048); superfície nova, sem promessa ainda.mcp— o servidor Model Context Protocol (ADR-0025), superfície nova. O transportestdioé o suportado (um processo filho do cliente de IA por sessão, nunca um daemon); um transporte HTTP local, se vier a existir, seria loopback-only com um token local, como oserve apijá é local-only e sem contrato publicado — não construas automação sobre ele ainda.policy—delonix policy unset(M04,docs/roadmap/ 13-improvements-traceability.md), o único verbo imperativo dekind: RuntimePolicy. Superfície nova, sem promessa declarada — opolicy.jsonque ele apaga já existia e já era lido porcontainer run/vm create; o que é novo é a forma declarativa (stack apply) e este comando de remoção. A leitura passa pordelonix get runtimepolicies/delonix describe runtimepolicy <nome>, os verbos genéricos, que herdam a promessa (ou falta dela) desses verbos e não uma própria deste grupo.backup— o grupo não estava declarado de nenhum dos lados, e essa omissão é ela própria um defeito: quem quisesse saber se podia depender dedelonix backup vm xnão tinha resposta. Fica NÃO estável enquanto os arquivos ganham verbos. O formato do arquivo é outra coisa e tem a sua própria guarda: obackup.jsontraz um número de versão e um leitor que não o conheça RECUSA em vez de adivinhar uma disposição que nunca viu.
Mudou na v0.67.0: delonix backup <kind> <nome> passou a
delonix backup create <kind> <nome>, e o delonix restore de raiz passou a
delonix backup restore <arquivo> — sem o <kind> posicional, que o arquivo
já regista (continua a poder afirmar-se com --kind para que uma discordância
seja recusada). O agendamento saiu de flags do backup para
delonix backup schedule, com a mesma semântica: instala o temporizador e
tira o primeiro arquivo. Corte limpo, sem aliases — a forma antiga falha com
«unrecognized subcommand», nunca em silêncio.
delonix system backup não foi tocado nessa data. É outro âmbito — o
state root inteiro de um nó — e não uma segunda porta para este grupo.
Mudou depois (Sprint 4 da reestruturação da CLI, 2026-09-12): o próprio
system backup/system restore foi renomeado para system snapshot
create/system snapshot restore — mesmo vocabulário de vm snapshot/
volume snapshot (captura num instante), só que à escala do nó. O ÂMBITO
não mudou nem se fundiu com este grupo — só a PALAVRA partilhada («backup»)
desapareceu, que era a fonte real da confusão que o parágrafo anterior já
andava a explicar por escrito. Corte limpo: a forma antiga falha com
«unrecognized subcommand».
* gitops (reconcile/status/suspend/resume, ADR-0021) — grupo novo
a 2026-10-12, NÃO estável. Age sobre uma kind: GitOpsSource que já existe;
a fonte declara-se com stack apply, como qualquer outro Kind. O registo e o
estado de cada passagem em $DELONIX_ROOT/gitops/ são estado interno (lê-se
com gitops status -o json, nunca do disco).
* oci-runtime (create/start/state/kill/delete, ADR-0080) — a CLI
compatível com o runc: o init do próprio motor por trás do contrato do runc,
para poder ocupar o lugar do runc/crun. Superfície nova e EXPERIMENTAL — o
contrato que ela imita é o do runc, não uma promessa própria deste motor, e
os verbos/estados podem ainda mudar enquanto a conformidade assenta.
* Tudo o que começa por net netns — plumbing interno exposto por
conveniência de depuração.
* O formato dos ficheiros de estado em $DELONIX_ROOT. Lê-se pelo inspect,
nunca do disco.
* stack history, stack rollback e o conteúdo de $DELONIX_ROOT/stacks/ (ADR-0019). É um
registo do que foi aplicado, e o desenho promete explicitamente que nada o
lê para decidir o que existe: apagar essa pasta não muda o que o plan, o
apply, o prune ou o destroy fazem — só perde o histórico, e há gate na
bateria E2E a exigi-lo. Automação que dependa da presença de uma revisão está
a apostar contra essa promessa.
Como uma quebra é feita, quando tem de acontecer
Precedente já cumprido pelo projecto: a reorganização da v0.30.0 (netns →
net netns, cri → serve cri, …) foi um corte limpo, sem aliases — a
forma antiga falha com «unrecognized subcommand», nunca em silêncio.
Isso mantém-se como regra: falhar alto. Um alias de compatibilidade que muda de comportamento é pior que um erro.
E uma lição que custou esta sessão: essa quebra deixou um chamador INTERNO por
actualizar (o delonix-cri continuou a invocar delonix netns attach), o que
partiu a criação de pod rootless durante meses — ver
cri-conformance.md. Um corte limpo obriga a fazer o grep
por chamadores em TODO o workspace, não só na documentação.