`runner ckeys`
Comandos para gerenciar content keys (ckeys) — passphrases usadas para encriptar .env + .secrets num blob .runner/secrets.enc por app, transportável entre servidores.
A ckey de cada app é guardada encriptada com a master key (mkey) em:
/opt/runner/state/{app}.yml— campockey:(apps com ckey legacy desde v2.12)/opt/runner/.keys/ckeys/{id}.json— ckeys standalone (compartilháveis entre apps)
Subcomandos
| Comando | Função |
|---|---|
runner ckeys list |
Lista todas as ckeys (por-app + shared) |
runner ckeys add |
Cria uma ckey standalone para um projeto |
runner ckeys export <id-or-app> |
Exporta o blob sealed (re-importável em servidor com a mesma mkey) |
runner ckeys import-sealed |
Importa um blob sealed |
runner ckeys reveal <id-or-app> |
Revela a ckey plaintext (gated por --insecure, v2.22.7+) |
runner ckeys delete <id> |
Remove uma ckey standalone do vault (v2.23.0+) |
runner ckeys rotate-secret <app> |
Rotaciona a ckey de um app e re-encripta o bundle (v2.53.0+) |
runner ckeys hash <app> |
Hash da ckey que o runner usa, geração e esquema; com --bundle, se o bundle decifra (v2.167.0+) |
`runner ckeys rotate-secret` (v2.53.0+)
Troca a ckey de um app por uma nova e re-encripta o secrets.<instance>.enc
atomicamente. A ckey nova vem via STDIN (nunca por argv, nunca ecoada nem
logada). Dois modos, exclusivos:
| Flag | Fonte da ckey nova |
|---|---|
--to-plain |
Ckey em texto puro via stdin — informada pelo CCS ou pelo usuário. Não passa pelo cofre. |
--to-sealed |
Blob selado do cofre via stdin (verify + unwrap). Requer o contrato do cofre (fase futura). |
# Manual (CCS ou usuário): pipe a ckey nova pra stdin
printf '%s' "$NOVA_CKEY" | runner ckeys rotate-secret meu-app --to-plain --yes -o jsonAtômico com failover/rollback: decifra o bundle com a ckey atual, re-encripta
com a nova, valida, e só então troca state.ckey + ckey_generation. Se o update
do state falhar, o bundle é revertido pra ckey antiga — nunca fica meio-trocado.
Qual bundle (v2.164.0+): a rotação re-cifra e publica o mesmo arquivo do push
normal — o bundle: do manifesto, senão o do target, senão
secrets.<instância>.enc, com o prefixo do serviço em compose.
Prova (JSON): op_id, status (aplicada | ja_aplicada), new_ckey_hash
(hash domain-separated, não a ckey), bundle_sha, bundle_path (caminho no
repo), branch, push_commit, generation e pushed. Contrato all-green: o
comando sai com exit≠0 se o push era esperado (--no-push ausente) e falhou — o
consumidor (CCS) só credita sucesso com pushed: true + bundle_sha conferido no
repo.
| Flag | Efeito |
|---|---|
--yes, -y |
Pula a confirmação (obrigatório em modo não-interativo, ex. CCS) |
--no-push |
Não faz push do bundle re-encriptado (só local) |
--op-id <ID> |
Id da operação, devolvido na prova (v2.167.0+) |
--generation <N> |
Geração da ckey nova (v2.167.0+, ver abaixo) |
-i, --instance <INST> |
Instância do bundle (padrão: a do registro) |
-o json |
Saída da prova em JSON |
Geração (--generation N, v2.167.0+):
| Caso | Resultado |
|---|---|
| N maior que a geração atual | aplica e grava N |
| N igual, com a mesma ckey | status: "ja_aplicada": prova nova do bundle atual, sem re-cifrar |
| N igual com outra ckey, ou N menor | recusa com geracao_obsoleta, sem mudar nada |
| sem a flag | a geração sobe 1 |
A rotação roda sob o lock da app: env set e deploy concorrentes esperam. Se o
lock não vier em 10 s, falha com app_lock_ocupado. App com esquema v2 recusa com
esquema_nao_suportado. Com -o json, um erro sai no stdout como
{"error": "<código>", "message": "…"}, com exit≠0.
`runner ckeys hash` (v2.167.0+)
Mostra o hash da ckey que o runner usa para a app (nunca a ckey), a geração e o
esquema (v1, v2 ou none). Sem ckey, ckey_hash é null.
runner ckeys hash meu-app -o json{"app":"meu-app","ckey_hash":"…","generation":3,"scheme":"v1"}Com --bundle [-i <instância>], diz também se o bundle que a rotação usaria
decifra com a ckey do state:
{"app":"meu-app","ckey_hash":"…","generation":3,"scheme":"v1","bundle_decifra":true,"bundle_sha":"…","bundle_path":".runner/secrets.production.enc"}Bundle ausente: bundle_decifra: false, bundle_sha e bundle_path null.
`runner ckeys list`
Lista todas as ckeys do servidor.
runner ckeys list
runner ckeys list --jsonOutput (human):
Ckeys (2):
------------------------------------------------------------------------------------------
ck_0177…6afd | minha-api | aes-256-gcm | app-state | secrets:8 | ck_0177…6afd
ck_a3b2…7e91 | loja-front | aes-256-gcm | shared | secrets:5 | ck_a3b2…7e91
shared_with: loja-backend, loja-adminNota: o id
ck_xxxx…xxxxcontém o caractere unicode…(U+2026). Para usar em outros comandos, copie-cole — não digite manualmente. Você também pode passar apenas o nome do app desde v2.22.7.
`runner ckeys add`
Cria uma ckey standalone para um projeto. Útil para compartilhar a mesma ckey entre múltiplos apps.
runner ckeys add meu-projeto
runner ckeys add meu-projeto --algorithm aes-256-gcm --jsonA ckey criada fica em /opt/runner/.keys/ckeys/{id}.json, sealed com a mkey do servidor.
`runner ckeys export`
Exporta uma ckey como blob sealed (JSON encriptado com a mkey atual). Importável em outro servidor que tenha a mesma mkey.
# Por id
runner ckeys export ck_0177…6afd
# Por nome do app (v2.22.7+)
runner ckeys export minha-api
# JSON com metadata
runner ckeys export minha-api --jsonOutput (raw): o JSON sealed (uma linha base64 ou bloco JSON).
Use case: migrar um app para outro servidor sem precisar regerar a ckey. Apenas se a mkey estiver sincronizada entre os servidores (via runner mkey export no original + runner mkey import no destino).
`runner ckeys import-sealed`
Importa um blob sealed (de ckeys export) num servidor que tenha a mesma mkey.
runner ckeys import-sealed --blob "$(cat ckey.sealed.json)"
runner ckeys import-sealed --blob /path/to/ckey.sealed.json --project meu-appA flag --project permite renomear durante o import.
`runner ckeys reveal` (v2.22.7+)
Revela a ckey plaintext de um app ou ckey id. Gated por --insecure para evitar leak acidental — o plaintext vai pra stdout.
# Pelo nome do app (recomendado)
runner ckeys reveal minha-api --insecure
# Por id
runner ckeys reveal ck_0177…6afd --insecure
# JSON
runner ckeys reveal minha-api --insecure --jsonOutput (sem --json):
WARNING: plaintext ckey printed below. Store in password manager and clear terminal scrollback.
minha-chave-secreta-do-projetoOutput (--json):
{
"ckey_id": "minha-api",
"plaintext": "minha-chave-secreta-do-projeto",
"warning": "Store this value in a password manager. Anyone with this ckey can decrypt the app's secrets."
}Quando usar
- Recovery: você precisa re-registrar um app num servidor novo (
runner add --ckey "...") mas só tem o servidor velho com a ckey encriptada - Backup manual: copiar a ckey pra password manager antes de descomissionar um servidor
- Debug: verificar qual ckey está em uso
O que precisa estar disponível
- A mkey do servidor (
/opt/runner/.keys/master.key) — para decifrar a ckey sealed - A ckey sealed no state file (
/opt/runner/state/{app}.yml::ckey) ou numa standalone (/opt/runner/.keys/ckeys/{id}.json)
Se a mkey foi perdida
Sem a mkey original, a ckey sealed é irrecuperável. Sua única opção é:
- Re-registrar a app gerando uma ckey nova (
runner add --ckey) - Recriar
.env+.secretsdo zero - Re-encriptar e commitar novo
.runner/secrets.<instancia>.enc
Por isso o runner mkey export deve sempre fazer parte do procedimento de backup do servidor.
Recuperação completa de uma app
Cenário: servidor antigo srv-old tem minha-api com ckey desconhecida. Você quer levar pro srv-new.
# 1. No srv-old: pega a ckey plaintext
ssh srv-old
runner ckeys reveal minha-api --insecure
# Copia o valor pro password manager
# 2. No srv-new: registra a app com a mesma ckey
ssh srv-new
runner add --repo seu/repo --branch main \
--token ghp_xxx \
--ckey "<valor-copiado-do-passo-1>"
# 3. Runner detecta .runner/secrets.<instancia>.enc (ou o flat legado) no repo e restaura .env/.secrets automaticamenteAlternativa (se a mkey for compartilhada entre os servidores):
# 1. No srv-old: export sealed
ssh srv-old "runner ckeys export minha-api" > ckey.sealed.json
# 2. No srv-new: import sealed
scp ckey.sealed.json srv-new:/tmp/
ssh srv-new "runner ckeys import-sealed --blob /tmp/ckey.sealed.json"`runner ckeys delete` (v2.23.0+)
Remove uma ckey standalone do vault (/opt/runner/.keys/ckeys/). Útil quando uma ckey órfã foi criada por engano (ex: runner ckeys add <project> antes de runner add, gerando uma ckey que ficou desconectada de qualquer app).
runner ckeys delete ck_a3b2…7e91
runner ckeys delete ck_a3b27e91 --yes
runner ckeys delete ck_a3b2…7e91 --json| Flag | Default | Descrição |
|---|---|---|
--force, -y, --yes |
false |
Skipa o prompt interativo de confirmação |
--json |
false |
Output em JSON |
Aceita tanto o storage id (ck_<8hex>) quanto o formatted id (ck_xxxx…xxxx). Não toca em per-app state ckeys — para essas, use runner unregister <app> (que remove a app inteira).
{
"action": "ckeys_delete",
"ckey_id": "ck_a3b2…7e91",
"deleted": true,
"deleted_at": "2026-05-16T..."
}A partir de v2.23.0, runner add consulta o vault antes de criar uma ckey nova — se já existe uma shared ckey para o projeto, herda silenciosamente. Isso elimina a fonte mais comum de órfãs.
Veja também
- `runner add` — registrar app com
--ckey - `runner mkey` — gerenciamento da master key
- Secrets e Encriptacao — visão geral do modelo de duas camadas
- Secrets Web Tool — gerador client-side de
.secrets.enc