# DibaGuard console > **Open-release copy** ([DibaAi/DibaGuard-Open](https://huggingface.co/DibaAi/DibaGuard-Open)). This document was written for the private build of DibaGuard. Numbers that involve the HTTP request model (`http_v2`) were measured with that model, which is **not** part of the open release; the open release ships the licence-clean network models described in `CLEAN_MODELS.md`. Licence statements inside this document are superseded by `DATA_LICENSES.md`, `CLEAN_MODELS.md` and the repository's `LICENSE`, `LICENSE-WEIGHTS` and `NOTICE` files. Sections of the original that were written in Persian were removed from this copy; nothing else was edited. The operator console is one self-contained page served by the sidecar at `GET /console` (`/` and `/dashboard` redirect to it). It loads nothing from anywhere else - no CDN, font or script - so it works on an air-gapped server. Tabs: Overview, Incidents (with a per-source drawer: evidence, timeline, what the layer did and why, database remediation), Review queue, Rules, Policy, Databases, Posture & enforcement, Teach. Light and dark themes follow the OS; the sun/moon button overrides it. ## Opening it The sidecar binds to `127.0.0.1` by default. From your laptop, tunnel to it: ``` ssh -N -L 8787:127.0.0.1:8787 user@guard-host # then open http://127.0.0.1:8787/console ``` Start the sidecar with the shield layer (without `--policy` the console is read-only, engine only): ``` python -m dibaguard.llm http --models models --policy policy.json --inputs http,flows --port 8787 ``` Reading needs nothing. Every write (verdicts, rules, policy, block / lift, teaching) needs the **console token**: - set `DIBAGUARD_CONSOLE_TOKEN` in the sidecar's environment, or - leave it unset: the sidecar generates one and prints it **once** on stderr at startup. Click **Read-only** in the header and paste the token. It is kept in that browser tab only (sessionStorage) and forgotten when the tab closes or you click **Unlocked** to lock again. ## Connecting an LLM (Teach) The model is configured in the sidecar's environment, never in the page; the key never reaches the browser. | variable | value | |---|---| | `DIBAGUARD_LLM_PROVIDER` | `openai` - any OpenAI-compatible `/chat/completions` endpoint (vLLM, llama.cpp server, Ollama, LM Studio, OpenAI) - or `anthropic` (Messages API) | | `DIBAGUARD_LLM_BASE_URL` | e.g. `http://10.0.0.30:8000/v1`; for `anthropic` defaults to `https://api.anthropic.com` | | `DIBAGUARD_LLM_MODEL` | the model name | | `DIBAGUARD_LLM_API_KEY` | the key (optional for local servers; required for `anthropic`) | | `DIBAGUARD_LLM_TIMEOUT` | seconds per call, default 120 | On an air-gapped site, point `DIBAGUARD_LLM_BASE_URL` at a model served on the local network; the model must support tool / function calling. Without an LLM, the Teach tab explains this and still offers the built-in Persian / English rule parser. The Rules tab can also use the connected LLM to interpret a rule written in any language (interpreter "Connected LLM", or "Auto" when the built-in parser finds no condition). The result is shown as a precise rule first. ## The approval model The sidecar runs the tool loop itself (`POST /console/chat`), with the dispatcher's tools (`guard_*`) plus four console tools (`console_entity`, `console_rule_install`, `console_rule_remove`, `console_enforce`). - **Reads run at once**: status, report, entity details, review queue, fast state, rules / policy (viewing), database status, playbook. - **Everything that changes state becomes a proposal**: rule install / removal, verdicts, policy changes, block / lift, calibration, and feeding traffic to the engine (scoring can lead to actions). The model is told "proposed - not executed" and the proposal appears in the transcript and in *Pending proposals* with its exact tool call. - **Only you execute it**, by clicking **Approve** (`POST /console/approve`). A rule proposal installs exactly the rule you saw in the preview - it is not re-interpreted. A mode switch additionally needs the target mode typed back. Proposals expire after an hour; each can be decided once. - The assistant hears your decisions (approved, rejected, failed, and your own changes from other tabs) at the start of its next turn. The server keeps a bounded history (40 messages for the model, 300 transcript lines); **Clear** forgets it. Policy changes from the Policy tab are validated by `Policy.validate` before they apply. Switching between REPORT and ENFORCE asks you to type `ENFORCE` / `REPORT`, server-side (an API caller cannot skip it either). Fields that only take effect at start-up (`alpha`, database thresholds read at load) are not editable live: edit `policy.json` and restart. Rules and policy changed in the console live in memory; **Policy -> Effective policy -> Download** gives a `policy.json` with the installed rules to keep them across restarts. ## Security notes - **Bind to loopback** and reach it over SSH. If you bind elsewhere the sidecar warns at startup; put TLS and access control in front. - **Writes**: every `POST /console/*` needs `X-DibaGuard-Token` (or `Authorization: Bearer`), the custom header `X-DibaGuard-Console: 1`, and `Content-Type: application/json`. Cross-site requests (`Origin` not matching `Host`, `Sec-Fetch-Site: cross-site|same-site`) are refused. No CORS headers are ever sent, so another site's page can neither send those headers nor read answers. - **DNS rebinding**: `/console*` is only served when `Host` is an IP literal or `localhost`; add names (a reverse proxy) with `DIBAGUARD_CONSOLE_HOSTS=guard.example.internal`. - **The page** carries a CSP that pins its one inline script by hash, forbids every external load, framing, forms and base changes. Everything shown is inserted as text - evidence, queries and user-agents from attackers never become markup. - **The tool API** (`POST /tools/`): calls that change what the layer enforces - `guard_policy` with `set`, `guard_feedback`, `guard_rules` with `add` (not `interpret_only`), `guard_posture` when port closing is on - need the same token **and** `X-DibaGuard-Console: 1`, and so do `guard_calibrate` / `guard_db_calibrate` (re-calibration rewrites what the site's normal is). Reads stay open on loopback. **Traffic feeds that carry sources** (`guard_score_http` with `client` / `t` / `status`, `guard_score_flows`, `guard_db`, `guard_rpc`) need the **feed token** - `DIBAGUARD_FEED_TOKEN`, sent as `X-DibaGuard-Feed-Token` by the Go proxy and the authz service, which read the same variable - or the console token: otherwise anyone who can reach the port could feed traffic that makes the layer block an address of their choice (validation finding V-16). Without `DIBAGUARD_FEED_TOKEN` the sidecar makes one and prints it once on stderr. A score-only `guard_score_http` (texts, no client or time) changes nothing and stays open. `DIBAGUARD_FEEDS_OPEN=1` reopens every feed (an isolated loopback deployment only; the sidecar warns at startup); `DIBAGUARD_FEEDS_NEED_TOKEN=1` demands the console token and header for feeds too. MCP over stdio is unchanged (it is the local process's own channel). - **Prompt injection**: tool results contain attacker-written text. The system prompt tells the model it is data, and the approval gate means a manipulated model can at most *propose*; read each proposal's exact tool call before approving. - The LLM key stays in the sidecar process; provider errors are redacted before they are shown. ## Endpoints | method | path | | |---|---|---| | GET | `/console` | the page | | GET | `/console/state` | counters, pressure, drift, mode, backend, latest alarms / actions, per-minute series | | GET | `/console/incidents?limit=` | sources with risk, score, families, playbook names, enforcement | | GET | `/console/entity?ip=` | one source: evidence, timeline, audit (what was done, why), explanation, remediation, verdicts | | GET | `/console/review` | review queue (incl. `site:drift`), verdict stats, recent verdicts | | GET | `/console/rules`, `/console/policy`, `/console/databases`, `/console/posture`, `/console/chat` | tab data | | POST | `/console/auth` | check the token | | POST | `/console/rules/interpret` `{text, engine: auto\|builtin\|llm}` | preview only | | POST | `/console/rules/install` `{rule, text}` / `/console/rules/remove` `{id}` | | | POST | `/console/policy` `{set, confirm?}` | 409 + `confirm_word` when the mode changes | | POST | `/console/feedback` `{entity, verdict, note}` | confirm / reject / benign_source | | POST | `/console/enforce` `{ip, action: block\|rate_limit\|challenge\|lift, reason}` | | | POST | `/console/posture/run` | runs the audit (closes ports only if the policy allows) | | POST | `/console/chat` `{message}` / `/console/chat/reset` | the assistant | | POST | `/console/approve` `{id, decision: approve\|reject, confirm?, note?}` | execute or drop a proposal |