Contrato de monitoramento
Dois tipos de heartbeat. Esta página cobre o monitoramento de uma extensão do Chrome registrada: seu agente envia o relatório para POST /api/v1/monitoring/report. Os ambientes de um projeto web usam no painel um endpoint separado, POST /api/v1/heartbeat/report, com outro token. O relatório de ambiente mínimo é {"overall":"ok"}; um payload de métricas personalizadas v2 também é aceito. Não troque um endpoint pelo outro ao configurar um agente. A seção 8b descreve o heartbeat de um ambiente.
O monitoramento mantém sua extensão sob vigilância constante: um semáforo ao vivo no cartão dela na lista, uma aba base monitoring no Detalhe e uma mensagem de status separada no Telegram com alertas de mudança de estado. Ele combina dois componentes ativados de forma independente: um heartbeat por push (um agente no host do seu serviço envia relatórios em um intervalo declarado; o próprio silêncio já é um sinal) e pings de disponibilidade (o painel verifica a disponibilidade das suas URLs públicas).
1. Como funciona (dois componentes, um semáforo)
- Heartbeat por push: um script cron no seu host (por exemplo, uma versão adaptada de
monitor.sh) faz POST de um relatório de métricas v2 para o painel no intervalo que você declarou. Todo relatório deve trazer o veredito reservadooverall. Relatórios ausentes elevam o semáforo (chave de homem morto): se o host, a rede ou o cron caírem, você percebe justamente porque as chamadas param. - Pings de disponibilidade: o próprio painel consulta periodicamente as URLs públicas que você configura (site, api, docs) e verifica apenas a disponibilidade, nunca o conteúdo. Não é preciso agente: basta uma URL.
Uma extensão está "em monitoramento" quando o monitoramento está ativado e pelo menos um componente está configurado. Caso contrário, o cartão dela mostra um ponto cinza neutro.
1b. Heartbeats e o conjunto de slots
O monitoramento fica no nível da extensão, em duas listas planas independentes que compartilham um único conjunto de slots: heartbeats (produtores por push, antes chamados de "stands") e
pings de disponibilidade (verificações por pull). Uma extensão pode ter vários heartbeats, por exemplo um host principal e um worker pago. Cada heartbeat envia relatórios com seu próprio token bearer e declara a própria apresentação (abas, seções e linhas do Telegram) por meio do seu label. Os pings de disponibilidade são uma lista plana separada que pertence à extensão, sem ficar aninhada em nenhum heartbeat. O painel junta tudo em um único semáforo agregado (pior caso), uma única superfície Detalhe e uma única mensagem-resumo no Telegram.
Heartbeats e pings compartilham, em cada extensão, um único conjunto de slots: um heartbeat ocupa um slot, um ping ativo ocupa um slot, e a soma não pode exceder o max_slots do plano (Trial 3 · Studio 10 · Fleet 25). Ultrapassar o conjunto retorna 409 slot_limit; após um downgrade, o excedente é suspenso (pings primeiro, heartbeats por último). A resposta traz slots_used e max_slots para que a interface possa mostrar o saldo disponível.
2. O semáforo
O agregado é o pior caso consolidado de todos os componentes de heartbeat ativados e do semáforo de cada ping ativo, em que as severidades se ordenam como ok < warn < crit. O componente de um heartbeat é max(reported, watchdog): reported é a pior severidade entre a métrica overall e os limites ultrapassados (6.2) do último relatório aceito ; watchdog é um piso de atualidade calculado pelo relógio de recebimento do próprio painel (o desvio de relógio do agente não causa problema).
| Estado | Cor | Quando |
|---|---|---|
off | ⚪ cinza | Monitoramento não ativado ou nenhum componente configurado. |
paused | ⚪ cinza | Ativado, mas congelado (bloqueio do plano ou pausa manual). Os relatórios continuam sendo armazenados. |
green | 🟢 | Todos os componentes ok. |
yellow | 🟡 | Pior componente warn. |
red | 🔴 | Pior componente crit. |
A severidade canônica é ok | warn | crit. Aliases amigáveis são normalizados na entrada: warning vira warn; error e critical viram crit.
3. Watchdog (heartbeats perdidos)
Você declara o intervalo esperado de relatórios (de 60s a 24h) ao ativar o heartbeat. A partir daí, o watchdog impõe um piso ao semáforo conforme a atualidade dos relatórios, com grace = max(60s, 25% of the interval) para absorver a variação do cron e da rede:
| Idade do último relatório aceito | Piso do watchdog |
|---|---|
| até intervalo + tolerância | ok (verde) |
| 1 relatório perdido (até 2 × intervalo + tolerância) | warn (amarelo) |
| 2 ou mais relatórios perdidos | crit (vermelho) |
Um relatório rejeitado (401 / 413 / 422 / 429) nunca renova o watchdog, então um agente com defeito escala de forma honesta; o último motivo de rejeição aparece nas configurações de monitoramento. A recuperação é instantânea: o primeiro relatório aceito recalcula o semáforo na hora.
4. Pings de disponibilidade
Um ping é uma verificação de disponibilidade nomeada de uma URL https pública. O painel executa GET com um timeout de 10s, segue no máximo 5 redirecionamentos, não lê o corpo além de um limite mínimo e trata um status final da classe 2xx como sucesso (204 e 206 também contam). Um status final 3xx (loop ou limite de redirecionamentos), 4xx, 5xx, timeout ou erro de DNS/TLS/rede é falha. Uma verificação com falha é confirmada por uma nova tentativa após ~15s antes de contar, o que absorve oscilações isoladas de rede.
| Falhas consecutivas confirmadas | Semáforo do ping |
|---|---|
| 0 | ok |
| 1 | warn |
| 2 ou mais | crit |
Os resultados dos pings aparecem como métricas próprias do painel ping:<name> na seção
pings (Uptime) da aba base
monitoring e como a linha reservada do Telegram pings: 🟢 api 210ms · 🟢 site 90ms · 🔴 docs (timeout). O número de pings por extensão e o intervalo mínimo dependem do plano:
| Plano | Pings de disponibilidade por extensão | Intervalo mínimo de ping |
|---|---|---|
| Trial | 3 | 5 min |
| Indie | 3 | 5 min |
| Studio | 10 | 1 min |
| Fleet | 25 | 1 min |
A disponibilidade do monitoramento e o número de slots dependem do seu plano; o intervalo mínimo de heartbeat é de 60s.
5. Como colocar uma extensão em monitoramento
Na extensão, em Configurações, bloco de monitoramento:
- Ative o monitoramento, se o seu plano incluir.
- Heartbeat: ative-o e declare o intervalo de relatórios; o painel emite um token bearer, o token de monitoramento, exibido uma única vez (você pode rotacioná-lo a qualquer momento; o token antigo passa a receber 401 na hora). Coloque o token e a URL do endpoint no ambiente do seu agente.
- Pings: adicione URLs públicas nomeadas (por exemplo,
site,api,docs) com um intervalo próprio para cada ping, até o limite do plano. - Telegram: ative a entrega do monitoramento e, se quiser, defina um
6. O formato do relatório
O relatório é o mesmo payload v2 orientado por metadados do
contrato de métricas personalizadas (abas, seções, linhas do Telegram, métricas), enviado para POST /api/v1/monitoring/report com Authorization: Bearer <monitoring token>. O monitoramento acrescenta ids reservados:
| ID reservado | Status | Significado |
|---|---|---|
overall | obrigatório | Métrica do tipo status: o veredito do tick, severidade ok | warn | crit. Alimenta o agregado. Um relatório sem um overall válido é rejeitado (422). |
issues | recomendado | Métrica do tipo list: os problemas atuais, items no nível superior da métrica. Até 5 itens são citados nos textos de alerta. |
ping:* | proibido | Namespace de ids de métrica das métricas de ping do próprio painel; enviar métricas nele é rejeitado (422). |
pings | proibido | Id de linha do Telegram da linha de ping do painel; enviá-la é rejeitado (422). |
monitoring | aba do próprio painel | Id da aba base. As métricas podem referenciá-la (dashboard.tab: "monitoring") sem declará-la em tabs; uma declaração é ignorada. |
base | aba do próprio painel | O id da aba Basic, reservado da mesma forma: uma declaração é ignorada. |
Limites: payload de no máximo 64 KB, no máximo 200 métricas, no máximo 2 relatórios por minuto por fonte (o excesso recebe 429 e não conta como heartbeat).
Um corpo acima de 64 KB recebe 413 payload_too_large e nunca é cortado nem lido como um relatório menor. Um relatório que viola as regras acima recebe 422 invalid_report com todos os problemas listados. Os dois motivos ficam guardados como o último erro do heartbeat:
HTTP/1.1 413 Payload Too Large
{ "code": "payload_too_large", "message": "report is over 64 KB; send fewer metrics or split them across heartbeats",
"limit_bytes": 65536, "size_bytes": 71680 }
HTTP/1.1 422 Unprocessable Entity
{ "code": "invalid_report", "message": "report rejected",
"errors": [ "metric \"overall\" must have kind \"status\" (got \"text\")",
"metric id \"ping:x\" uses the reserved \"ping:\" namespace" ] }6.1. Relatório de exemplo
{
"schema_version": "2.0",
"generated_at": "2026-07-12T10:19:58Z",
"tabs": [ { "id": "stand", "title": "Stand", "order": 5, "icon": "server" } ],
"sections": [ { "tab": "stand", "id": "docker", "title": "Docker", "order": 0 },
{ "tab": "monitoring", "id": "host", "title": "Host", "order": 10 } ],
"telegram": { "header": "{sev} {name} monitoring",
"lines": [ { "id": "host", "order": 0, "prefix": "🖥 " },
{ "id": "docker", "order": 1, "prefix": "📦 " } ] },
"metrics": [
{ "id": "overall", "kind": "status", "state": "ok", "severity": "ok" },
{ "id": "issues", "kind": "list",
"items": [ { "text": "RAM 91% (7.1/7.8G)", "severity": "warn" } ],
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 0,
"render": "list", "name": "Issues" },
"telegram": { "show": false } },
{ "id": "ram", "kind": "percent", "label": "RAM", "value": 91, "severity": "warn",
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 1,
"render": "kpi" },
"telegram": { "show": true, "line": "host", "pos": 10,
"format": "RAM {value}%", "sep": " · " } }
]
}Resposta de sucesso:
{ "accepted": true, "effective": "green", "next_expected_by": "2026-07-12T10:26:13Z" }6.2. Limites nas métricas do relatório
Qualquer métrica numérica de um relatório, exceto overall, pode trazer thresholds (o bloco do contrato de métricas personalizadas, seção 5.4). O painel calcula a severidade dessa métrica no recebimento, e a luz do heartbeat é o pior entre overall e os limites ultrapassados: uma métrica acima de crit deixa o heartbeat vermelho mesmo quando overall é ok. recover é comparado ao relatório aceito anterior do mesmo heartbeat. Um bloco malformado recebe 422 invalid_report com o motivo na lista. Um relatório sem limites se comporta como antes.
{ "id": "queue", "kind": "number", "label": "Mail queue", "value": 41,
"thresholds": { "direction": "above", "warn": 20, "crit": 100, "unit": "msgs", "recover": 15 },
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 2, "render": "kpi" },
"telegram": { "show": true, "line": "host", "pos": 20, "format": "queue {value}" } }O painel a mostra como 41 (threshold 20/100 msgs) em um bloco amarelo, e o heartbeat fica amarelo.
7. Como integrar um script de monitoramento (no estilo monitor.sh)
Se você já roda um script cron de verificação de saúde, adicione um POST best-effort no fim de cada tick. Uma falha de entrega não deve quebrar o tick: o watchdog vai detectar um silêncio prolongado de qualquer forma.
# monitor.env
EXTOPS_REPORT_URL="https://extops.dev/api/v1/monitoring/report"
EXTOPS_REPORT_TOKEN="<monitoring token from Settings>"
# end of the tick: build the payload with jq from the values you already computed,
# then push it best-effort (never let delivery break the tick itself)
payload="$(jq -n --arg sev "$SEVERITY" '{
schema_version: "2.0",
generated_at: (now | todate),
metrics: [ { id: "overall", kind: "status", state: $sev, severity: $sev } ]
}')"
code="$(curl -sS -m 10 -o /tmp/extops-report.json -w '%{http_code}' -X POST "$EXTOPS_REPORT_URL" \
-H "Authorization: Bearer $EXTOPS_REPORT_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload" || echo 000)"
# 413 payload_too_large: over 64 KB; 422 invalid_report: .errors lists every problem
case "$code" in 413|422) logger -t monitor "panel: $code $(jq -c . /tmp/extops-report.json)" ;; esac
Mapeie as severidades do seu script para o conjunto canônico (critical é aceito e normalizado para crit) e alimente a métrica issues com a sua lista de problemas existente. Defina o intervalo declarado igual ao período do cron.
8. Telegram: mensagem de status separada e alertas
- Os dados de monitoramento vão para o Telegram como uma mensagem de status separada e editável, distinta do status de métricas personalizadas: cabeçalho + a linha reservada
pings+ as linhas que o seu relatório declara. - Opcionalmente, direcione o monitoramento para um grupo/chat separado; quando não definido, é usado o chat do canal da extensão. O transporte do bot é compartilhado com o canal da extensão.
- Alertas de mudança de estado disparam a cada mudança da cor agregada, nos dois sentidos, incluindo a recuperação para verde (com a duração do incidente). Enquanto a cor está estável, há silêncio; mudanças de configuração (pausar, editar pings) não geram alertas.
8b. Heartbeats de projetos web
Um projeto web tem heartbeats próprios. Cada um pertence a um ambiente (por exemplo Production) e tem seu próprio token hb_. As regras do relatório são as da seção 6; abaixo está o que muda.
POST /api/v1/heartbeat/report
Authorization: Bearer hb_<64 hex>
Content-Type: application/json
{"overall":"ok"}- Corpo: um relatório v2 com a métrica obrigatória
overalldo tipo status, ou o corpo mínimo{"overall":"ok"}comok,warnoucrit. O painel transforma o corpo mínimo em um relatório v2 e guarda o corpo normalizado. - Limites: no máximo 64 KB por relatório, no máximo 4 relatórios por minuto por token e no máximo 200 métricas. Um relatório com mais de 200 métricas recebe 422, como em uma extensão.
- Respostas:
200 {"accepted":true,"light":"green"},401para um token desconhecido,413 payload_too_large,422 invalid_report,429comRetry-After: 60. Os motivos de 413 e 422 aparecem como Last error em Setup, Monitoring. - Covers: um heartbeat cobre zero ou mais superfícies do seu ambiente. Sua luz colore cada superfície coberta; um heartbeat que não cobre nenhuma colore o ambiente. Covers é definido no painel, não no relatório.
- Claro: verde até interval + grace após o último relatório aceito, amarelo até 2 × interval + grace e vermelho depois, com
grace = max(60s, 25% of the interval). O intervalo vai de 60s a 24h. - Waiting for first report: um heartbeat novo fica cinza até o primeiro relatório aceito. Depois de 24 horas sem relatório ele fica amarelo e envia uma notificação; nunca fica vermelho antes do primeiro relatório.
- Limites: como na seção 6.2. A luz da resposta 200 e do painel é o pior entre a atualidade,
overalle os limites ultrapassados, e por meio de Covers chega às superfícies cobertas. - Abas: métricas com
tab:"monitoring"aparecem na primeira aba da seção Monitoring do projeto, ebasetambém é reservado. As abas que você declara ganham abas próprias.
8c. Quem se conecta aos seus serviços e como
Um projeto web pode adicionar verificações de portas a um Service cujo endereço é um host, por exemplo um servidor de e-mail. Essas verificações são feitas pelo próprio Ext Ops Panel. Esta seção diz quem se conecta, o que é enviado, com que frequência e como um host pode se excluir. O contrato do agente acima não muda.
- De onde: um endereço,
89.117.144.124, com o nomeprobe.extops.dev. O nome resolve para este endereço, e o endereço resolve de volta para o nome. Não há outro ponto de observação. - Só hosts verificados: uma verificação só roda em um host sob um domínio que o dono do projeto verificou com um registro DNS TXT ou um arquivo well-known. Quando a prova some, as verificações entram em pausa.
- Portas: TCP 25, 110, 143, 465, 587, 993, 995; TLS 443, 465, 993, 995; SMTP 25 e 587; SMTPS 465; IMAP 143; IMAPS 993. Nenhuma outra porta é verificada.
- TCP e TLS: conectar e ler a saudação que o servidor envia, ou concluir um handshake TLS e ler o certificado. Nada é enviado.
- SMTP: esperar a saudação
220, enviarEHLO probe.extops.dev, enviarSTARTTLSquando o servidor oferece e concluir o handshake, depois enviarQUIT. SMTPS na porta 465 faz o mesmo dentro do TLS, sem STARTTLS. - IMAP: esperar
* OK, enviara1 CAPABILITY, enviarSTARTTLSna porta 143 quando oferecido, enviarID ("name" "ExtOpsPanel-Probe" "support-url" "https://extops.dev/docs/monitoring#probes")só quando o servidor anuncia ID, depois enviarLOGOUT. IMAPS na porta 993 faz o mesmo dentro do TLS. - Nunca enviado:
AUTH,LOGIN,AUTHENTICATE,MAIL FROM,RCPT TO,DATA,VRFY,EXPN,SELECT. A sonda nunca faz login, nunca envia e-mail e nunca abre uma caixa postal. Cada linha passa por uma lista fixa de comandos permitidos antes de ser escrita, e um teste falha se qualquer outro comando chegar a um servidor. - Com que frequência: uma verificação de e-mail roda no máximo a cada 5 minutos em qualquer plano, com variação de 10% para mais ou para menos. Um host recebe no máximo uma conexão por vez e no máximo 40 conexões por hora, para todos os projetos juntos. Acima disso, o host fica em paz até a hora seguinte.
- Quando um servidor diz não: um SMTP
421para todas as verificações desse host por uma hora. Um SMTP554ou um IMAP* BYEé registrado como recusa e só é tentado de novo no intervalo seguinte. - Limites de uma verificação: 5 segundos para conectar, 10 segundos para todo o diálogo, no máximo 4 KB lidos.
- O que é guardado: o resultado, o código de resposta, a latência, a primeira linha da saudação cortada em 120 caracteres sem nenhum endereço de e-mail, uma lista curta de recursos como STARTTLS e IMAP4rev1 e as datas do certificado. Não a saudação inteira, não as respostas a EHLO ou ID, e nenhum endereço.
- Como se excluir: se você opera um host e não quer essas verificações, escreva para
support@extops.dev com o nome do host. Excluímos o host ou todo o seu domínio, e depois disso nada se conecta a ele. Você também pode responder
421ou bloquear89.117.144.124. O dono do projeto pode pausar ou excluir uma verificação a qualquer momento em Setup, Monitoring.
9. O contrato legível por máquina
A descrição formal do endpoint de relatórios, do esquema do payload, dos ids reservados e da semântica do semáforo é o arquivo OpenAPI 3.1. Ele traz a mesma versão de contrato (2.1.0).