Dinoer

Architecture

Why Dinoer is built the way it is: what it guarantees, what it refuses, and where its limits are — narrowed once already on real evidence.

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
  1. The manifest is a JSON file of targets: a query for SearXNG, a fixed url, a produit (find the one page that really is the product page), or a table_reference (a list of trusted domains for a subject). Only the campaign identifier and the targets are required.
  2. Discovery. A query target goes to the SearXNG instance you configured. That instance queries other search engines in turn, which Dinoer does not control.
  3. Collection. Each page is fetched with a simple HTTP request, after its robots.txt has been read. Only a page that does not suffice is escalated to a real browser, one Playwright process per call. A target that fails does not stop the others.
  4. The corpus. One collecte.jsonl per campaign, one line per successful extraction, and operations.jsonl, which records every attempt, failed or not.
  5. The report. The pages are ranked (by local embeddings when you give a subject), cut to a size budget, and handed to OpenCode, which writes the body of rapport_<timestamp>.md. The list of sources is appended by Dinoer itself; the model never writes it.

shot.py and rpa.py are different: independent calls, one process each, that finish as soon as they have answered. Why →

What leaves your machine, and by which door

WhatWhere it goesWho decidesIn the code
Discovery queriesthe SearXNG instance you configured, which queries other engines in turnyou, with DINOER_SEARXNG_URLlib/searxng.py
Page requestseach site directly, its robots.txt read firstthe targets in your manifestlib/fetch_leger.py, rpa.py
Collected textOpenCode’s model, hosted by default, for the report, for targeted extraction, and to choose a product page (the first 1,500 characters of each candidate)you, with DINOER_OPENCODE_MODELlib/modeles.py, lib/synthese.py, lib/extraction.py, lib/selection_candidats.py
A subject, for a reference tableOpenCode’s model, the subject only, when no table exists for it yetthe table_reference targetlib/tables_reference.py
Embeddingsyour local Ollama, http://localhost:11434: the text stays on the machine unless you point DINOER_OLLAMA_URL elsewhereyoulib/vector.py
A notificationa ntfy server, https://ntfy.sh unless you set another: the campaign identifier and the number of sources, never the local path of the report. Only when a ntfy topic is setyou, with ntfy_topic, DINOER_NTFY_TOPIC, DINOER_NTFY_URLcampagne.py, lib/ntfy.py
Your credentialsnowhere: resolved inside the Playwright process, from the encrypted directory—lib/repertoire_chiffre.py

Source: the code of version 1.0.1, read on 25 September 2026. The opencode/ models are hosted: that provider answers at opencode.ai/zen (per opencode models --verbose, OpenCode 1.18.32). What the model is then allowed to do with the web is on the trust page, with the limit it does not cover.

In detail