Architecture
How a research campaign runs, and what leaves your machine by which door, with the file of the code behind each line.
How a research campaign runs
manifest.json
│ campagne.py
▼
SearXNG ── queries
▼
light HTTP fetch ──► real browser (only if the page does not suffice)
▼
collecte.jsonl + operations.jsonl
▼
OpenCode ── ranking, then the report
▼
rapport_<timestamp>.md
- The manifest is a JSON file of targets: a
queryfor SearXNG, a fixedurl, aproduit(find the one page that really is the product page), or atable_reference(a list of trusted domains for a subject). Only the campaign identifier and the targets are required. - Discovery. A
querytarget goes to the SearXNG instance you configured, which in turn queries other search engines that Dinoer does not control. - Collection. Each page is fetched with a plain HTTP request, after its
robots.txthas been read. Only a page whose text cannot be read that way goes to a real browser, one Playwright process per page. A target that fails does not stop the others. - The corpus. One
collecte.jsonlper campaign, one line per page retained, andoperations.jsonl, which records every attempt, failed or not. - The report. The pages are reordered (by time window and by local
embeddings, when the manifest asks for it), cut to a size budget, and handed
to OpenCode, which writes the body of
rapport_<timestamp>.md. Dinoer appends the list of sources itself; the model never writes it.
shot.py and rpa.py work differently: each call is its own process and
ends as soon as it has answered. Why →
What leaves your machine, and by which door
| What | Where it goes | Who decides | In the code |
|---|---|---|---|
| Discovery queries | the SearXNG instance you configured, which queries other engines in turn | you, with DINOER_SEARXNG_URL | lib/searxng.py |
| Page requests | each site directly, its robots.txt read first | the targets in your manifest | lib/fetch_leger.py, rpa.py |
| Collected text | the OpenCode model you name, for the report, for targeted extraction, and to choose a product page (the first 1,500 characters of each candidate) | you, with DINOER_OPENCODE_MODEL | lib/modeles.py, lib/synthese.py, lib/extraction.py, lib/selection_candidats.py |
| A subject, for a reference table | the same OpenCode model, the subject only, when no table exists for it yet | the table_reference target | lib/tables_reference.py |
| Embeddings | your local Ollama, http://localhost:11434: the text stays on the machine unless you point DINOER_OLLAMA_URL elsewhere | you | lib/vector.py |
| A notification | a ntfy server, https://ntfy.sh unless you set another: the campaign identifier and the number of sources. Only when a ntfy topic is set | you, with ntfy_topic, DINOER_NTFY_TOPIC, DINOER_NTFY_URL | campagne.py, lib/ntfy.py |
| Your credentials | nowhere: resolved inside the Playwright process, from the encrypted directory | — | lib/repertoire_chiffre.py |
Source: the code of version 1.0.1, read on 27 September 2026. The models
listed under opencode/ are hosted by their provider (opencode.ai/zen,
according to opencode models --verbose, OpenCode 1.18.32). What the model may
do with the web is on the trust page.