API HTTP endpoints
O Tatuzim Server expoe 3 superficies HTTP, separadas por porta e nivel de autenticacao.
Portas e niveis de auth
| Porta | TLS | Auth | Rotas |
|---|---|---|---|
| 8080 | nenhum | nenhuma | GET /health |
| 8444 | sem TLS* | enrollment token | POST /v1/enroll |
| 8443 | HTTPS strict | mTLS (cert step-ca) | POST /v1/certs/issue, GET /v1/manifest, POST /v1/events |
* Em prod, expor 8444 atras de reverse proxy (Traefik) com TLS Let's Encrypt termination.
Endpoints
`GET /health` (porta 8080)
Liveness probe. Sem auth.
Request: nenhum body.
Response 200:
{
"status": "ok",
"version": "0.1.0"
}Uso: load balancer health check, Kubernetes liveness probe, monitoring.
`POST /v1/enroll` (porta 8444)
Bootstrap de identidade pra um novo agent. Valido apenas com token one-time.
Request body (JSON):
{
"token": "EXEMPLO_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"hostname": "vps-mautic-01",
"role": "mautic",
"csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIB...=\n-----END CERTIFICATE REQUEST-----\n"
}Response 200:
{
"agent_id": "00000000-0000-4000-8000-000000000001",
"cert_pem": "-----BEGIN CERTIFICATE-----\nMIIC...=\n-----END CERTIFICATE-----\n",
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIC...=\n-----END CERTIFICATE-----\n"
}Response 401 — token_not_found:
{ "error": "token_not_found" }Causas:
- Token nunca existiu
- Token consumido em enrollment anterior
- Token expirou (TTL)
Response 401 — token_consumed:
{ "error": "token_consumed" }Token foi usado antes (single-use).
Response 401 — token_expired:
{ "error": "token_expired" }Response 403 — hostname_mismatch:
{ "error": "hostname_mismatch" }Hostname no request nao bate com o vinculado ao token.
Response 403 — role_mismatch:
{ "error": "role_mismatch" }Response 502 — stepca_failed:
{ "error": "stepca_failed" }Server nao conseguiu chamar step-ca (problema na CA, JWK errado, network).
Validacoes:
SHA256(req.token)existe emenrollment_tokensconsumed_atainda NULLexpires_at > now()expected_hostname == req.hostnameexpected_role == req.role- CSR e parseavel (apenas verificacao basica — CN nao e validado aqui)
Apos sucesso:
- INSERT
agentes(id, hostname, role, cert, ...) - UPDATE
enrollment_tokens SET consumed_at = now()
`POST /v1/certs/issue` (porta 8443, mTLS)
Agente autenticado pede novo cert (renovacao ou issue de cert pra outro hostname — futuro).
mTLS requerido: client cert assinado pela CA do step-ca, com CN = agent hostname.
Request body:
{
"csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\n..."
}Response 200:
{
"cert_pem": "-----BEGIN CERTIFICATE-----\n...",
"ca_pem": "-----BEGIN CERTIFICATE-----\n..."
}Response 400 — csr_invalid:
{ "error": "csr_invalid" }CSR nao parseavel.
Response 403 — csr_cn_mismatch:
{ "error": "csr_cn_mismatch" }CN do CSR != hostname do agent autenticado. (No MVP, agent so pode pedir cert pra si mesmo.)
Response 502 — stepca_failed:
{ "error": "stepca_failed" }Uso tipico:
tatuzim-agent rotate(renovacao manual)- Loop interno do
run(renovacao automatica perto da expiracao) - Processamento de carga
csr_cert(gera cert pra escrever em out dir)
`GET /v1/manifest` (porta 8443, mTLS)
Lista entregas pendentes pro agent autenticado.
Request: sem body.
Response 200:
{
"entregas": [
{
"id": "00000000-0000-4000-8000-000000000010",
"carga_id": "00000000-0000-4000-8000-000000000020",
"tipo": "csr_cert",
"nome": "traefik-cert",
"versao": 1,
"status": "pendente"
}
]
}Response 200 sem cargas:
{ "entregas": [] }Filtro: server filtra automaticamente — agent so ve cargas onde entregas.agente_id == este agent e status pendente ou em_andamento.
Tipos suportados no MVP:
csr_cert— agent processa viaprocess_csr_certselado(futuro v0.2) — agent loga warn e skippush(futuro v0.3) — agent loga warn e skip
`POST /v1/events` (porta 8443, mTLS)
Agent reporta evento de lifecycle. Insere em audit log com hash chain.
Request body:
{
"tipo_evento": "entrega_instalada",
"payload": {
"cert_path": "/var/lib/tatuzim-agent/out/traefik-cert.crt",
"key_path": "/var/lib/tatuzim-agent/out/traefik-cert.key",
"carga_nome": "traefik-cert"
},
"entrega_id": "00000000-0000-...",
"request_id": null
}Response 200:
{
"evento_id": "00000000-0000-4000-8000-000000000003",
"hash_atual": "0000000000000000000000000000000000000000000000000000000000000000"
}Tipos de evento conhecidos (tipo_evento):
| Tipo | Emitido por | Quando | Payload tipico |
|---|---|---|---|
agent_started |
agent | boot do run |
{"hostname": "..."} |
cert_renewed |
agent | apos rotate (auto ou manual) | `{"old_serial", "new_serial", "trigger": "auto_threshold" |
entrega_instalada |
agent | apos process_csr_cert ok |
{"cert_path", "key_path", "carga_nome"} |
entrega_falhou |
agent (futuro) | apos falha de processamento | {"erro": "..."} |
manifest_pulled |
agent (futuro) | a cada poll | {"count": N} |
Side-effects no server:
- INSERT
eventos(com hash_atual = SHA256(hash_anterior || payload || timestamp)) - UPDATE
agentes.last_seen_at = now() - Se
tipo_evento∈{entrega_instalada, entrega_entregue, entrega_falhou}Eentrega_idpresente: UPDATEentregas.status
Middleware mTLS
Endpoints na porta 8443 (/v1/certs/issue, /v1/manifest, /v1/events) passam pelo middleware require_authenticated_agent:
rustlsvalida cert do cliente (chain ate root CA do step-ca)- Middleware extrai CN do client cert (
extract_cn) find_agente_by_hostname(cn)→ busca no DB- Se ausente → 401
agent_not_registered - Se
revoked_at IS NOT NULL→ 401agent_revoked - Injeta
Arc<Agente>comoaxum::Extension - Handler recebe via
Extension<Arc<Agente>>
Headers HTTP esperados
Todos os endpoints aceitam:
Content-Type: application/json(mandatorio em POST)User-Agent: tatuzim-agent/0.1.0(informativo)
Nenhum header de auth — autenticacao e via mTLS (cert do cliente).
Exemplo de chamada manual (com curl)
# Read identity
CERT=/var/lib/tatuzim-agent/identity/agent.crt
KEY=/var/lib/tatuzim-agent/identity/agent.key
CHAIN=/tmp/agent-chain.pem
# Bundle cert + intermediate (server precisa da chain)
sudo cat "$CERT" /var/lib/tatuzim-agent/identity/ca.pem > "$CHAIN"
sudo chmod 0644 "$CHAIN"
# Manifest
curl --cert "$CHAIN" --key "$KEY" \
--cacert /etc/tatuzim-agent/stepca-root.pem \
https://tatuzim.dev.borlot.com.br:8443/v1/manifest
# Renew (CSR pode ser gerado via openssl req)
openssl req -new -newkey ec:<(openssl ecparam -name prime256v1) -nodes \
-keyout /tmp/new.key -out /tmp/new.csr -subj "/CN=vps-01" -batch 2>/dev/null
CSR=$(cat /tmp/new.csr)
curl --cert "$CHAIN" --key "$KEY" \
--cacert /etc/tatuzim-agent/stepca-root.pem \
-X POST -H "Content-Type: application/json" \
-d "$(jq -nc --arg c "$CSR" '{csr_pem:$c}')" \
https://tatuzim.dev.borlot.com.br:8443/v1/certs/issue
# Event
curl --cert "$CHAIN" --key "$KEY" \
--cacert /etc/tatuzim-agent/stepca-root.pem \
-X POST -H "Content-Type: application/json" \
-d '{"tipo_evento":"manifest_pulled","payload":{"count":0}}' \
https://tatuzim.dev.borlot.com.br:8443/v1/eventsVersionamento da API
- v1 atual: endpoints
/v1/... - Mudancas breaking → nova versao (
/v2/...) - Mudancas backward-compat → mesma versao
Roadmap
| Endpoint | Versao |
|---|---|
GET /v1/blobs/<id> (download de blob selado) |
v0.2 |
POST /v1/push/<agent_id> (server-initiated push) |
v0.3 |
| Admin API (revoke agent, list cargas, etc.) | v0.4 |
| OpenAPI spec | v0.4 |