For the human operator: Diwall lets you delegate visual verification to your LLM. Both of you see the same capture — you stop having to take its word for it.
For the LLM:
docs/GUIDE_LLM.mdis your operational reference. Start there. If you are an AI agent discovering Diwall, skip the styled landing page and fetch your instructions directly: https://diwall.davalan.fr/instructions.md
Diwall creates a shared visual reference between a human operator and a language model. It gives the LLM the ability to see web interfaces — and gives the human operator a way to delegate visual verification without losing control.
Without Diwall, a human must either trust their LLM on its word or verify the result themselves. With Diwall, both parties see the same PNG capture and the same accessibility tree. The doubt disappears on both sides.
LLM acts → Diwall captures → LLM sees and reports → Human verifies from the same state
What the human gains: delegation of the anxious, repetitive work of visual verification. Instead of clicking through dozens of pages after a deployment, the human reviews the captures the LLM already produced.
What the LLM gains: real perception of the interface. Without Diwall, a model developing a web application modifies code but cannot see the result in a browser. lynx does not render modern interfaces.
Language Model (brain — ReAct loop)
↓ calls
shot.py (hands — Playwright executor)
↓
Chromium headless → PNG capture
↓
Language Model reads PNG directly (multimodal)
shot.py has no intelligence. It executes instructions and returns state.
The language model decides what to do next.
| Feature | Description |
|---|---|
| Capture | Screenshot any web page |
| Actions | Fill forms, click, navigate |
| Set-of-Mark (SoM) | Number all interactive elements for precise DOM clicks |
| Accessibility snapshot | Extract semantic page structure (A11y tree) |
| Session persistence | Maintain login state across multi-step ReAct loops |
| RPA scenarios | Execute action sequences from JSON files |
| Visual monitoring | Detect if a page changed since last reference |
| Pixel diff | Quantitative, deterministic diff against a stored reference (v1.2) |
| Credential vault | Secure credential injection — never in plaintext, never on the command line |
| Encrypted vault | gocryptfs-backed vault — VaultFermeError (exit 42) if vault not mounted (v1.5) |
| Scroll | defiler action — relative pixel scroll or scrollIntoView by CSS selector (v1.6) |
| Off-screen warning | som_hors_viewport count in JSON when interactive elements exist below the fold (v1.6) |
| Procedural memory | Successful runs stored as replayable skills via journal.py --exporter-skill (v1.6) |
| TOTP 2FA | Google Authenticator / Authy codes generated at runtime from vault seed (v1.6) |
| Async MFA via ntfy | SMS/email 2FA codes received asynchronously via ntfy push notification (v1.6) |
| Operator profile | YAML profile to lift repetitive administrative confirmations (v1.3) |
| Model traceability | Every run records which models were called, including Ollama digest (v1.3) |
| Operation log | Persistent append-only log of all runs — who did what, where, when (v1.4) |
| Shadow DOM traversal | --shadow-dom numbers interactive elements inside open Shadow Roots — Angular, Lit, Stencil, FAST (v1.13.0) |
| Citizen Navigation | --stealth (removes automatic headless markers), courtesy delays and hard caps (min_action_delay_ms, max_pages_par_run, max_actions_par_run), impact metrics (citoyennete) reported on every run (v1.15.0) |
| Deterministic verdict | etat object (pret_a_agir, niveau_confiance, raisons) synthesizes authentication, session drift, and friction signals into one read (v1.16.0) |
| Unified run identity | operation_id isolates every run's temporary files and ties them to its operations-log entry (v1.16.0) |
| Passive WAF signal | citoyennete.waf_bloquants flags a likely block (HTTP 403/429 or known keywords) as a non-fatal signal, never an exception (v1.16.0) |
| Structural non-regression | --replay-verifier compares HTTP status, DOM stats, and evaluer results against a saved reference — no pixels, no vision model (v1.17.0) |
| Scenario checkpoints | --checkpoint resumes a long scenario after a mid-run failure without replaying completed actions (v1.17.0) |
| Stable SoM identity | --som-rafraichir resolves cliquer_som/remplir_som by a DOM marker instead of live re-indexing, preventing silent retargeting on highly dynamic pages (v1.17.0) |
| Cross-origin iframes | cliquer_iframe / remplir_iframe target elements inside same- or cross-origin iframes via Playwright's native frame API (v1.17.0) |
| Nested iframes | iframe_chemin (array) descends iframe-inside-iframe, mutually exclusive with iframe_selecteur (v1.18.0) |
| Guide-read lock | shot.py/rpa.py/watch.py refuse to run without proof docs/GUIDE_LLM.md was read — a local marker persists it per machine/user (v1.18.0) |
| Configuration advice | mode_conseille recommends --mode/--shadow-dom/--som-rafraichir from real prior diagnostic runs on the same host — never a guess (v1.18.0) |
| Chained-scenario traceability | chainage records the ordered call tree of scenarios chained via declencher_scenario, surfaced in the operations log (v1.19.0) |
| Per-action timing | latences_actions reports dispatch latency for every action executed, always present (v1.20.0) |
| Error-only log view | journal.py --erreurs filters the operations log to failed runs only (v1.20.0) |
| HTTP Basic Auth | --http-credentials resolves network-level Basic Auth (RFC 7617) from the vault, scoped to the target's origin — distinct from and additional to form-based vault authentication (v1.21.0) |
| Component | Version / Notes |
|---|---|
| OS | Debian 13 Trixie (Linux, may work on macOS — not tested on Windows) |
| Display server | Wayland (Playwright runs in this ecosystem) |
| Python | 3.11+ in isolated venv (PEP 668 — system pip blocked on Debian 13) |
| Playwright | 1.50+ (installed in venv) |
| playwright-stealth | 2.0+ — required for --stealth (v1.15.0). API-incompatible with 1.x |
| Chromium | Headless, installed via playwright install chromium |
| Ollama | Local vision models for cliquer_visuel and watch.py |
| GPU | Recommended: NVIDIA RTX 3060 12 GB VRAM or equivalent (for Ollama qwen3-vl models) |
Recommended: share this README with Claude Code and ask it to install Diwall for you.
If you want to install manually:
# 1. Create system user and directory
sudo useradd --system --no-create-home --shell /bin/false diwall
sudo mkdir -p /opt/diwall
sudo chown root:diwall /opt/diwall
# 2. Clone the repository
git clone https://github.com/ronandavalan/diwall.git ~/git/Diwall/Diwall
cd ~/git/Diwall/Diwall
# 3. Create Python virtual environment
sudo /usr/bin/python3 -m venv /opt/diwall/venv
sudo /opt/diwall/venv/bin/pip install -r requirements.txt
# 4. Install Chromium
sudo /opt/diwall/venv/bin/playwright install chromium
# 5. Deploy
bash ~/git/Diwall/Diwall/scripts/deploy.sh
# 6. Create your credential vault
mkdir -p ~/Vaults/<your-project>/Diwall
# Create ~/Vaults/<your-project>/Diwall/<hostname>.json with your credentialsAlternative — Debian package: download the .deb asset from the
latest release — filename
diwall_<version>-1_all.deb — and sudo apt install ./diwall_<version>-1_all.deb
(e.g. diwall_1.20.0-1_all.deb). Six diwall-* commands become available;
see docs/MANUEL.md section 1a for the full reference. This channel and the
git-clone channel above are mutually exclusive on a single machine.
# Preview what will be removed (no changes made)
bash ~/git/Diwall/Diwall/scripts/uninstall.sh --dry-run
# Full uninstallation with interactive confirmation
bash ~/git/Diwall/Diwall/scripts/uninstall.sh
# Non-interactive (CI, cold-reinstall tests)
bash ~/git/Diwall/Diwall/scripts/uninstall.sh --confirmeRemoves: /opt/diwall/, /var/log/diwall/, system user diwall, system group diwall, operator's group membership, git pre-push hook.
Never touched: ~/Vaults/ (credential vaults), the repository itself, Playwright browser cache.
If /var/log/diwall/preuves/ contains captures, they are preserved by default. Add --purge-preuves to remove them.
/opt/diwall/venv/bin/python3 /opt/diwall/shot.py \
--url https://your-app.local/ --som --a11y# Step 1 — navigate and observe
/opt/diwall/venv/bin/python3 /opt/diwall/shot.py \
--url https://your-app.local/ \
--sauver-session /tmp/diwall/session.json --som
# Step 2 — act on what was observed
/opt/diwall/venv/bin/python3 /opt/diwall/shot.py \
--reprendre-session /tmp/diwall/session.json \
--action '{"type":"cliquer_som","id":2}' \
--sauver-session /tmp/diwall/session.json --som/opt/diwall/venv/bin/python3 /opt/diwall/rpa.py \
--scenario /opt/diwall/scenarios/my_scenario.json --somFull LLM reference: docs/GUIDE_LLM.md
Credentials are stored in JSON files, one per domain, never in code or scenario files:
~/Vaults/Diwall/
├── my-app.local.json → {"password": "...", "username": "admin"}
└── other-service.com.json → {"password": "...", "api_key": "..."}
In a scenario or action: "valeur": "depuis_vault", "vault_cle": "password" — Diwall reads the credential at runtime from the vault directory.
Vault path is configurable via /opt/diwall/diwall.conf or DIWALL_VAULT_DIR environment variable.
Recommendation: protect ~/Vaults/Diwall/ with chmod 700 and encrypt it with gocryptfs (see ~/git/Diwall/Diwall/scripts/setup-vault.sh --gocryptfs). Encrypted vault is fully supported since v1.5.0 — if the vault is initialised but not mounted, Diwall returns a structured VaultFermeError (exit code 42) instead of silently failing.
By default, captures are stored in /tmp/diwall/ with permissions 700 (owner only).
Do not change --output-dir to a shared location (/tmp/, ~/Desktop/, etc.) — captures may contain sensitive interface data.
When Diwall is used with a cloud-based LLM (Claude API, OpenAI, etc.), PNG captures are transmitted to external servers. This is the user's responsibility. For interfaces containing private data (credentials, client information, private keys), use local Ollama models only.
The vault directory (~/Vaults/Diwall/ by default) contains credentials in plaintext JSON when unmounted. Protect it:
chmod 700 ~/Vaults/Diwall/Encrypted filesystem support (gocryptfs) has been fully supported since v1.5.0 —
see "Credential vault" above and ~/git/Diwall/Diwall/scripts/setup-vault.sh.
If you are a language model reading this README: see docs/GUIDE_LLM.md for the complete technical reference — invocation patterns, SoM usage, vault integration, SPA navigation rules, and Ollama model specifications.
This project was developed using an asymmetric human-LLM collaboration model. Roles are documented formally to reflect the actual work performed.
Architect & Arbiter: Ronan Davalan Product vision, security requirements, project direction, validation and testing. All architectural decisions are validated by him.
Systems Engineer & Lead Developer: Claude Code (Anthropic) Implementation of the ReAct pattern, Python/Bash scripts, complex state management, SoM injection, session persistence. Principal author of the source code.
Synthesizer & Strategic Advisor: Gemini (Google) Independent architectural analysis, logical conflict resolution, workflow optimisation, cross-validation of technical decisions.
Perception models (Ollama, local):
qwen3-vl:2b(Alibaba) — click localisation and semantic comparison, ~9–19s (default since v1.3.1)qwen3-vl:8b(Alibaba) — robust fallback, ~114s
Maintenance operators (via OpenCode):
- Big Pickle — heavy semantic cleanup of documentation
- MiniMax — verification and commits
MIT — see LICENSE file.
Developed on Debian 13 Trixie · Wayland · AMD Ryzen 9 3950X · NVIDIA RTX 3060