topsrv
topsrv — метрики, алерты, CVE и HTTP-логи серверов проекта через публичный JSON-RPC API app.topsrv.io. Используй, когда нужна загрузка CPU, памяти или диска хоста, метрики PostgreSQL/nginx/angie агента topsrv (`topsrv_*`), какие алерты горят, уязвимые пакеты на серверах или кто (боты, сети, IP) нагружает сайт.
- License 许可
- MIT
- 最近更新
- 2026-11-05
- Stars
- ★ 7
- Forks
- 1
01 前言 Front Matter
- name
- topsrv
- description
- topsrv — метрики, алерты, CVE и HTTP-логи серверов проекта через публичный JSON-RPC API app.topsrv.io. Используй, когда нужна загрузка CPU, памяти или диска хоста, метрики PostgreSQL/nginx/angie агента topsrv (`topsrv_*`), какие алерты горят, уязвимые пакеты на серверах или кто (боты, сети, IP) нагружает сайт.
- allowed-tools
- Bash(pcurl:*)
03 SKILL.md 正文
topsrv — серверы проекта: метрики, алерты, инвентарь, HTTP-логи
topsrv — агент мониторинга серверов (один бинарь вместо node_exporter, postgres_exporter, process-exporter, nginx-exporter и smartctl_exporter) и сервис app.topsrv.io, куда агенты отправляют метрики, инвентарь пакетов и access-логи. Скилл работает с публичным API v1: один токен — один проект, только чтение, кроме alert.silence.
Метрики topsrv_* лежат в хранилище topsrv, а не в Prometheus проекта: метрики сервиса (app_*) — через /prometheus, метрики серверов — здесь.
Профиль определяется при онбординге (/onboard) и хранится в ~/.claude/memory/infra-{group}.md, секция topsrv.
Подключение
Profile: @{topsrv_profile} # обычно topsrv-{slug}; второй токен проекта — topsrv-{slug}-2
Endpoint: POST https://app.topsrv.io/api/v1/rpc/ # JSON-RPC 2.0, слэш в конце обязателен
Contract: https://app.topsrv.io/api/v1/rpc/openrpc.json # открыт без токена
- Профиля нет в infra-файле — найди строки
topsrv-*вpcurl show; если проектов несколько, спроси, какой нужен. - Профиля нет вообще — попроси пользователя выпустить токен в app.topsrv.io → Project settings → Tokens → API tokens и выполнить команду
pcurl addс вкладки pcurl. Токен (tsk_…) не спрашивай и не печатай. - Проект берётся из токена, ни один метод не принимает
slug. - Сессионный ключ веб-интерфейса (заголовок
Authorization2) здесь даёт 401invalid_token, а API-токен не работает на внутреннем/v1/rpc/.
Вызов
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"meta.whoami"}'
idобязателен: тело без него — notification, ответ — пустой 200.- Параметры — объектом по именам:
"params":{"hostname":"web-1"}. Имена методов регистронезависимы. - Кавычки PromQL внутри single-quoted тела экранируй
\". Тело с вычисляемыми значениями (окно времени, длинный PromQL) собирай черезjq -ncв аргумент-d "$(…)": pcurl не передаёт stdin, и-d @-даёт-32700 Parse error. - Профиль хранит
Content-Type: application/json. Если заголовка в профиле нет — добавь-H 'Content-Type: application/json', иначе 415. - Батч — массив до 10 вызовов, каждый вызов батча стоит единицу rate limit.
Первый вызов в сессии — meta.whoami: project, scopes, expiresAt. Метод вне scopes токена отвечает 403 insufficient_scope — сообщи пользователю, какой scope нужен. Если до expiresAt меньше недели — предупреди.
Методы
| Метод | Scope | Параметры | Возвращает |
|---|---|---|---|
meta.whoami |
— | — | project, scopes, tokenPrefix, expiresAt |
host.list |
hosts:read | — | хосты с метриками за 48 ч: hostname, ОС, ядро, agentVersion, lastSeen, addresses[] |
host.get |
hosts:read | hostname |
один хост; 404 host_not_found |
host.summary |
hosts:read | — | строка на хост: cpu %, memUsed/memTotal байт, diskMax %, load 1m, uptime с |
metric.query |
metrics:read | query |
instant PromQL |
metric.queryRange |
metrics:read | query, start, end, step? |
range PromQL |
alert.rules |
alerts:read | — | действующие правила (системные и свои, с override); у открытых — currentState, activeEventId |
alert.list |
alerts:read | state?, severity?, host?, page?, pageSize? (≤500) |
события алертов, новые первыми |
alert.silence |
alerts:write | input: matcher, startsAt, endsAt, comment |
созданное окно тишины |
inventory.hosts |
inventory:read | — | ОС, ядро, пакеты, cveCount/fixableCount, rebootRequired, lastAuditAt |
inventory.findings |
inventory:read | hostname |
открытые уязвимости хоста: пакет, версия, fixedInVersion, advisory, CVE |
botlog.search |
botlogs:read | filter, limit? (≤1000), offset? |
события ботов |
weblog.summary |
weblogs:read | filter |
число запросов, avg/p50/p95 мс, ответы по классам |
weblog.top |
weblogs:read | groupBy, filter, sortBy?, sortDir?, limit? (≤500), offset?, details? |
трафик, сгруппированный по измерению |
weblog.networks |
weblogs:read | filter, sortBy?, minIPs?, limit? (≤200) |
строка на автономную систему |
weblog.blockImpact |
weblogs:read | ip?, userAgent?, window? |
кого заденет блокировка IP, UA, подсети /24, AS |
weblog.search |
weblogs:read | filter, sortDir?, limit? (≤500), offset? (≤10000) |
сырые события HTTP-потока |
? — необязательный параметр. Namespace inventory, botlog, weblog регистрируются, только если у инсталляции включён ClickHouse; иначе вызов отвечает -32601 Method not found.
Полная схема метода, описания полей и коды ошибок — в контракте; читай его, если нужного поля нет в таблице:
pcurl @{topsrv_profile} https://app.topsrv.io/api/v1/rpc/openrpc.json -s | jq '.methods[] | select(.name | ascii_downcase == "weblog.top")'
pcurl @{topsrv_profile} https://app.topsrv.io/api/v1/rpc/openrpc.json -s | jq '.components.schemas.WebLogFilter'
Метрики (PromQL)
- Хост — лейбл
instance, его значение =hostnameизhost.list. Лейблhostnameесть только уtopsrv_host_info:{hostname="web-1"}у остальных метрик ничего не находит. - Ответ —
result.data.result[]:metric— массив пар{name, value}(не map),values[]— точки{timestamp, value}, у instant-запроса ровно одна. Значение — строка, бываетNaN,+Inf. queryRange:startиend— unix-секунды, обязательны. Безstepокно делится примерно на 1000 точек, шаг не меньше 1m. Больше 11000 точек — 400too_many_points, увеличьstep.- Лейбла
project_idв ответе нет. - Пример
topsrv_disk_used_percentиз описанияmetric.queryв контракте — такой метрики у агента нет, запрос вернёт пустой результат. Заполненность диска считается изtopsrv_filesystem_bytes(рецепт ниже).
Каталог метрик агента и готовые рецепты — в репозитории агента; читай, когда нужной метрики нет в таблице:
pcurl https://raw.githubusercontent.com/vmkteam/topsrv/master/docs/metrics.md -s
pcurl https://raw.githubusercontent.com/vmkteam/topsrv/master/docs/promql-recipes.md -s
Основные семейства
| Семейство | Метрики | Лейблы |
|---|---|---|
| Система | topsrv_cpu_seconds_total, topsrv_cpu_cores, topsrv_memory_bytes, topsrv_load_average, topsrv_swap_bytes, topsrv_uptime_seconds |
cpu, mode; type (total/used/available/…); interval (1m/5m/15m) |
| Диск | topsrv_filesystem_bytes, topsrv_filesystem_inodes, topsrv_disk_io_time_seconds_total, topsrv_disk_read_bytes_total, topsrv_disk_write_bytes_total |
mountpoint, device, fstype, type (total/used/free) |
| Сеть | topsrv_network_bytes_total, topsrv_network_errors_total, topsrv_netstat_tcp_connections, topsrv_netstat_tcp_retransmits_total, topsrv_netstat_listening_ports |
interface, direction; state, direction, remote_scope; proto, port, scope, process |
| Процессы | topsrv_process_cpu_seconds_total, topsrv_process_memory_bytes, topsrv_process_open_fds, topsrv_process_worst_fd_ratio |
group; type (rss/vms/swap) |
| PostgreSQL | topsrv_pg_up, topsrv_pg_connections, topsrv_pg_max_connections, topsrv_pg_longest_transaction_seconds, topsrv_pg_blocked_backends, topsrv_pg_replication_lag_seconds, topsrv_pg_query_time_seconds_total, topsrv_pg_table_bloat_pct, topsrv_pg_wraparound_xid_age |
state; database, usename; client_addr; queryid, query; schema, table |
| Nginx | topsrv_nginx_http_requests_total, topsrv_nginx_5xx_requests_total, topsrv_nginx_request_duration_seconds, topsrv_nginx_upstream_duration_seconds |
status, uri, ExtraLabels из конфига агента (например server_name) |
| Angie | topsrv_angie_server_zone_responses_total, topsrv_angie_upstream_peer_state, topsrv_angie_upstream_peer_responses_total |
zone, code; upstream, peer |
| S.M.A.R.T. | topsrv_smart_device_healthy, topsrv_smart_device_temperature_celsius, topsrv_smart_nvme_percentage_used |
device |
| SSL | topsrv_ssl_certificate_expiry_seconds, topsrv_ssl_certificate_san_info |
path, cn, issuer; domain |
| Агент | topsrv_host_info, topsrv_collector_scrape_duration_seconds, topsrv_collector_scrape_panics_total |
hostname, version; collector |
Рецепты
# CPU, % (так считает host.summary)
sum by (instance) (rate(topsrv_cpu_seconds_total{mode!="idle"}[2m])) / on(instance) topsrv_cpu_cores * 100
# Память, %
topsrv_memory_bytes{type="used"} / on(instance) topsrv_memory_bytes{type="total"} * 100
# Заполненность файловых систем хоста, %
topsrv_filesystem_bytes{instance="{instance}",type="used",fstype!~"tmpfs|squashfs|devtmpfs|autofs"}
/ on(instance, mountpoint, device, fstype) topsrv_filesystem_bytes{type="total"} * 100
# Top-5 групп процессов по CPU, ядер
topk(5, sum by (instance, group) (rate(topsrv_process_cpu_seconds_total[5m])))
# PostgreSQL: занятость соединений, %; самая длинная транзакция, с; лаг реплик, с
sum by (instance) (topsrv_pg_connections) / on(instance) topsrv_pg_max_connections * 100
max by (instance, database) (topsrv_pg_longest_transaction_seconds)
max by (instance, client_addr) (topsrv_pg_replication_lag_seconds)
# PostgreSQL: top-10 запросов по времени выполнения
topk(10, sum by (instance, queryid, query) (rate(topsrv_pg_query_time_seconds_total[5m])))
# Nginx: RPS по статусам; p95 времени ответа, с
sum by (instance, status) (rate(topsrv_nginx_http_requests_total[5m]))
histogram_quantile(0.95, sum by (instance, le) (rate(topsrv_nginx_request_duration_seconds_bucket[5m])))
# TCP retransmit, % (норма до 1–2%)
100 * rate(topsrv_netstat_tcp_retransmits_total[5m]) / rate(topsrv_netstat_tcp_out_segs_total[5m])
# Дней до истечения сертификатов — те, что истекают в ближайшие 14 дней
(topsrv_ssl_certificate_expiry_seconds - time()) / 86400 < 14
# Порты, открытые наружу; нездоровые диски
topsrv_netstat_listening_ports{scope="public"}
topsrv_smart_device_healthy == 0
Вызовы
# Instant
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"metric.query","params":{"query":"topsrv_load_average{instance=\"{instance}\",interval=\"1m\"}"}}'
# Range за последний час, шаг по умолчанию
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ -d "$(jq -nc \
--arg q 'rate(topsrv_cpu_seconds_total{instance="{instance}",mode="user"}[5m])' \
'{jsonrpc:"2.0",id:1,method:"metric.queryRange",params:{query:$q,start:((now|floor)-3600),end:(now|floor)}}')"
# Компактный вывод: лейблы и последнее значение серии
… | jq -c '.result.data.result[] | {m: (.metric | map("\(.name)=\(.value)") | join(",")), v: .values[-1].value}'
Алерты
# Что горит сейчас: правила с открытым событием
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ -d '{"jsonrpc":"2.0","id":1,"method":"alert.rules"}' \
| jq -c '.result[] | select(.currentState) | {code, severity, currentState, activeEventId, promql, threshold}'
# События: firing на хостах, имя которых содержит "web"
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"alert.list","params":{"state":"firing","host":"web","pageSize":50}}'
hostвalert.list— подстрока имени хоста. Хост события — вlabels[], элемент сname=instance.- Закрытые вручную (dismissed) события
alert.listне возвращает. alert.rules[].promql— выражение правила: прогони его черезmetric.queryRange, чтобы увидеть, как метрика подошла к порогуthreshold.alert.silence— запись. Вызывай только по явной просьбе пользователя, показав matcher и окно до вызова. Matcher:ruleCode,severity,instance,group— хотя бы одно поле;ruleCodeиgroupберутся изalert.rules. Окно — RFC3339, не длиннее 30 дней.
Инвентарь и CVE
# Хосты с исправимыми уязвимостями
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ -d '{"jsonrpc":"2.0","id":1,"method":"inventory.hosts"}' \
| jq -c '.result[] | select(.fixableCount > 0) | {hostname, os, osVersion, cveCount, fixableCount, rebootRequired, lastAuditAt}'
# Исправимые находки одного хоста
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"inventory.findings","params":{"hostname":"{instance}"}}' \
| jq -c '.result[] | select(.fixedInVersion != "") | {package, version, fixedInVersion, cveId}'
cveCount: 0сам по себе ничего не значит: безlastAuditAtаудит ещё не запускался.rebootRequired— установлено ядро новее работающего, фикс ядра вступит в силу после перезагрузки.- Пустой
fixedInVersion— исправленной версии пакета ещё нет.
HTTP-логи: botlog и weblog
| botlog | weblog | |
|---|---|---|
| Что | только запросы ботов (UA из каталога агента) | весь HTTP-поток, включая людей |
| Хранение | 90 дней | 72 часа — это и максимальное окно запроса |
| Методы | search |
summary, top, networks, blockImpact, search |
filterобязателен:{}— последние 24 часа. Время — RFC3339;fromвключительно,toстрого.- Фильтры weblog — массивы:
host,serverName,path,pathTopSection,method,status,statusClass(2xx…5xx,dropped— 444, 499 и коды выше 599),remoteAddr(IP или IPv4 CIDR),asn,asnOrg,networkType,country,userAgent,botName,verifyState,requestId,visitorId; скалярыuaMatched,ipListed,requestTimeMinMs. weblog.top:groupBy— host | hostPath | path | pathTopSection | remoteAddr | asn | userAgent | country | status | platform | appVersion | platformVersion | visitorId;sortBy— requests | bytes | effort | avgMs | p95Ms | c4xx | c5xx | firstSeen | lastSeen.effort— суммарное время обслуживания: сколько группа стоит бэкенду.- Прежде чем советовать блокировку IP, UA или подсети —
weblog.blockImpact:visitorsOnlyHereпоказывает, сколько живых посетителей потеряют доступ. weblog.searchвыгружай по времени, а не по offset:sortDir: "asc", сдвигайfromна время последнего события, дубли отбрасывай поrequestId.requestId— id запроса, который nginx передал бэкенду: по нему событие находится в логах сервиса (/loki).botlog.searchсlimitбольше 1000 отвечает 400, а не урезанной страницей.verifyState: spoofed— UA бота, но IP не подтверждает бота.- Ответы:
weblog.top—result.rows[], значение группы вvalue, стоимость группы вeffortMs;weblog.search—result.events[], адрес с query string вuri(поляpathу события нет), время —requestTimeMsиupstreamMs. - Логи пишутся с момента, когда в агенте включили WebLogs, и окно бывает короче 72 часов. Первый срез —
weblog.topсgroupBy: "host"иfromна 71 час назад (ровно 72 часа сервер считает как72h1m0sи отвечает 400):firstSeenпокажет начало данных, строки — сайты проекта. Если сайтов несколько, фильтруй поhost. - Независимые срезы (summary, top по разным измерениям) отправляй одним батчем.
- Как читать 5xx: 502 за единицы миллисекунд при
upstreamMs: 0— бэкенд не отвечал, обычно это рестарт при выкатке (сверь время с деплоями); 503/504 сupstreamMs, равным таймауту, — бэкенд не успел ответить. - 499 и 405 на стриминговых эндпоинтах (MCP, SSE) — норма протокола: клиент закрыл поток, GET без поддержки SSE. Это не ошибки сайта.
- Живые посетители — примерно
uaMatched: falseвместе сnetworkType: ["residential","mobile"].
# Кто нагружает сайт за последний час: AS по суммарному времени обслуживания
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ -d "$(jq -nc \
'{jsonrpc:"2.0",id:1,method:"weblog.top",params:{groupBy:"asn",sortBy:"effort",filter:{from:(now-3600|todate)},limit:10}}')"
# Самые дорогие пути с 5xx за последний час
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ -d "$(jq -nc \
'{jsonrpc:"2.0",id:1,method:"weblog.top",params:{groupBy:"path",sortBy:"effort",filter:{from:(now-3600|todate),statusClass:["5xx"]},limit:20}}')"
# Пулы скраперов: сети с десятками IP и малым числом User-Agent (uaPerIp сортирует по возрастанию)
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"weblog.networks","params":{"filter":{},"minIPs":20,"sortBy":"uaPerIp","limit":20}}'
# Кто запрашивает файл (llms.txt, robots.txt) и с каким ответом
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"weblog.top","params":{"groupBy":"userAgent","filter":{"path":["/llms.txt"]},"limit":50}}'
# Поддельные боты за сутки
pcurl @{topsrv_profile} -sS https://app.topsrv.io/api/v1/rpc/ \
-d '{"jsonrpc":"2.0","id":1,"method":"botlog.search","params":{"filter":{"verifyState":["spoofed"]},"limit":100}}'
Ошибки
Код и текст — в error.code и error.message ответа.
| Код | message | Что делать |
|---|---|---|
| пустой 200 | — | в запросе нет id |
| 401 | invalid_token, token_expired, token_revoked |
пользователь перевыпускает токен в UI и обновляет профиль; pcurl add сам не запускай |
| 403 | insufficient_scope |
у токена нет scope метода; токен при этом валиден |
| 400 | bad_query |
PromQL отклонён VictoriaMetrics, причина — в error.data |
| 400 | invalid_range, too_many_points |
нет start/end или end не позже start; увеличь step |
| 400 | period exceeds 72h… |
окно weblog шире 72 часов |
| 404 | host_not_found |
хоста нет среди живых за 48 ч — сверь с host.list |
| 415 | Unsupported Media Type | в профиле нет Content-Type: application/json |
| 429 | rate_limited |
60 запросов в минуту на токен, burst 20; подожди Retry-After |
| -32601 | Method not found | опечатка в имени метода или namespace не зарегистрирован |
| -32700 | Parse error | тело не JSON, например -d @- |
Прямые ссылки
https://app.topsrv.io/projects/{slug}/ # хосты
https://app.topsrv.io/projects/{slug}/{instance}/ # хост
https://app.topsrv.io/projects/{slug}/alerts/
https://app.topsrv.io/projects/{slug}/inventory/
https://app.topsrv.io/projects/{slug}/web-logs/
https://app.topsrv.io/projects/{slug}/bot-logs/
04 同仓库更多 Skills
zenrpc
Zenrpc — справочник по JSON-RPC 2.0 серверу для Go. Используй при создании и редактировании RPC-сервисов.
youtrack
YouTrack — задачи через REST API и pcurl. Используй для поиска issues, создания, обновления описаний, смены assignee/stage/приоритета, логирования времени и работы с комментариями.
testing
Testing — используй всегда, когда пишешь или правишь Go-тесты (*_test.go), в том числе по сценариям Given/When/Then из spec: фреймворк как в соседних файлах, префикс TestDB, фабрики pkg/db/test, реальная БД без моков. Также при настройке TestMain/test-short и когда тесты падают в gate, но проходят локально.
solve
Solve — полный цикл решения задачи из YouTrack: preflight → анализ → план → реализация → ревью → коммит. Используй при любой YouTrack-задаче (`/solve PLF-819`); флаг `--fast` сокращает HITL для тривиальных багфиксов.
skills-check
Skills Check — smoke-тесты интеграций из project-index.md. Используй для проверки что pcurl-профили и URL всех data-source скиллов ещё живы.
sentry
Sentry — мониторинг ошибок (issues, events, releases) через REST API. Используй когда нужно найти ошибку по ключевым словам, разобрать stack trace, проверить что релиз поднялся или сопоставить ошибку с коммитом.