Agent Governance — Developer Reference
Agent Governance — Developer Reference
Section titled “Agent Governance — Developer Reference”Neexistuje žádný samostatný mikroservis „Action Gateway”. Governance agentů je implementována jako tři obyčejné Strapi content-types v sencai.space/src/api/: agent-policy, approval-request a elevation-request. Vynucování je deny-by-default a žije ve Strapi controllerech/policies — ne v samostatné službě a ne v OPA/Rego.
Žebřík autonomie
Section titled “Žebřík autonomie”agent-policy.autonomy_level je enumerace L0–L3 s tímto přesným významem:
| Úroveň | Název | Chování |
|---|---|---|
| L0 | Jen pozorování | Agent pouze reportuje a dotazuje. Žádné zápisy. |
| L1 | Jen návrh | Agent navrhuje akce, ale nic se nevykoná — čistě poradní, žádné zápisy, i kdyby člověk návrh „schválil”. Nezaměňovat s L2. |
| L2 | Vykonání se schválením | Člověk schválí čekající žádost, poté agent vykoná. |
| L3 | Automatické vykonání v rámci politiky | Předschválené hranice — bez lidského kroku, akce se spustí automaticky, pokud zůstává v rámci politiky (max_blast_radius, allowed_action_types, freeze windows). |
Výchozí hodnota je L0. Politika bez odpovídajícího aktivního řádku pro danou org/environment/action_type znamená zamítnutí (deny-by-default).
Content-type agent-policy
Section titled “Content-type agent-policy”Scoped podle scope_type (org / environment / action_type) + scope_value, per organizace. Klíčová pole:
autonomy_level—L0–L3(viz žebřík výše)freeze_windows(JSON) — časově ohraničené blokace, např. deploy freeze. Formát:[{ name, start: "HH:MM", end: "HH:MM", days: number[], timezone }](days: 0=neděle…6=sobota). Dokud jakékoliv okno odpovídá, jsou všechny akce agenta zablokované bez ohledu na úroveň autonomie.max_blast_radius— enumsingle_resource/service_tier/availability_zone/region; omezuje maximální rozsah dopadu jedné akce pod danou politikouallowed_action_types/blocked_action_types(JSON pole) —blocked_action_typesmá vždy přednostis_active(boolean)created_by_agent(boolean) — chrání proti tomu, aby si agent sám mutoval vlastní politiku
Schéma nese také sadu polí, které jsou přímo v samotném schématu označené jako „Legacy: OPA-compatible” — policy_document (JSON), scope (global/per-provider/per-env/per-action), provider, environment, action_pattern. Ta existují jen kvůli zpětně kompatibilnímu tvaru dat. Nikde v kódu neběží žádné živé vyhodnocování OPA: v monorepu nejsou žádné .rego soubory ani žádné volání CLI opa eval. Nepište dokumentaci k autorování politik, která by předpokládala pipeline s Rego bundly — vynucování je obyčejný TypeScript v controllerech Strapi (controller agent-policy + controllery approval/elevation popsané níže).
Vlastní routy na agent-policy:
POST /api/agent-policies/:documentId/activatePOST /api/agent-policies/:documentId/deactivateObě jsou org-scoped (volající musí být člen organizace vlastnící politiku, nebo platform admin) a emitují audit události gateway.*.
Workflow schvalování (approval-request)
Section titled “Workflow schvalování (approval-request)”Uloženo v content-type approval-request (sencai.space/src/api/approval-request/), ne cloud-approval-request.
Pole žádosti o schválení
Section titled “Pole žádosti o schválení”action_type(string) — strojově čitelný identifikátor akce, např.backup.policy.apply,runbook.execute,bulk.deauthpayload(JSON) — payload akce, která se má po schválení vykonatstatus— enumpending/approved/rejected/expiredblast_radius(JSON) —{ resource_count, resource_types, estimated_impact }, naplňuje se pomocí dry-runcost_delta_usd(decimal) — odhadovaná změna nákladů v USD (záporná hodnota = úspora)dry_run_status— enumpending/running/completed/faileddry_run_result(JSON) — výsledek simulace před vykonánímrollback_plan(text)expires_at(datetime) — při vytvoření nastaveno nacreated_at + 4 hodiny
TTL 4 hodiny je pevná konstanta (TTL_MS = 4 * 60 * 60 * 1000) v controlleru. Neexistuje žádné pole pro per-organizační override ani na content-type organisation, ani nikde jinde v kódu — TTL v tuto chvíli není konfigurovatelné.
Routy žádosti o schválení
Section titled “Routy žádosti o schválení”Všechny routy vyžadují global::hybrid-auth. Mutační sub-routy jsou deklarovány před obecnými /:id routami, aby je Strapi neresolvoval jako findOne/update:
GET /api/approval-requestsGET /api/approval-requests/:idPOST /api/approval-requestsPOST /api/approval-requests/:id/approvePOST /api/approval-requests/:id/rejectPOST /api/approval-requests/:id/dry-runPUT /api/approval-requests/:idDELETE /api/approval-requests/:idapprove— platí jen ze stavupending; pokud užexpires_atuplynulo, záznam se přepne naexpireda volání vrátí 400 místo schválení. Při úspěchu nastavíapproved_at,approved_by, emituje audit událostapproval.approved.reject— platí jen ze stavupending; vyžaduje neprázdnérejection_reason. Emitujeapproval.rejected.dry-run— simuluje blast radius a cenový dopad bez skutečného vykonání. Počítá dotčené záznamycloud-instance/cloud-networkpro organizaci žádosti, označuje destruktivní typy akcí (vzordestroy/terminate/delete/bulk.deauth) a zapisujedry_run_status,blast_radius,dry_run_result,cost_delta_usd. Emitujeapproval.dry_run.completed.update(PUT /:id) — pouze allowlistovaná editace metadat (action_label,rollback_plan). Nikdy nemůže změnitstatus,organisation,requester,approved_by,expires_atani pole cost/blast-radius — ta jsou výhradně ve vlastnictví akcí approve/reject/dry-run.find/findOne/deletejsou org-scoped: volající vidí/maže jen žádosti patřící organizacím, jejichž je členem (platform adminové vidí vše).findOnena žádost mimo organizace volajícího vrací 404, ne 403, aby se nevyzradila existence záznamu napříč tenanty.
Životní cyklus žádosti o schválení
Section titled “Životní cyklus žádosti o schválení”Agent / operátor → POST /api/approval-requests (action_type, payload, blast_radius?, cost_delta?, ...) status = "pending", expires_at = now + 4h → volitelně: POST /api/approval-requests/:id/dry-run simuluje blast radius + cenový dopad, bez vykonání → Schvalovatel žádost posoudí v platform UI → POST /api/approval-requests/:id/approve (nebo zamítnutí → POST .../reject s rejection_reason) → status = "approved" → volající vykoná samotnou akci mimo tento tok (cloud-connector / fleet agent / runbook engine) → každý přechod stavu emituje audit událost approval.* přes @sencai/auditPro žádosti o schválení neexistuje žádný dedikovaný real-time push kanál (pro tento účel neexistuje žádný WebSocket endpoint). Změny stavu schválení jsou pozorovatelné jen pollingem výše uvedených REST endpointů, nebo přes audit událost, kterou vyvolají; širší real-time notifikace by šla přes obecnou platformní event architekturu notification.events/platform.events (viz notification-service a event-store-consumer), ale žádné napojení specifické pro schvalování na tuto cestu aktuálně neexistuje — před zdokumentováním živého notifikačního toku ověřte se service ownerem.
Vynucování cross-tenant capability
Section titled “Vynucování cross-tenant capability”Cross-tenant kontroly capability (přístup MSP/agentury k prostředkům jiné organizace) jsou vynucovány Strapi policy global::is-organisation-scoped-or-cross-tenant (src/policies/is-organisation-scoped-or-cross-tenant.ts, WAVE-19 / F2.GATEWAY.06):
- Načte aktivní tenant kontext (
TenantContext, naplněný zorganisation-relationship.scope) přesgetTenantContext(). - Pokud požadavek není cross-tenant (
isCrossTenant === false), policy je no-op a propustí ho dál — přímí členové organizace jsou řízeni běžnými org-scoping policies místo toho. - Pokud požadavek je cross-tenant, ověří, zda udělené
capabilitiesdaného vztahu (např.*:read,*:*,compute:read,network:read,cost:read,iam:read) pokrývají capability vyžadovanou přesconfig.capabilitydané routy. - Chybějící
config.capabilityna routě selže closed (zamítnutí). - Při zamítnutí: vrací 403 a publikuje audit událost
CROSS_TENANT_ACCESS_DENIEDs požadovanými/dostupnými capabilities a id vztahu.
Aplikuje se na routách, které vystavují cross-tenant compute/network/iam/cost data, vedle route-specifických policies:
policies: [ 'global::hybrid-auth', { name: 'global::is-organisation-scoped-or-cross-tenant', config: { capability: 'compute:read' } },]Poznámka z dokumentace samotné policy: tato policy sama o sobě neposkytuje tenant izolaci pro non-cross-tenant volání — routy, které se na ni spoléhají jako na jedinou policy, potřebují vedle ní global::is-organisation-scoped (nebo ekvivalentní org-scoping na úrovni controlleru).
JIT elevace (elevation-request)
Section titled “JIT elevace (elevation-request)”Just-in-time, cross-tenant elevace oprávnění žije v content-type elevation-request.
Pole žádosti o elevaci
Section titled “Pole žádosti o elevaci”requesting_user,requesting_org,target_org— uživatel a dvě organizace zapojené do cross-tenant granturequested_capabilities(JSON pole) — řetězce požadovaných capabilitiesjustification(text, povinné)duration_minutes(integer, 1–480, tedy max 8 hodin) — ne pole TTL v sekundáchstatus— enumpending/approved/denied/expired/revokedapprover,approved_at,auto_expire_at(=approved_at + duration_minutes)denial_reasonused(boolean) /used_at(datetime) — single-use guard: grant je spotřebován při prvním použití, ne pouze časově ohraničen. Jakmileused = true, kontrola elevace považuje grant za neaktivní, i kdybyauto_expire_atještě neuplynulo.relationship— odkaz na záznamorganisation-relationship, který stojí za cross-tenant grantemhome_region(eu/us/apac) acell_id— CELL pole data-rezidence (F2.CELL.01)
Routy žádosti o elevaci
Section titled “Routy žádosti o elevaci”GET /api/elevation-requestsGET /api/elevation-requests/:idPOST /api/elevation-requestsPUT /api/elevation-requests/:id/approvePUT /api/elevation-requests/:id/denyPOST /api/elevation-requests/:id/consumecreate— volající musí být členrequesting_org(nebo platform admin). Validujerequested_capabilitiesjako neprázdné pole aduration_minutesjako integer v rozsahu1..480. Nastavístatus = "pending",used = false. EmitujeELEVATION_REQUESTED.approve(PUT :id/approve) — gated pomocíglobal::hybrid-auth+global::is-elevation-approver(owner/admintarget_org— ne obecná platformní admin kontrola). Nastavístatus = "approved",approved_at = now,auto_expire_at = approved_at + duration_minutes,approver = volající. EmitujeELEVATION_APPROVED(risk_levelcritical).deny(PUT :id/deny) — stejné gating pravidlo pro schvalovatele. Nastavístatus = "denied",denial_reason,approver,approved_at. EmitujeELEVATION_DENIED.consume(POST :id/consume) — spotřebovat může jen původnírequesting_user; autorizace je vynucena přímo v controlleru (ne route policy). Deleguje naconsumeElevation()ze service vrstvy, která přepneused = true/used_atjako single-use guard. EmitujeELEVATION_USED.find/findOne— non-admin volající vidí jen žádosti, kde je požadatelem, nebo je členemrequesting_org/target_org.
Poznámka: pro schválení neexistuje žádná obyčejná cesta PUT /api/elevation-requests/:id (generic update) — schválení a zamítnutí jsou dedikované akce (:id/approve, :id/deny) gated přes global::is-elevation-approver, odlišné od obecného core update handleru.
Životní cyklus žádosti o elevaci
Section titled “Životní cyklus žádosti o elevaci”Požadující uživatel (člen requesting_org) → POST /api/elevation-requests { requesting_org, target_org, requested_capabilities, justification, duration_minutes } → status = "pending" (audit událost ELEVATION_REQUESTED) → owner/admin target_org: PUT /api/elevation-requests/:id/approve → status = "approved", nastaveno auto_expire_at PUT /api/elevation-requests/:id/deny → status = "denied" → requesting_user zavolá POST /api/elevation-requests/:id/consume aby grant skutečně uplatnil — single-use: used = true po prvním consume → grant je neaktivní, jakmile used=true NEBO uplynulo auto_expire_at, podle toho, co nastane dřívŘešení problémů
Section titled “Řešení problémů”Žádost o schválení uvízlá v pending
Section titled “Žádost o schválení uvízlá v pending”Zkontrolujte expires_at — po 4 hodinách od vytvoření žádost automaticky expiruje při dalším pokusu o approve (nebo přes inline kontrolu při vynucování), ne přes samostatný polling cron. Expirované žádosti je nutné znovu odeslat; neexistuje cesta pro jejich znovuotevření.
Cross-tenant capability nečekaně zamítnuta (403, CROSS_TENANT_ACCESS_DENIED)
Section titled “Cross-tenant capability nečekaně zamítnuta (403, CROSS_TENANT_ACCESS_DENIED)”Zkontrolujte organisation-relationship.scope pro vztah mezi organizací volajícího a cílovou organizací — udělené řetězce capabilities (*:*, *:read, <resource>:<action>) musí pokrývat capability nastavenou na dané routě. Neexistuje žádný OPA bundle ani .rego soubor k prohlédnutí; porovnávací logika je obyčejná funkce hasCapability() v src/policies/is-organisation-scoped-or-cross-tenant.ts.
L3 automatické vykonání se nespouští
Section titled “L3 automatické vykonání se nespouští”Ověřte, že příslušný řádek agent-policy má is_active = true, autonomy_level = "L3", typ akce není v blocked_action_types a žádný záznam freeze_windows aktuálně neodpovídá (čas/den/timezone).
Grant elevace schválen, ale akce je stále zamítnuta
Section titled “Grant elevace schválen, ale akce je stále zamítnuta”Ověřte, že grant ještě nebyl spotřebován (used = true) — granty jsou single-use bez ohledu na to, kolik z duration_minutes ještě zbývá. Zkontrolujte také, zda neuplynulo auto_expire_at.