OPSLOOP Intelligence Loop — Developer Reference
OPSLOOP Intelligence Loop — Developer Reference
Section titled “OPSLOOP Intelligence Loop — Developer Reference”OPSLOOP je pipeline pro zpracování incidentů, která proměňuje alerty z Alertmanageru (a další platformní události) na záznamy incident, obohacuje je o kontext, řadí pravděpodobné příčiny a — u modulů, které jsou zapnuté — spouští nápravu. Většina jejích schopností nad rámec základní alert-to-incident pipeline je opt-in, zamčená za environment flagy.
Komponenty
Section titled “Komponenty”| Komponenta | Umístění | Role |
|---|---|---|
opsloop-consumer | opsloop-consumer/ | Fastify HTTP server + RabbitMQ consumer — dedup/korelace alertů, sběr kontextu, RCA, remediace, forecasting |
llm-service | llm-service/ | Fastify fasáda nad Anthropic (výchozí) / libovolným OpenAI-kompatibilním endpointem (fallback), port 4430 |
| Lumen AI panel | sencai.space-frontend/ | Frontend intelligence panel (stores/lumen.ts, lib/lumen/) |
| Strapi content-type | sencai.space/src/api/incident/ | Jediný CT incident — korelace alertů i RCA kontext žijí jako JSON pole přímo na incidentu |
Neexistuje žádný opsloop-incident, opsloop-context-bundle ani opsloop-runbook-suggestion content-type. Vše je uloženo na jediném collection type incident: kontext je JSON pole context_bundle, seřazené důkazy o root cause jsou JSON pole rca_candidates, obojí se patchuje na tentýž záznam postupně, jak pipeline pokračuje.
llm-service
Section titled “llm-service”Fastify fasáda (port 4430), která směruje LLM volání na nakonfigurovaný backend a vynucuje per-org autorizaci, rate limiting a rozpočtové stropy. Je provider-agnostická:
- Výchozí provider: Anthropic (
@anthropic-ai/sdk), modelclaude-haiku-4-5-20251001(LLM_DEFAULT_MODEL) - Fallback provider:
LLM_PROVIDER=openai_compat— funguje proti libovolnému OpenAI-kompatibilnímu endpointu (OPENAI_COMPAT_BASE_URL), včetně self-hosted řešení jako Ollama, vLLM nebo LocalAI
Endpointy (bez prefixu /api/, žádný /embed endpoint):
POST /chatBody: { messages: [{ role: 'user'|'assistant', content: string }], model?: string, org_id?: number }Response: { content: string, model: string, usage: { input_tokens, output_tokens, cost_usd } }
POST /completeBody: { prompt: string, model?: string, org_id?: number }Response: { content: string, model: string, usage: { input_tokens, output_tokens, cost_usd } }Autentizace je na obou endpointech povinná — vyžaduje se Keycloak Bearer JWT, ověřený přes JWKS (pouze RS256). org_id použité pro rate limiting, evidenci spotřeby a vynucení rozpočtu se vždy odvozuje ze skutečného záznamu volajícího v Strapi (organisation-member, dohledaný podle ověřeného Keycloak sub) — nikdy se nedůvěřuje hodnotě z těla požadavku. org_id zaslané v těle požadavku je porovnáno se skutečným členstvím volajícího a při neshodě zamítnuto (403 llm.auth.org_mismatch).
Vynucení rozpočtu: každá organizace má měsíční strop útraty, LLM_BUDGET_USD_PER_ORG_MONTH (výchozí $10). Po dosažení stropu jsou požadavky zamítnuty s 402 (llm.budget.exceeded) ještě předtím, než dojde k volání providera.
Rate limiting: denní počítadla požadavků per-org, rozlišená podle tarifu — free vs. pro plán (RATE_LIMIT_FREE_PER_DAY výchozí 100, RATE_LIMIT_PRO_PER_DAY výchozí 5000).
Rozpočtový tracker i rate limiter jsou pouze in-memory — stav se při restartu služby ztrácí, žádná Redis perzistence zatím neexistuje.
Lumen AI Panel
Section titled “Lumen AI Panel”Lumen je frontendový intelligence povrch v sencai.space-frontend (stores/lumen.ts, components/layout/Lumen*.vue, skládatelný panel).
Privacy filtr: než je jakýkoliv kontext předán LLM adaptéru, sencai.space-frontend/lib/lumen/privacy-filter.ts (scrubContext()) provede allowlist-based očištění snapshotu context busu: neznámé druhy položek se zahodí zcela, u každého druhu položky přežijí jen povolené klíče, jakýkoliv klíč odpovídající vzoru citlivého jména (secret, password, token, credential, api[-_]?key, auth, …) je redigován a hodnoty, které vypadají jako JWT, API klíče ve stylu Stripe/OpenAI, AWS access-key ID nebo connection stringy s přihlašovacími údaji, jsou rovnou zahozeny. Tento filtr žije na straně klienta ve frontendu, ne v llm-service.
Pipeline Alert → Incident
Section titled “Pipeline Alert → Incident”Primární, vždy zapnutá pipeline:
Alertmanager → POST /webhook/alertmanager (HMAC ověřeno přes ALERTMANAGER_WEBHOOK_SECRET) → processWebhookBatch() → buildDedupKey() — SHA-256 seřazených labelů alertu, bez `instance` → in-memory dedup okno (5 min, CORRELATION_WINDOW_MS) + fallback lookup ve Strapi → existující incident v okně → inkrementace alert_count → jinak → createIncident() → Strapi POST /api/incidents → collectContext() [async] → context_bundle napatchován na incident → scoreAndPatchRcaCandidates() [async] → heuristické skórování rca-engine.ts → napatchováno rca_candidates → handleIncidentAutoRemediation() [async, fire-and-forget] → matchování remediation-rule + cooldownRabbitMQ exchanges a queues
Section titled “RabbitMQ exchanges a queues”| Exchange | Typ | Queue | Routing key | Zdroj / účel |
|---|---|---|---|---|
alertmanager.events | topic | opsloop.alerts | alert.firing | Primární dedup/incident pipeline (dostupná i přes HMAC-ověřený POST /webhook/alertmanager) |
platform.events | fanout | opsloop.alert-correlator | monitoring.alert.*, agent.alert.*, cloud-instance.error (filtrováno v consumeru) | Alert correlator |
platform.events | fanout | rca-generator.dispatch | incident.resolved (filtrováno v consumeru) | AI-asistovaný RCA generátor |
platform.events | fanout | code-review.dispatch | code.review.# (filtrováno v consumeru) | Handler code review dispatche |
opsloop.dlx | topic | opsloop.alerts.dead | # | Sdílená dead-letter queue pro všechny čtyři fronty výše |
platform.events je deklarován jako fanout exchange — každá navázaná queue dostane každou zprávu a filtrování podle routing-key vzoru si dělá sám každý consumer (amqp-topic-match.ts), místo aby se spoléhal na broker-side topic routing.
Prefetch je napříč consumerem 10. Při selhání zpracování jsou zprávy nackovány bez requeue rovnou do dead-letter queue — momentálně neexistuje žádný retry counter ani exponenciální backoff; zpráva jde do dead-letter fronty hned při první chybě.
Moduly
Section titled “Moduly”Nad rámec vždy zapnuté dedup/context/RCA-heuristic/auto-remediation cesty výše nabízí opsloop-consumer zhruba deset dalších schopností. Většina je opt-in přes environment flag a defaultně vypnutá; dvě z nich (heuristický RCA engine a auto-remediace) jsou vždy aktivní, protože nemají vlastní nezávislý flag — jejich bezpečnostní pojistka žije na úrovni remediation-rule (confidence_threshold/cooldown_minutes), ne jako globální vypínač.
| Modul | Soubor | Popis | Zapnutí přes |
|---|---|---|---|
| Alert correlator | services/alert-correlator.service.ts | Cross-service korelace alertů z platform.events do incidentů (klíč org:severity:resource-type, klouzavé 5min okno) | OPSLOOP_DEDUP_ENABLED=true |
| Context collector (rozšířený) | services/context-collector.service.ts | Sbírá časovou osu změn kolem okna incidentu — Gitea commity, IAM audit události, drift, provisioning — tenant-scoped na orgId | CONTEXT_COLLECTION_ENABLED=true |
| RCA engine | rca-engine.ts | Heuristické skórování kandidátů na root cause (deploy_recent, iam_change, drift_event, config_change, resource_exhaustion). Nikdy nevolá LLM a nikdy nevydává jediný autoritativní verdikt — jen řadí důkazy pro operátora | Vždy aktivní (volán z context collectoru) |
| RCA generátor (AI-asistovaný) | rca-generator.ts | Při incident.resolved sestaví evidence bundle a zavolá llm-service (model claude-haiku-4-5-20251001); při selhání nebo nedostupnosti LLM volání spadne na rule-based shrnutí. Odlišný od vždy zapnutého RCA enginu výše | INCIDENT_RCA_ENABLED=true |
| RCA ranker | services/rca-ranker.service.ts | Váhované skórování důkazů podle jistoty plus Jaccard-similarity dedup RCA kandidátů | Volán z pipeline RCA generátoru |
| Auto-remediace | auto-remediation.ts | Po vytvoření incidentu: dohledá remediation-rule (regex/klíčové slovo na dedup_key/titulek), aplikuje práh jistoty + cooldown a pak buď automaticky spustí runbook (auto_approve=true), nebo otevře change request na lidské schválení | Vždy aktivní — bez nezávislého flagu |
| SRE runbook auto-trigger | services/auto-trigger.service.ts | Alternativní/doplňková cesta k auto-remediaci — práh závažnosti + cooldown, emituje ops.auto_trigger.fired/skipped | AUTO_TRIGGER_ENABLED=true |
| Capacity forecast | capacity-forecast.service.ts | Týdenní cron (neděle 05:00 UTC) — lineární regrese nad agent-metric-snapshot (cpu/paměť/disk), prahové recommended_size, upsertuje lumen-recommendation (right-sizing) | CAPACITY_FORECAST_ENABLED=true |
| Workload profiler | services/workload-profiler.service.ts | Denní cron (04:00 UTC) — bucketing CPU/paměti podle hodiny dne do profile_type (cpu_bound/memory_bound/idle/io_bound/mixed), LLM-asistované doporučení migrace s rule-based fallbackem | WORKLOAD_PROFILER_ENABLED=true |
| Pattern correlator | pattern-correlator.ts | Běží každých 15 minut — cross-service detekce anomálií: kaskádové selhání (≥3 incidenty / 10 min), opakovaný alert (stejný dedup_key ≥3× / 24h), upsertuje correlation-findings | CORRELATION_ENABLED=true |
| Code review handler | code-review-handler.ts | Při code.review.requested: stáhne surový diff z Gitea Apps API (max 2 MB), zapíše diff_patch do Strapi, spustí POST /api/code-review-requests/:id/process | Vždy aktivní (registrován bezpodmínečně v consumer.ts) |
Návrhy runbooků a auto-remediace
Section titled “Návrhy runbooků a auto-remediace”Auto-remediace ani SRE auto-trigger cesta neprodukují volný {cmd, timeout} návrh. Pracují proti skutečnému schématu Strapi content-type runbook:
{ "name": "Restart nginx on node-abc", "trigger_type": "alert", "trigger_condition": { "metric": "cpu_percent", "operator": ">", "threshold": 90 }, "actions": [ { "type": "restart_service", "params": { "service": "nginx" } } ], "confirmation_required": true, "cooldown_minutes": 60}actions[].type je jedno z restart_service, clear_disk_space, kill_process, run_approved_script. Runbooky s confirmation_required: true (výchozí hodnota schématu) se nevykonávají přímo — jsou odeslány jako approval-request (POST /api/approval-requests, action_type: "runbook.execute") na lidské schválení, se 4hodinovým TTL (expires_at) a uloženým blast_radius/rollback_plan. V tomto flow neexistuje žádný “Action Gateway” ani “cloud-approval-request” content-type — skutečným mechanismem je approval-request.
Eskalace (PagerDuty / Opsgenie)
Section titled “Eskalace (PagerDuty / Opsgenie)”Eskalace na externího poskytovatele pagingu nežije v opsloop-consumer. Je implementována v cloud-connector/src/services/escalation.service.ts, která čte konfiguraci politiky ze Strapi content-type escalation-policy (provider: pagerduty nebo opsgenie, per-org routing_key / šifrovaný API klíč, zamčeno za ESCALATION_ENABLED).
escalateIncident() v této službě posílá na PagerDuty Events v2 API nebo Opsgenie Alerts API podle providera nastaveného v politice. K okamžiku psaní tohoto textu je funkce plně implementovaná, ale není volána z žádné cesty vytváření incidentu v repozitáři — při startu workeru běží jen startEscalationConsumer(), který ale nedělá nic víc než zaloguje připravenost. Před spoléháním na živou PagerDuty/Opsgenie eskalaci v produkci ověřte aktuální stav zapojení s vlastníkem služby.
Syntetický monitoring
Section titled “Syntetický monitoring”Syntetické kontroly spouští cloud-connector (src/services/synthetic-check.service.ts, zamčeno za SYNTHETIC_CHECK_ENABLED), ne Alertmanager. Cron běžící každou minutu znovu spustí jakoukoliv synthetic-check, jejíž interval vypršel, a výsledek odešle do Strapi:
cloud-connector cron (každou minutu) → GET /api/synthetic-checks (zapnuté kontroly) → per kontrola: HTTP požadavek, porovnání statusu/těla s očekáváním → POST /api/synthetic-results (X-Service-Secret) → PUT /api/synthetic-checks/:id (last_check_at, last_status)Momentálně neexistuje žádný lifecycle hook, RabbitMQ publish ani routing ve stylu anomaly.events z vytvoření synthetic-result do incidentní pipeline — selhávající syntetická kontrola se zaznamená ve Strapi, ale automaticky neotevře incident ani neuvědomí opsloop-consumer. Jakoukoliv automatizaci synthetic-check → incident považujte za zatím neimplementovanou; před dokumentováním nebo spoléháním na ni ověřte s vlastníkem služby.
Observabilita
Section titled “Observabilita”- Health:
GET /health - Metriky:
GET /metrics— Prometheus textový formát (text/plain; version=0.0.4), včetněhttpRequestsTotal,httpRequestDurationMs,alertsProcessedTotal,incidentsCreatedTotal - Error tracking:
@sentry/node, mířící na self-hosted instanci GlitchTip (kompatibilní se Sentry ingestion protokolem, ne Sentry SaaS — profilglitchtipv orchestraci), v souladu s platformní politikou žádného placeného SaaS.beforeSendPII scrubbing probíhá vsrc/sentry-pii-scrub.ts. PrázdnéSENTRY_DSNje bezpečný no-op — pro lokální vývoj není potřeba žádná instance GlitchTipu. - Audit: non-HTTP mutace (dedup, sběr kontextu, RCA skórování/generování, remediace, forecasting) publikují přímo přes vendorovaný balíček
@sencai/audit(např.incident.deduplicated,incident.context.collected,incident.rca.candidates_updated,rca.generated,ops.remediation.triggered/completed,ops.auto_trigger.fired/skipped,capacity.forecast.generated,workload.profile.generated). Vytvoření incidentu přes Strapi REST API navíc automaticky prochází globálním middlewaremaudit-logger.
Přidání nového zdroje intelligence
Section titled “Přidání nového zdroje intelligence”- Publikujte do příslušného exchange —
alertmanager.events(routing keyalert.firing) pro zdroje ve tvaru alertu, neboplatform.events(fanout; zvolte konvenci routing key a filtrujte na ni v consumeru přesamqp-topic-match.ts) pro cokoliv jiného. - Přidejte modul consumeru/handleru v
opsloop-consumer/src/(nebosrc/services/) podle existujícího vzoru (deklarace queue sx-dead-letter-exchange: opsloop.dlx, bind, filtrování podle routing key). - Zapojte ho v
consumer.ts(neboindex.tspro HTTP/cron moduly) a zamkněte ho za nový environment flag, defaultněfalse, pokud nejde o nízkorizikovou read-only cestu. - Pokud má zdroj sytit root-cause analýzu, rozšiřte
context-collector.ts/context-collector.service.tso nový typ důkazu a případně přidejte odpovídající typ kandidáta dorca-engine.ts.