runner tokens

Gerencia tokens GitHub registrados nas apps. Tokens NUNCA são mostrados em plaintext — apenas fingerprints. Disponivel a partir da v2.13.0.

Sintaxe

runner tokens list [--json]
runner tokens test <app> [--json]
runner tokens reveal <app> --insecure [--json]

E no runner add:

runner add --repo <r> --branch <b> --token-from <APP>

`runner tokens list`

Enumera tokens registrados, agrupados por fingerprint. Apps que compartilham token aparecem juntas no mesmo grupo.

Output humano

Tokens (2):
--------------------------------------------------------------------------------
  tk_a1b2…c3d4 | 3 app(s) | app1, app2, app3
  tk_e5f6…g7h8 | 1 app(s) | app4

Output JSON

{
  "count": 2,
  "tokens": [
    {
      "fingerprint": "tk_a1b2…c3d4",
      "apps_count": 3,
      "apps": ["app1", "app2", "app3"]
    },
    {
      "fingerprint": "tk_e5f6…g7h8",
      "apps_count": 1,
      "apps": ["app4"]
    }
  ]
}

Contrato de seguranca

  • Tokens em plaintext nunca aparecem no output (qualquer formato)
  • Apenas o fingerprint sha256 (12 chars hex, formato tk_4hex…4hex) eh exibido
  • A lista eh derivada lendo cada state file e descriptografando localmente — sem trafego de rede

`runner tokens test `

Valida o token de uma app via GitHub API (GET /user). Desde a v2.158.0, quando a app tem cofre (.runner/secrets.enc), também testa se o token escreve no repositório:

runner tokens test app1
# Token VALID for app1:
#   Fingerprint: tk_a1b2…c3d4
#   Username:    devborlot
#   Scopes:      repo, workflow

Output JSON

{
  "valid": true,
  "app": "app1",
  "fingerprint": "tk_a1b2…c3d4",
  "username": "devborlot",
  "scopes": ["repo", "workflow"],
  "write": true
}

write é true, false ou null (a app não tem cofre ou não deu para testar).

Erros

Cenario Comportamento
App nao tem token configurado Exit 1, erro no_token_configured
Token revogado/invalido Exit 1, output JSON com "valid": false, http_status, error
Erro de rede Exit 1, mensagem do reqwest
Token só lê e a app tem cofre (v2.158.0+) Exit 1, "write": false e error com token_sem_escrita

Como o teste funciona

  1. Carrega state.github_token (encriptado)
  2. Decrypt com mkey atual
  3. Calcula fingerprint pra mostrar
  4. Pergunta à API do provedor do repositório quem é o token:
    • GitHub (github.com): GET https://api.github.com/user, escopos no header x-oauth-scopes;
    • GitLab (v2.192.0+, por https): GET /api/v4/personal_access_tokens/self na instância do repositório, com escopos e validade (expires_at);
    • demais provedores: sem API de token. A validade é provada por um git ls-remote, e scopes_auditaveis sai false.
  5. Com cofre: pede ao servidor git o começo de um push (info/refs?service=git-receive-pack). 200 = escreve, 403 = só lê. Nada é escrito.

Por que a escrita importa

A cada mudança de secret, o runner comita e dá push no .runner/secrets.enc. Com um PAT fine-grained em Contents: Read-only, esse push falha. O cofre do repositório fica com o valor antigo, e uma recuperação do zero (--ckey) traria de volta um segredo já trocado. Desde a v2.158.0:

  • runner tokens set recusa token sem escrita em app com cofre (token_sem_escrita). O token antigo fica intacto.
  • runner tokens audit marca a credencial como no_write e lista as apps.
  • O deploy testa a escrita antes de comitar o cofre. Sem escrita, termina pendente com secrets_push_token_rejected.

No PAT fine-grained, dê Contents: Read and write nos repositórios das apps.

O token plaintext nao aparece em nenhum log/output.


`runner tokens reveal ` (v2.23.3+)

Revela o token GitHub em plaintext para uma app. Gated por --insecure (audit-logged). Mirror do runner ckeys reveal.

runner tokens reveal meu-app --insecure
# WARNING: plaintext GitHub token printed below. Store in password manager and clear terminal scrollback.
#   app: meu-app
#   fingerprint: tk_a1b2…c3d4
# ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

runner tokens reveal meu-app --insecure --json | jq -r .plaintext

Quando usar

  • Operações git ad-hoc fora do runner (clone de inspeção, push de file unrelated)
  • Scripts que precisam do PAT pra chamar GitHub API
  • Debug em emergência

Quando NÃO usar

  • Para reusar o token em outra app no mesmo servidor — use --token-from <APP> em runner add
  • Para teste de validade — use runner tokens test <app>
  • Sem motivo claro — cada reveal vira audit event em /opt/runner/logs/runner.log

Erros

Cenario Comportamento
Falta --insecure Exit 1, mensagem: "tokens reveal prints the plaintext..."
App não tem token Exit 1, no_token_configured
mkey mismatch (token criado com outra mkey) Exit 1, "Failed to decrypt token"

`runner add --token-from `

Reusa o token de outra app ja registrada. Util quando varias apps usam o mesmo token (ex: todas as repos da mesma org).

# Primeiro registro
runner add --repo devborlot/app1 --branch main --token ghp_xxx --ckey
# Reusing token from 'app1' (fingerprint: tk_a1b2…c3d4)

# Apps subsequentes — sem precisar do plaintext do token
runner add --repo devborlot/app2 --branch main --token-from app1
runner add --repo devborlot/app3 --branch main --token-from app1

Comportamento

  • Mutuamente exclusivo com --token (clap rejeita ambos)
  • Decrypt local da mkey + re-encrypt pra nova app, plaintext nunca persiste fora do state file
  • Fingerprint mostrado em stderr pra confirmacao visual
  • Falha com token_source_not_found ou token_source_no_token se a app fonte nao existe ou nao tem token

Erros

Erro Causa
token_source_not_found App fonte nao existe no state dir
token_source_no_token App fonte existe mas nao tem state.github_token
Conflict com --token Clap erro: "argument '--token' cannot be used with '--token-from'"

Quando usar

Cenario Comando recomendado
Identificar quantos tokens distintos estao em uso runner tokens list
Auditar quais apps compartilham um token runner tokens list --json
Verificar se um token ainda eh valido runner tokens test <app>
Registrar nova app reusando token existente runner add --token-from <other-app>
Rotacionar um token compartilhado (manual) gerar novo, runner edit --token em cada app
Recuperar token pra scripts/debug runner tokens reveal <app> --insecure (v2.23.3+)

Veja Tambem

By Borlot.com.br on 05/05/2026