FastAPI service: natural-language instructions → structured plan → FortiGate tool
calls over SSE. Default path: parse → validate → confirm → execute with
composite blacklist_and_block (and create_s2s_vpn_psk for site-to-site IPsec).
Optional Jira lifecycle for mutating runs. Same tools on MCP.
- Structured plan (default) - LLM intent parser → schema validator (live
FortiGate names) → confirm tool list → execute. Prefers composite
blacklist_and_blockfor blacklist add (order in code, not the model). - Site-to-site VPN (PSK) - composite
create_s2s_vpn_psk/delete_s2s_vpn(phase1/2, addresses, route, policies) for consumer or producer role on this FortiGate. - Client-to-Site VPN users - composite
create_vpn_user/delete_vpn_user(local user + VPN user group, optional new group + SSL-VPN access policy) with FortiAgent-side password policy and UI password generator. - Confidence from logprobs - parse confidence is
exp(mean token logprob)from the llama.cpp completion (0..1), not self-reported JSON or heuristics. Requires server logprobs support; falls back to 0.5 if absent. - Jira tracking (optional) - when
JIRA_ENABLED=true: confirm plan → Yes → create ticket (instruction + plan + Q&A in description) → OK → In Progress → execute → comment results → In Review → Confirm Done. Mock tickets if creds unset. - Zoho Cliq (optional) - when
CLIQ_ENABLED=true: posts to your Cliq channel when FortiGate execution starts and when the run finishes (success or failure). - IP + FQDN - auto
ipmask/fqdnaddress objects. - Web UI - three-column layout: history | instruction | run timeline;
sidebar template pills (Client VPN, S2S VPN, blacklist); MCP at
/mcp.
uv sync
cp .env.example .env # set FortiGate + LLM + optional Jira
# llama.cpp example (enable logprobs on server for confidence scores):
# llama-server -m …qwen2.5-3b….gguf --port 8080 -a qwen2.5-3b-instruct
uv run uvicorn fortigate_workflow.app:app --reload
or
uv run fastapi dev app.py
# UI: http://127.0.0.1:8000/| Variable | Purpose |
|---|---|
FORTIGATE_HOST, creds |
Firewall API |
LLM_BASE_URL |
e.g. http://localhost:8080/v1 |
LLM_MODEL |
e.g. qwen2.5-3b-instruct |
DRY_RUN |
true = log only, no writes |
VPN_USER_GROUP |
Fallback group name for delete / docs (create flow asks if unset) |
VPN_ACCESS_DST |
Optional default destination for new VPN group policies |
JIRA_ENABLED |
true for ticket gate on mutating plans |
JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN, JIRA_PROJECT_KEY |
SOI project |
CLIQ_ENABLED |
true to notify Cliq on execute start / done |
CLIQ_WEBHOOK_URL |
Channel webhook (simplest; zapikey in URL) |
CLIQ_CHAT_ID + OAuth tokens |
Group/DM via v3 API (CLIQ_REFRESH_TOKEN recommended) |
Place UI logos in fortigate_workflow/static/assets/ (gitignored). See docs/architecture.md.
Test Cliq: uv run python -m fortigate_workflow.cliq.test_send "hello"
Cleanup VPN test objects on FortiGate: uv run python -m fortigate_workflow.cleanup_vpn
| PLAN_EXCLUDE_TOOLS | leave empty; set blacklist_and_block only to debug multi-step |
- docs/architecture.md - layout, SSE, registry
- docs/interactive-flow.md - recipe / slot-filler (override path)
- docs/test-samples.md - sample instructions + edge cases
- Composites own mutate order; validator owns live names and “must ask” slots.
- Parser prompt stays short;
blacklist_and_blockwhen available. RunStoreis in-memory (history survives page refresh while server is up).
FortiOS 7.4 POC: use lan/wan on policies. auto/rules agent strategies disabled.