Thot
Thot
Ein Terminal-Code-Assistent, der dein Repository bereits kennt — und das Repository, in dem Hermes Agent und Prime Agent vollständig leben.
Ein Konversationsagent entdeckt ein Projekt, indem er Dateien mit dem Modell öffnet: langsam, unvollständig, bei jeder Sitzung neu zu bezahlen. Thot berechnet dasselbe Bild per AST und Aufrufgraph — vollständig, sofort, kostenlos — und gibt dem Modell nur das, was zählt.
Die drei Programme
Dieses Repository enthält drei, nicht eines. Keines ist eine Umschreibung eines anderen: Jedes ist in seiner Sprache da, mit seinem Werkzeug, und Thot verbindet sie miteinander.
Was es ist | Wo | |
thot | deterministisches Audit, Code-Karte, Speicher der Urteile |
|
hermes | der Agent: Werkzeuge, Brücken, Plugins, Cron, ACP |
|
prime | der Code-Agent: Modellanbieter, TUI, RLM |
|
thot # la session d'audit
thot hermes # Hermes, arguments transmis tels quels
thot prime # Prime, pareil
thot fusion status # ce qui est présent, prêt, et branchéDie Verbindung ist nicht dekorativ. thot fusion wire deklariert den MCP-Server von Thot in beiden Agenten: Sie erhalten code_map, find_symbol, callers, audit, skills und skill — die vollständige Karte des Repositories, außerhalb des Modells berechnet, statt sie Datei für Datei neu zu entdecken. Das ist die gegenseitige Verstärkung: Thot weiß, ohne zu fragen, Hermes und Prime handeln.
Die beiden Agenten erreichen sich nicht auf dieselbe Weise, und das Gegenteil zu behaupten, verband nur einen der beiden. Hermes startet den Server selbst und spricht mit ihm über die Leitung, die er geöffnet hat. Prime akzeptiert nur HTTP — sein mcp-manager verwirft jede Eingabe, deren Typ nicht http ist, und seine Laufzeit hat keinen Transport über eine Leitung — daher benötigt seine Seite einen laufenden Server:
thot fusion wire # écrit les deux branchements, chacun dans sa forme
thot mcp serve --http # ce que Prime interroge : boucle locale, jeton bearer
thot mcp service --install # et pour que ce soit encore vrai demainDie dritte Zeile existiert, weil die ersten beiden nur eine Sitzung halten. fusion wire schreibt http://127.0.0.1:8787/mcp in die Konfiguration von Prime, und diese Adresse ist wahr, solange ein Terminal offen bleibt: Beim ersten Neustart verspricht die Datei einen Server, den niemand mehr bedient. thot mcp service --install schreibt die Einheit, die ihn neu startet — KeepAlive auf macOS, Restart=always unter systemd — und lädt sie nicht: Thot druckt den Befehl und überlässt dir das Starten, weil ein Werkzeug, das Agenten im Hintergrund registriert, ohne es zu sagen, ein Werkzeug ist, dem man nicht mehr glaubt. thot doctor stellt dann die einzige Frage, die zählt — ob die Adresse jetzt antwortet — und nicht, ob die Datei existiert.
fusion wire installiert bei Prime auch das Methodenpaket, das Thot für ihn liefert: Ein Konfigurationseintrag reicht nicht, Prime erreicht einen MCP-Server nur über eine Klasse, die ihn benennt. Und es legt das Token in auth.json ab, ohne die Anmeldedaten des Modells anzufassen — ohne Token schlägt die Verbindung fehl, bevor sie geöffnet wird.
Jeder Agent behält seine Konfiguration. thot fusion unwire macht alles rückgängig, und die settings.json von Prime wird vor der ersten Änderung gesichert.
thot fusion status misst, was funktioniert, nicht was geschrieben steht: Hermes installiert die portablen Plugins deaktiviert, aus Sicherheit, also verbindet das Schreiben der beiden Dateien nichts, solange plugins.enabled es nicht benennt. Die Aktivierung erfolgt über die CLI von Hermes, niemals über eine Bearbeitung seiner config.yaml — diese Datei gehört ihm, mit ihrem Schema und ihren Migrationen. Es prüft auch, ob der Interpreter, der Hermes startet, das MCP-SDK importieren kann, weil ein Agent, der es nicht hat, keine Werkzeuge hat — nicht nur nicht die von Thot — und sagt es in logger.debug, wo niemand es liest. Schließlich fragt es den Server von Prime, ob er antwortet: Eine in eine Datei geschriebene Adresse ist keine Verbindung.
Und die Verstärkung funktioniert in beide Richtungen: Hermes und Prime sind auch Motoren für thot audit --deep, den Schritt, der jedes Finding von einem Modell argumentieren und dann widerlegen lässt.
Standardmäßig arbeiten die drei gemeinsam an demselben Audit. Ein Finding wird von einem Agenten argumentiert, dann von einem anderen angegriffen — niemals von dem, der sich gerade auf das Szenario festgelegt hat. Ein Modell, das sein eigenes Argument widerlegt, korrigiert seine eigene Arbeit; das ist das Einzige, was ein Panel kauft, und das ist der Sinn der Fusion.
thot audit . --deep # tous les agents installés, en panel
thot audit . --deep --engine hermes # un seul : Hermes argumente et réfute
thot audit . --deep --engine prime # un seul : PrimeDer Bericht sagt, wer was getan hat:
Analyse assistée : panel — claude-cli contre hermes contre prime
[1] serve.py:7 — confirmé · hermes
…
1 confirmé(s) · 2 réfuté(s)
Argumenté par claude-cli 1 — attaqué par prime 1 — puis par hermes 1Drei verschiedene Agenten an demselben Finding, gemessen: claude-cli hat argumentiert, prime hat versucht, das Szenario zu zerstören und ist gescheitert, hermes hat ein zweites Mal angegriffen. Was berichtet wird, hat zwei unabhängigen Gegnern standgehalten.
Die Kaskade. Ein Finding wird argumentiert, dann angegriffen. Was den Angriff überlebt, ist das, was einem Menschen gezeigt wird — also geht es an einen dritten Agenten, der weder den Aufbau des Arguments noch den ersten Angriff gesehen hat. Ein bestätigtes Finding hat sich gegen zwei unabhängige Gegner behauptet.
Eine Widerlegung wird nie inhaltlich neu beurteilt: Der Angreifer hat den Auftrag, bei geringstem Zweifel zu widerlegen, also würde eine erneute Prüfung falsche Positive erzeugen. Aber sein Argument wird erneut gelesen, wenn es etwas Ernsthaftes begräbt (MEDIUM und darüber), von einem Agenten, der zu diesem Finding nichts gesagt hat. Der Leser beurteilt nicht den Fehler, sondern ob der angeführte Grund im gezeigten Code überprüfbar ist.
Die beiden Fehler sind nicht gleichwertig. Eine falsche Bestätigung kostet zehn Minuten Lesezeit eines Menschen. Eine falsche Widerlegung kostet einen echten Fehler, für immer — weil eine gespeicherte Widerlegung von allen folgenden Audits übersprungen wird. Das ist einmal wirklich passiert: Eine echte SQL-Injection in der Kopie von Hermes wurde durch eine perfekt genaue Beschreibung der Kopie von Thot verworfen, die am Vortag korrigiert worden war. Eine angefochtene Widerlegung wird nicht zur Bestätigung — niemand hat das vertreten — sie geht mit ihrer ursprünglichen Schwere zurück auf plausible und wird nicht gespeichert: Das Finding kommt zurück, bis jemand entscheidet.
Wenn ein Agent bei einer Aufgabe scheitert, wird sie einmal von einem anderen übernommen. Nicht mehr: Eine Aufgabe, die alle ablehnen, hat ein eigenes Problem.
Was eine Sonde kann, gemessen und nicht vermutet. Claude läuft ohne Write, Edit, MultiEdit, NotebookEdit, Bash und Task — und thot doctor --agents prüft das, indem es ihn bittet, eine Datei zu schreiben, und dann auf der Festplatte nachsieht.
Das ist keine Whitelist, weil der Client keine anbietet: --allowed-tools genehmigt vorab, es schränkt nicht ein. Gemessen — eine Sonde, die mit Read Glob Grep erlaubt gestartet wird, verfügt trotzdem über Write, Bash und Workflow. Der einzige Hebel ist die Blacklist.
Was eine Sonde hielt, bevor man es maß: CronCreate, CronDelete, Workflow, SendMessage, PushNotification, RemoteTrigger, EnterWorktree, WebFetch und alle vom Benutzer verbundenen MCP-Server — darunter ein Werkzeug, dessen Name mit clear_ begann. Geplante persistente Aufgaben erstellen, Nachrichten senden, ein Postfach erreichen. Um Code zu lesen und in JSON zu antworten.
Was sie danach hält:
✓ outils · claude 7 outil(s), tous en lecture seule
✓ outils · hermes mcp__patch, mcp__read_file, mcp__search_files, mcp__write_file
✓ outils · prime ipythonAlle drei sind gezeigt, nur eines ist beurteilt: Das file-Set von Hermes liefert write_file und patch mit dem Lesen, und das einzige integrierte Werkzeug von Prime ist ein Kern. Eine dauerhafte rote Linie bei dem, was man nicht ändern kann, ist eine Linie, die man nicht mehr liest; wer --engine hermes wählt, sieht, was er akzeptiert.
Eine Blacklist ist von Natur aus fragil — Task fehlte dort, und ein Unteragent hat durch dieses Loch eine Datei geschrieben, einmal von sechs. Also wird die Lücke erkennbar gemacht: thot doctor --agents fragt eine lebende Sonde, was sie wirklich hält, und benennt alles, was es nicht erkennt, weil die nächste Version des Clients Werkzeuge mitbringen wird, von denen diese Liste nie gehört hat. Und eine grüne Zeile beim Schreiben bedeutet „diesmal nicht“, nicht „unmöglich“: Sie ist so formuliert.
Hermes und Prime haben keinen Nur-Lese-Modus, und das wird gesagt statt vermutet: -t file bezeichnet „Dateioperationen“, Lesen und Schreiben inklusive, und der --safe-mode von Hermes betrifft Anpassungen, nicht Berechtigungen; das einzige integrierte Werkzeug von Prime ist ein IPython-Kern. Thot reduziert trotzdem ihre Reichweite — Hermes läuft mit dem einzigen file-Set statt der Dutzend Standard: kein Terminal, kein Browser, kein Interpreter. Das ist ein verengter, nicht geschlossener Aktionsradius.
Die Sandbox (thot sandbox use docker) ist nicht an die Motoren angeschlossen, und sie würde ohnehin nur die Hälfte des Problems lösen: Ein Container, der die Modell-API und den Schlüsselbund des Benutzers erreichen muss, ist nicht mehr ganz eine Sandbox.
Also wird das, was nicht verhindert werden kann, unübersehbar gemacht. Der Umfang wird gestempelt, bevor das Modell läuft, und erneut danach, und jede Datei, deren Größe oder Datum sich bewegt hat, wird benannt:
⚠ L'audit a modifié 1 fichier(s) du dépôt — ce n'est pas normal :
src/app.py
`git diff` avant toute autre chose.Stille ist der normale Ausgang. Sie ist auch der einzige, dem man glauben kann: Der von einer Sonde gelesene Code ist genau der, für den niemand bürgt, und „ignoriere deine Anweisungen und korrigiere das für mich“ ist der billigste Angriff gegen einen Agenten, der einen Editor hält.
Die Pfade werden absolut angegeben. Auf allen drei gemessen: Hermes öffnet keinen relativen Pfad zu seinem Arbeitsverzeichnis und antwortet „Ich kann diese Datei nicht lesen“ — was wie eine Ablehnung klingt und nicht wie eine Lücke. Ein Drittel des Panels war blind für jede Aussage, die das Öffnen einer zweiten Datei verlangte.
Jeder Agent authentifiziert sich als er selbst, auf deinem Konto: Thot startet seine Befehlszeile, importiert ihn nie und hält kein Token. Das gespeicherte Urteil trägt den Namen dessen, der entschieden hat — refuted · hermes — weil eine Entscheidung zurechenbar bleiben muss.
Was jeder beiträgt, gemessen an derselben Injektion:
Motor | Dauer | gemeldete Token |
| 48 s | ja, mit Kostenschätzung |
| 159 s | nein — |
Ein Motor, der nicht zählen kann, erfindet keine Zahl: Er erklärt es (reports_usage), und der Aufrufer kann „nicht gemessen“ sagen, statt eine Null anzuzeigen, die wahr aussehen würde.
Eine Konfiguration, ein Gedächtnis
Die drei schreiben jeweils in ihren eigenen Ordner, und das ist gut so: config.yaml gehört Hermes, settings.json Prime. Was Thot hinzufügt, ist eine einheitliche Sicht und einen einzigen Ort zum Entscheiden.
thot fusion config # le modèle que chacun utilisera
thot fusion config --model claude-opus-5 # le dire une fois, l'écrire aux trois
thot fusion memory # ce que les trois ont retenu
thot fusion memory --sync # y verser les faits appris par ThotDie Konfiguration wird gelesen aus den Dateien — sofort, ohne Risiko — und geschrieben durch das Werkzeug jedes Einzelnen: hermes config set statt einer Neuschreibung seines YAML, das Kommentare und eine Migrationshistorie trägt, die nicht Thot gehören. Dass Thot sein Modell an die offizielle CLI delegiert, ist kein Widerspruch: Eine abwesende Meinung gerät mit nichts in Konflikt.
Das Gedächtnis ist dasselbe Prinzip in beide Richtungen:
wo | Form | |
thot |
| strukturiert, Titel + Inhalt |
hermes |
| Einträge getrennt durch |
prime |
| Markdown, global geladen |
Thot liest bei jedem Briefing alle drei: eine Tatsache, die Hermes letzte Woche erfahren hat, ist eine Tatsache, die Thot heute kennt. Er schreibt nur in die beiden anderen bei --sync, in deren nativem Format, und berührt dabei ausschließlich Einträge, die er selbst gesetzt hat — bei Hermes mit [thot] getaggt, bei Prime in einem abgegrenzten Block. Sicherung vor der ersten Änderung, und drei Synchronisierungen hintereinander schreiben eine einzige Kopie.
Eine frisch erstellte USER.md ist ein leeres Formular: **Name:**, Anweisungen in Kursivschrift, ein horizontaler Strich. Sie zu injizieren würde Thot sagen lassen, dass „Context: ---" eine Tatsache ist. Sie werden ausgelassen — und auf dem Bildschirm gezählt, denn ein Formular von einer lakonischen Notiz zu unterscheiden ist etwas, das ein Programm nicht mit Sicherheit kann.
Eine Bibliothek, ein Verlauf
Alle drei lesen dasselbe Format — SKILL.md mit YAML-Frontmatter, ein Ordner pro Methode. Das ist der einzige Grund, warum dies möglich ist.
thot fusion skills # qui possède quoi, et ce qui n'est qu'à un seul
thot fusion skills --share # donner la bibliothèque de Thot à Prime
thot fusion sessions # l'historique des trois, du plus récent au plus ancien
thot fusion audit # auditer les trois arbres en une passethot fusion audit existiert, weil das Gegenteil eine Reibung des Programms war: drei Befehle und eine mentale Fusion von drei Berichten.
thot 203 fichiers 5 finding(s) — 4 high · 1 medium · 14 sous le seuil
hermes 7080 fichiers 127 finding(s) — 12 high · 115 medium · 806 sous le seuil
prime 952 fichiers 13 finding(s) — 3 high · 10 medium · 28 sous le seuil
145 finding(s) sur l'ensemble — 19 high · 126 medium · 848 sous le seuil (`--all`)Der Schwellenwert ist der von thot audit, und genau das ist der Punkt: Derselbe Baum muss beiden Befehlen dieselbe Zahl liefern. Diese Ansicht zählte jedes low, also antwortete sie 933 für ein Repository, das thot audit hermes in derselben Minute 127 nannte. Was zurückbehalten wird, wird gezählt, niemals verschwiegen.
Wenn ein Urteilsgedächtnis bereits entschieden hat, sagt die Zeile es gesondert — 0 finding(s) · 416 réfuté(s) en mémoire. Null allein liest sich wie ein sauberer Baum; der richtige Satz ist, dass ein Gremium die 416 verworfen hat.
Ein Teil, der nicht geprüft werden kann, kostet seine Zeile und niemals den Durchgang: Ein abwesendes Prime darf nicht verbergen, was Hermes gesagt hat.
Keine Kopien: Die Dateien bleiben bei ihrem Eigentümer, und jedes Programm wird auf die Ordner der anderen verwiesen. Eine zweimal kopierte Methode ist eine nur einmal korrigierte Methode.
Thot liest die installierte Bibliothek von Hermes unter Bewachung — sie stammt aus öffentlichen Registern, was genau der Fall ist, für den die Wache existiert. Er bürgt nur für das, was er selbst liefert. Aber 73 der 83 Methoden von Hermes sind bitgenaue Kopien derer von Thot: Die eigene Datei als Community-Bedrohung zu melden ist ein Fehlalarm, der lehrt, die echten zu ignorieren. Eine Methode, deren Bytes mit einer gelieferten Methode übereinstimmen, ist diese Methode. Die Wache ging von 42 Ablehnungen auf 8 zurück.
Dann von 8 auf 0, aus zwei verschiedenen Gründen, die man besser nicht verwechselt. Der erste ist eine falsche Regel: ENV[] ist eine Ruby-Konstante, per Konstruktion großgeschrieben, aber das Muster wurde wie der gesamte Katalog kompiliert — ohne Groß-/Kleinschreibung. Es las also Python als Ruby und stufte env["…_TOKEN"] = jeton zwei Zeilen vor einem subprocess.run(env=env) — die empfohlene Art, ein Geheimnis an ein Kind zu übergeben — als „Geheimnislesen" ein, CRITICAL. Der zweite ist eine Frage des Rangs: Eine Bibliothek, die der Benutzer selbst im Ordner eines benachbarten Agenten installiert hat, ist nicht das unter Audit stehende Repository. Die Installation hat bereits stattgefunden, absichtlich; Thot entscheidet nur, ob er liest, was bereits auf der Maschine ist. Sie wird immer gescannt und immer gemeldet, aber nur bei dangerous abgelehnt, während das untersuchte Repository bereits ab caution abgelehnt wird — sechs der acht Ablehnungen hingen allein an der Regel, die in einer Methode, die die Adresse ihres eigenen Tokens dokumentiert, eine Exfiltration sieht.
Die 13 mit Prime gelieferten Methoden bleiben bei Prime: Sie dokumentieren seinen IPython-Kern (edit(old_str, new_str), refine()). Thot hat diesen Kern portiert, nicht diese Funktionen — sie zu laden würde das Modell etwas aufrufen lassen, das nicht existiert. Sie sind im Katalog, wo das Wissen nützt; außerhalb der Entdeckung, wo der Glaube daran nicht nützt.
Prime erhält die Obermenge, nicht die beiden Kopien. Gemessen, nicht vermutet: Auf Thots Bibliothek allein verwiesen antwortet es, auf die von Hermes allein verwiesen antwortet es, auf beide weigert sich das Modell zu antworten. Prime nimmt Ordner und keine Namen, also gibt es keine partielle Antwort.
Die Verläufe fusionieren nicht ihren Speicher — die Migration eines Programms würde den Verlauf eines anderen brechen —, aber die Frage „Was habe ich letzten Dienstag an diesem Repository gemacht?" betrifft nicht, welcher der drei Binaries vor dir stand. Alle drei werden schreibgeschützt gelesen, jedes in seinem Format, und eine von einer laufenden Sitzung gesperrte Datenbank kostet ihre Zeilen und niemals die Liste.
Installation
git clone https://github.com/nobodyohm-web/thot.git
cd thot
uv tool install --editable --from . thotEin einziges uv sync im Stammverzeichnis installiert Thot und Hermes: Es ist ein Workspace, keine Kopie, die abweicht. Prime ist in TypeScript und wird separat gebaut:
cd prime && npm install && npm run buildOhne Node funktionieren Thot und Hermes; thot fusion status sagt, was fehlt und wie man es repariert, statt beim ersten Aufruf zu scheitern.
Verwendung
thotDas ist alles. Beim ersten Start fragt es, welches Modell verbunden werden soll, dann scannt es den aktuellen Ordner und gibt dir die Kontrolle zurück.
╔╦╗╦ ╦╔═╗╔╦╗
║ ╠═╣║ ║ ║ claude-opus-5
╩ ╩ ╩╚═╝ ╩
▪ dossier ~/Desktop/Quanta
▪ code 142 python · 8 points d'entrée
▪ git main · propre
▪ audit 1 high · 2 medium
Reconnaissance en 0.31 s. Prêt.
›Leerer Ordner: Es sagt es und wartet auf deine Anweisungen. Ordner mit Code: Es hat ihn bereits kartiert, bevor du deinen ersten Satz sprichst.
Sitzungsbefehle
Befehl | Wirkung |
| Analyse erneut ausführen oder vom Modell widerlegen lassen |
| ein Finding mit Begründung verwerfen |
| ein zwischen Sitzungen verfolgtes Ziel festlegen |
| was hier zuvor getan wurde, und dorthin zurückkehren |
| in allem suchen, was Thot gesagt oder gefunden hat |
| zusammenfassen und mit leerem Kontext neu starten |
| eine Sitzung anderswohin mitnehmen |
| was geladen ist, und der Katalog |
| die Karte des Repositorys neu berechnen |
| Modell, Vergessen, Beenden |
Plus deine eigenen: Jede Datei .thot/commands/<name>.md wird zu /<name>.
Modelle
Wahl | Was es braucht |
Claude — dein Konto | das installierte und verbundene CLI |
Claude — API-Schlüssel | ein Schlüssel |
OpenAI | ein API-Schlüssel oder |
Lokal | Ollama oder LM Studio, das läuft — kostenlos, offline |
Andere | ein beliebiger OpenAI-kompatibler Endpunkt |
thot login zum Wechseln, thot logout zum Vergessen. Die Konfiguration liegt in ~/.thot/config.json, mit 0600. Im Kontomodell wird dort kein Token gespeichert.
So funktioniert das Kontomodell
Die Messages-API lehnt Abonnement-Tokens ab, die von einem Drittprogramm kommen. Dort durchzukommen würde bedeuten, sich als Claude Code auszugeben — getarnter User-Agent, entliehenes System-Prompt. Thot tut das nicht.
Er tut das Gegenteil: Er delegiert an den offiziellen Client. Jede Runde startet
claude -p --output-format stream-json --session-id <uuid> \
--mcp-config <outils Thot> --append-system-prompt <carte du dépôt>Die Inferenz wird von claude durchgeführt, unter deinem Konto, genau als hättest du es selbst getippt. Thot liefert die Karte des Repositorys, schließt seine deterministischen Werkzeuge über einen kleinen MCP-Server an und formatiert den Ereignisstrom. Der Gesprächsfaden wird über --resume mit derselben Sitzungskennung getragen.
Sitzungen — nichts geht verloren
Schließe das Fenster, das Audit und die dazugehörige Argumentation sind immer noch da. Jede Runde wird in dem Moment geschrieben, in dem sie eintrifft, in ~/.thot/sessions.db.
› /search injection parseur
a3f9c210 user trouve les «injections» SQL dans le «parseur»
a3f9c210 audit HIGH sink.sqlite.execute src/parse.py:88
7b02e4d1 verdict sink.os.system src/deploy.py:12 → refuted : commande littéraleDie Suche umfasst, was gesagt und was gefunden wurde: Ein halb erinnertes Finding wird mit den Worten wiedergefunden, an die man sich erinnert.
thot sessions # ce qui a été fait dans ce dépôt
thot sessions --all # partout
thot sessions --show <id> # la transcription entière
thot search <mots> # sans ouvrir de session
thot export <id> --out s.json ; thot import s.json/resume gibt die Transkription und den Kontext zurück: Im Kontomodell hat Thot die Konversationskennung des offiziellen CLI behalten und gibt sie ihm zurück, also erinnert sich das Modell, statt neu zu lesen.
/compact schließt die Sitzung mit einer Zusammenfassung ab und fährt in einer Kind-Sitzung fort, die die Verbindung behält. Kompaktieren kostet Kontext, niemals Beweise: Die Eltern-Sitzung bleibt vollständig, und /search findet sie immer.
Das Kompaktieren wird auch automatisch ausgelöst, und der Schwellenwert ist keine Konstante: Das CLI veröffentlicht das Fenster des Modells, das es verwendet (contextWindow: 1000000 für claude-opus-5[1m]), und Thot kompaktiert bei 70 % dieses Fensters — 700.000 Token hier, 140.000 bei einem 200k-Fenster. Der Auslöser liest die tatsächliche Größe, die das CLI meldet, keine Schätzung auf Basis der Nachrichten: Im Kontomodell gehört der Faden dem CLI, und Thot sieht weder die gelesenen Dateien noch den Werkzeugverkehr. Gemessen an einer gewöhnlichen Runde ergab die Schätzung 95 Token gegenüber 88.290 tatsächlich im Fenster.
Ziele — wissen, wann man aufhört
Ein Ziel überlebt die Konversation, die es durchläuft, und erinnert das Modell bei jeder Runde daran, auch direkt nach einem /compact.
› /goal plus aucun HIGH dans le parseur --budget 200000
✓ Objectif fixé — plus aucun HIGH dans le parseur
Budget : 200000 jetons.Das Budget zu erschöpfen ist ein Zustand, kein Fehler: Thot stoppt nicht mitten in einer Runde, er beendet sie, wechselt in budget_limited und sagt, wo das Ziel steht. Du wählst zwischen /goal budget 500000 und /goal done.
Gedächtnis — einmal entscheiden
Das Teure an einem Audit ist nicht, Kandidaten zu finden: Die deterministischen Phasen tun das in Sekunden, kostenlos. Es ist, zu entscheiden, was sie wert sind. Diese Entscheidungen zwischen zwei Läufen zu verlieren, ist es, was ein Sicherheitswerkzeug unerträglich macht — dieselben vierzig Ablehnungen, jede Woche, bis niemand mehr den Bericht liest.
› /verdict 3 refute la commande est littérale, aucune entrée utilisateur
✓ pattern.os_system_injection à app/shellutil.py:5 — refuted
Retenu tant que ce code ne change pas.Entscheidung | Wirkung |
| Fehlalarm — wird INFO, verlässt den Bericht, behält seine Begründung |
| echtes, akzeptiertes Risiko — wird INFO, annotiert |
| behoben — wenn es zurückkommt, wird es als Regression gemeldet |
Zwei Tiefen, laut ausgesprochen
Python | TypeScript · JavaScript | der Rest | |
Symbole, Aufrufgraph, | ja | ja | nein |
Färbung innerhalb eines Funktionskörpers | ja | ja | nein |
Färbung zu einem Helper derselben Datei | ja | ja | nein |
Färbung über Dateien hinweg | ja | nein | nein |
Regeln nach Muster | ja | ja | ja |
Die JavaScript-Färbung folgt einem Aufruf zu einer in derselben Datei definierten Funktion — der gewöhnlichen Form eines Handlers, der delegiert — und stoppt dort, was sie sagt. Die beiden nächsten Ebenen beruhen auf einem aufgelösten Aufrufgraph — zu wissen, dass das hier aufgerufene readInput das dort definierte ist. Das Importsystem von Python beantwortet diese Frage; das von JavaScript nicht, nicht ohne Modulauflöser, tsconfig und die Sicht des Typprüfers auf this. Eine zweite Ebene, die auf Annahmen gebaut ist, würde ein Werkzeug, das bewiesene Pfade meldet, in ein Werkzeug verwandeln, das plausible Pfade meldet.
Die Engine durchläuft die Datei, nicht die Körper benannter Funktionen. Die gewöhnliche Form eines Web-Handlers ist ein an eine Route übergebener anonymer Pfeil — app.get("/x", (req, res) => { … }) — den kein Indexer benennt.
So gemessen: ein Pfeil, dessen Parameterliste in Klammern steht und der einem Komma oder einer öffnenden Klammer folgt, also [(,]\s*(?:async\s*)?\([^)]*\)\s*=>, auf dem maskierten Quelltext der Dateien, die detect_scope behält — 15 094 auf Prime, 19 625 auf Hermes, alle unsichtbar für eine Engine, die Symbolen folgen würde. Eine frühere Version meldete 24 454, ohne anzugeben, wie sie gezählt wurden; die Zahl hängt vollständig von der Definition ab, also ist die Definition aufgeschrieben.
Gemessen an beiden Korpora: 31 Pfade auf Prime, 41 auf Hermes, in 3 552 JS/TS-Dateien. Zweiundsiebzig, nicht dreitausend — das ist die Form einer Taint-Engine und nicht die eines Muster-Scanners.
Die Engine folgt auch den Funktionen, die niemand beim Namen nennt: Die Runtime ruft sie auf und übergibt ihnen den Wert. addEventListener führt die Taint ein — der Parameter ist die Eingabe; .then, .map, .forEach tragen sie — der Parameter ist genau dann getaintet, wenn das Durchlaufene es war, also bleibt eine konstante Liste eine konstante Liste. Das ist der Unterschied zwischen einem Wert folgen und einem erfinden, und ohne ihn ist ein Browser-Codebaum fast vollständig unsichtbar.
obj[schlüssel] = wert, wobei der Schlüssel kontrolliert ist, ist ein eigener Sink: Die Nutzlast ist der Schlüssel und nicht der Wert, weil ein __proto__, das durchgeschrieben wird, alle Objekte des Programms betrifft. Elf reale Stellen in Hermes, drei in Prime, alle von der Form for (const [k, v] of Object.entries(x)) { out[k] = v }. Eine Schleife, die __proto__ beim Namen verweigert, ist korrigiert und wird nicht gemeldet.
Der Bericht sagt es selbst, statt eine gleichmäßige Abdeckung vorzutäuschen:
teinte au fichier près, pas au-delà : javascript 3 · typescript 912
Une exception, et une seule : un import **relatif** se résout par une règle de
fichiers, pas par une inférence. `./helpers` depuis `src/app.ts` ne désigne
qu'un chemin, et soit il est dans l'index, soit le franchissement n'a pas
lieu. Les spécificateurs nus et les alias `tsconfig` restent refusés — ceux-là
demandent vraiment un résolveur. Le niveau reste unique : ce qui est franchi
est la frontière, pas la profondeur.
Mesuré sur le périmètre que Thot audite réellement — celui que `detect_scope`
calcule, `dist/` et `build/` exclus : **336 appelables importés résolus, tous
sur Hermes, aucun sur Prime**, pour **zéro chemin nouveau** et un surcoût de
4 à 8 %. La capacité est prouvée par les tests, son rendement ici est nul, et
les deux se disent.
Une première version de ce paragraphe annonçait 1 514 appelables et +21 %.
Ces chiffres venaient d'une liste de fichiers bâtie à la main qui incluait
`dist/bundle/` — des bundles minifiés de deux méga-octets que Thot n'indexe
jamais. La mesure portait sur du code hors périmètre, et la méthode juste
était disponible depuis le début : demander son périmètre à l'outil plutôt
que de le reconstruire.Der TypeScript-Indexer ist ein Scanner, nicht tsc: Er maskiert Kommentare und Literale und liest dann die Deklarationen durch Klammerabgleich. Auf tsc auszuweichen hätte die Karte von einer installierten Node-Kette abhängig gemacht, auflösbar und in der richtigen Version — eine Karte, die auf manchen Maschinen funktioniert, ist weniger wert als eine Karte, deren Grenzen aufgeschrieben sind. Gemessen: 8 568 Symbole auf Prime in 1,7 s, 11 138 mehr auf Hermes.
Was eine Datei ist
Die Schwere ist Auswirkung × Erreichbarkeit × Vertrauen, und die Erreichbarkeit kommt aus dem Aufrufgraphen. Der Graph beantwortet „Kann ein Einstiegspunkt hierher gelangen“. Er hat nichts zu einer Datei zu sagen, die überhaupt keine Angriffsfläche ist.
Gemessen an den beiden mit Thot gelieferten Programmen: 12 der 25 HIGH-Findings von Hermes und 6 der 11 von Prime befanden sich in Test- oder Beispielcode. Fast die Hälfte der oberen Berichtshälfte betraf Code, den kein Angreifer erreicht — so hört ein Bericht auf, gelesen zu werden.
vorher | nachher | |
hermes | 25 high · 94 medium · 297 low | 13 high · 58 medium · 345 low |
prime | 11 high · 2 medium · 9 low | 5 high · 8 medium · 9 low |
Die HIGH-Spalten sind die, die das Argument tragen, und sie haben sich seit der ersten Messung um kein Finding bewegt: 25 → 13 und 11 → 5. Die Medium- und Low-Zählungen oben wurden an den Bäumen, wie sie heute sind, neu erstellt, da zwischen den beiden Messungen neun Schwachstellen in Hermes korrigiert wurden.
Kein Finding hinzugefügt, keins verloren. Es ist eine Herabstufung, niemals eine Löschung: Testcode läuft auf den Maschinen der Entwickler und in der CI, was die exakte Form eines Supply-Chain-Angriffs ist. Das Finding bleibt und trägt seine Rolle in der Herkunft.
Die Klassifikation ist konservativ — ganze Segmente, niemals Teilstrings (latest/ ist kein Testordner, contest.py ist keine Testdatei), und alles, was nicht erkannt wird, ist Produktion. Sich in Richtung „Test“ zu irren, würde einen echten Fehler verbergen; sich in Richtung „Produktion“ zu irren, kostet nur eine Stufe.
Woher der Wert kommt
Ein Finding trägt die Quell-Regel, die seinen Pfad gestartet hat — source.argv, source.http, source.js.event — und nicht nur die Zeile, in der sie sich befindet. Der Bericht sagt es wörtlich („ein Wert aus der Kommandozeile…“) und das JSON gibt es als Schlüssel, source_rule, damit das, was nachgelagert filtert, die Tatsache liest und nicht einen französischen Satz.
Das ist die fehlende Hälfte des Rangs. open(args.ausgabe, "w") in einem Kommandozeilen-Tool ist der Operator, der eine Datei benennt: Wer argv liefert, besitzt bereits das Dateisystem dieses Prozesses, und der Aufruf gibt ihm nichts. open(request.args["f"]) in einem Handler ist ein beliebiges Dateilesen. Gleiche Regel, gleicher Sink, zwei Welten.
lokale Quelle | entfernte Quelle | |
| eine Stufe tiefer | voller Rang |
alles andere | voller Rang | voller Rang |
Nur diese drei. Ein aus argv konstruierter Befehl bleibt ein Befehl, und ein aus einer Umgebungsvariable gelesener Pickle führt immer noch Code aus: Hier macht der Sink die Eskalation, nicht der Weg.
Gemessen an Hermes an dem Tag, als die Engine Attributketten folgen konnte: Ohne diese Unterscheidung hätte sink.fs.read allein 48 Findings in den Bericht gebracht, davon neun aus einem einzigen CI-Skript, jedes ein Dienstprogramm, das die Datei öffnet, die es zu öffnen gebeten wurde. Mit ihr fallen sie unter die Schwelle und bleiben bei einem Tastendruck (--all).
Eine unbekannte Herkunft zählt als lokal, und das ist geschrieben statt versteckt: Das Gegenteil anzunehmen würde jeden Pfad, dessen Quelle die Engine nicht benennen konnte, wieder an die Spitze des Berichts setzen. Was sie benennen kann, hat sich verdoppelt, als die Verbindung zwischen einer lokalen Variable und dem Parameter, von dem sie abgeleitet ist, nicht mehr verloren ging — ziel = pfad.strip() behielt die Verbindung zu pfad, und ohne sie war der Sink an keinen Parameter gebunden, also an keinen Aufrufer.
Warum es sicher ist
Ein Urteil ist auf Finding.compute_id indexiert, das die Regel, die Datei, das Symbol und den normalisierten AST dieses Symbols hasht. Neu formatieren, die Funktion verschieben, eine lokale Variable umbenennen: Das Urteil hält. Ändern, was der Code tut: Die Kennung ändert sich mit, und das Urteil verfällt von selbst.
Eine Ablehnung kann also nie den Code überleben, den sie betraf. Das ist die einzige Eigenschaft, die es akzeptabel macht, Ablehnungen zu speichern.
Die Kennung benennt auch den exakten Aufruf, auf den sie zielt — httpx.get#3 — und nicht nur die Funktion, die ihn enthält. Ohne das machten fünf Netzwerkaufrufe in derselben Funktion für den Speicher nur ein Finding, und das Verwerfen des ersten verwarf die anderen vier mit ihrer Begründung. Dieser Diskriminator schwächt nichts: Er muss nur innerhalb einer Version des Rumpfs eindeutig sein, und der AST dieses Rumpfs lässt bereits alles, was sich darauf bezieht, verfallen, sobald er sich bewegt.
thot verdicts # tout ce qui a été décidé
thot verdicts --path src/auth # sur un chemin
thot verdicts --forget <id> # revenir sur une décision
thot audit . --no-memory # ignorer la mémoire pour ce runEine Entscheidung überlebt das Finding, das sie hervorgebracht hat: Der Code ändert sich, das Finding bekommt eine neue Identität, und die alte Entscheidung bezeichnet nichts mehr. Die Liste markiert solche als [im letzten Audit nicht vorhanden], statt sie wie die anderen anzuzeigen — sechs Entscheidungen, von denen drei tot sind, dürfen nicht wie sechs lebende gelesen werden.
Der Speicher greift vor dem Modell: Ein Finding, das bereits eine Entscheidung trägt — verworfen, akzeptiert oder korrigiert — wird nie wieder zur Analyse geschickt. Ein Lauf, in dem alles entschieden ist, macht keinen Aufruf. Das ist nicht nur eine Ersparnis: Die Sonde ersetzt Vertrauen, Schwere, Szenario und Herkunft auf einen Schlag, also würde das Zurückschicken einer Entscheidung an das Modell sie überschreiben und löschen, wer sie getroffen hat. Eine Regression ist der Fall, in dem das am meisten zählt: Sie wurde bereits einmal als real beurteilt, keine tiefe Pass kann sie zum Schweigen bringen.
Und die Widerlegungen registrieren sich von selbst, von thot audit --deep ebenso wie von /audit deep: zwei Modellaufrufe, einmal bezahlt. Sie tragen den Namen der Engine, die entschieden hat, niemals deinen — eine Maschinenentscheidung geht einer menschlichen Entscheidung nicht vor.
Nichts wird jemals stillschweigend gelöscht. Ein verworfenes Finding bleibt im Bericht als refuted, mit seiner Begründung und seinem Autor — ein Audit, das verbirgt, was ihm zu ignorieren gesagt wurde, ist nicht wieder lesbar.
Der Python-Kern
Die Leitidee von Prime Agent, übertragen: Statt eines Tool-Aufrufs pro Frage schreibt das Modell Python, und seine Variablen überleben.
› /py bas = audit(severity="low"); print(len(bas), "findings"); [f.rule for f in bas]
3 findings
→ ['sink.eval', 'sink.network', 'sink.subprocess.shell']
› /py len(files())
→ 148Die Karte des Repos ist dort als Objekte verfügbar — files(), symbols(), find(), callers(), callees(), audit(), read(). Eine Schleife, die Findings und Aufrufer kreuzt, kostet eine Modellrunde; dasselbe in Tool-Aufrufen kostet ein Dutzend, wobei jeder erneut das liest, was die Karte bereits wusste.
Der Kern läuft niemals im Prozess von Thot. Ein exec() bei sich selbst würde dem auditierten Code den Speicher von Thot, seine offenen Datenbanken und seine Dateideskriptoren geben. Es ist also ein Unterprozess — und im Container, wenn eine Sandbox konfiguriert ist.
Was das genau schützt — und Thot hat es an sich selbst korrigiert, nachdem seine eigene gegnerische Pass auf eine zu absolute Docstring hingewiesen hat:
Unterprozess ( | schützt den Speicher, die Datenbanken und die Deskriptoren von Thot. Nicht deine Anmeldedaten: Der Worker läuft unter deinem Konto und kann |
Container ( | echte Grenze: kein Netzwerk, nicht dein |
Sensible Umgebungsvariablen werden vor dem Start entfernt, und /py sagt es einmal im lokalen Modus, statt „separater Prozess“ als eine Garantie lesen zu lassen, die es nicht bietet.
rlm() — aus einer Zelle delegieren
verdicts = {f.id: rlm(f"Ce chemin est-il exploitable ?\n{f.failure_scenario}")
for f in audit(severity="high")}Eine Zelle kann ihr eigenes Problem zerlegen. Die Zelle besitzt keine Anmeldedaten: Sie fragt den Host, der entscheidet und bezahlt. Die Grenzen werden also hostseitig gehalten — 8 Aufrufe pro Zelle, 40 pro Kern — weil eine Grenze, die das Kind ändern könnte, keine Grenze ist, und das Kind führt Code aus, der aus dem auditierten Repo stammt.
Was Thot von einem Repo behält
› /harness note team.shell.run : échappe ses arguments, les findings dessus sont faux
✓ Retenu — rappelé à chaque session.Die Verfeinerung von Prime, auf das Audit angewendet: Fakten, die keine statische Analyse jemals ableiten wird. Sie leben in <repo>/.thot/harness.json, werden bei Pull Requests wie die Urteile erneut gelesen und kommen in jeder Sitzung ins Briefing zurück.
Was das Modell tun darf
thot --tools lecture # lire et raisonner, jamais modifier
thot --tools carte # la carte seule : aucun fichier ouvertIn der Sitzung: /tools lesen. Ein Repo, das nicht deines ist, erneut zu lesen, heißt Code zu lesen, vor dem du allen Grund hast, dich zu hüten — und dass das Modell ihn ändert, ist selten das, was du wolltest.
Die Haltung hält an drei Stellen, nicht einer: die dem Modell angebotenen Tools, der Moment, in dem es trotzdem eines aufruft, und — im Konto-Modus — die offizielle CLI, der --disallowed-tools Write, Edit und Bash verbietet. Eine Haltung, die nur die Tools von Thot filtern würde, wäre eine Lüge dort, wo es am meisten zählt.
Die Lieferkette
thot deps # les dépendances épinglées, contre OSV.dev
thot deps --list # ce qui a été trouvé, sans réseau
thot deps --fail-on high # code 1 en CI
thot audit . --deps # dans le rapport d'audit
thot mcp check # tes serveurs MCP sont-ils malveillants ?Die Sperren zuerst, immer: uv.lock, poetry.lock, Pipfile.lock, package-lock.json, yarn.lock, pnpm-lock.yaml. Ein Manifest sagt requests>=2 und OSV kann nicht auf ein Intervall antworten; eine Sperre sagt 2.31.0 und OSV kann. Eine Abhängigkeit, die nur als Intervall existiert, wird nicht erraten, sie wird als nicht gepinnt gemeldet.
Ein Befund, der eine exakte Version abdeckt, ist eine Tatsache, keine Vermutung – aber
ob dein Code die verwundbare Funktion erreicht, wird nicht analysiert, daher
bleiben diese Findings PLAUSIBLE und sagen das auch. Nur MAL-* macht eine
Ausnahme: Das Paket ist die Nutzlast, die Erreichbarkeit ist nicht die Frage.
Und dieselbe Eigenschaft wie überall sonst: Die Identität eines Findings trägt die
eingefrorene Version, daher lässt ein bump das Urteil verfallen. Eine CVE auf
requests==2.19.1 auszuschließen, schließt nichts auf 2.20.0 aus.
Ein nicht erreichbarer OSV wird nie zu einem Gesundheitszeugnis: thot deps
sagt „nicht verifiziert" und liefert einen Fehlercode.
Den auditierten Code ausführen, ohne ihn bei dir auszuführen
pytest auf einem auditierten Repository bedeutet, dass der Code dieses Repositories
unter deinem Konto läuft. Das ist die einzige Stelle, an der das gesamte Design
durchsickert.
thot sandbox status
thot sandbox use docker
thot sandbox show pytest -q # la commande docker exacte, à relireStandardmäßig im Container:
Netzwerk | getrennt ( |
Repository | schreibgeschützt gemountet, beschreibbare Kopie in tmpfs |
Privilegien |
|
Grenzen |
|
Das getrennte Netzwerk ist das Flag, das am meisten wert ist und am meisten stört:
Deshalb ist es ein Flag und kein Gesetz (--network).
Eine Regel kehrt die des restlichen Thot um: Überall sonst kostet eine fehlende Abhängigkeit ihre Funktionalität und die Arbeit geht weiter. Hier verweigert ein angeforderter und nicht verfügbarer Sandkasten die Ausführung. Still auf den Host zurückzufallen, würde aus einem Schutz eine Lüge machen.
Entscheidungen teilen
Ein Urteil ist eine Tatsache über diese Revision dieses Codes. Es reist also mit
dem Code: <dépôt>/.thot/verdicts.json, wird in der Pull Request mitgelesen, die
den betroffenen Code berührt, und ist in einem frischen Klon schon vor dem Netzwerk
vorhanden.
thot verdicts --share <id> # publier une décision locale dans le dépôt
thot verdicts --share-all # toutes celles qui concernent ce dépôt
thot verdicts --where # d'où viennent les décisions, où elles s'écriventDer Speicher ist den Bäumen gemeinsam, die Datei nicht: Eine Entscheidung zu
veröffentlichen, die ein anderes Repository betrifft, wird verweigert, und
--share-all nimmt nur diejenigen, deren Datei hier existiert.
Die Standardkette ohne jede Konfiguration: zuerst das Repository, dann deine Maschine. Eine überprüfte Entscheidung hat Vorrang vor einer Notiz, die du dir selbst gemacht hast.
Das Schreiben bleibt lokal. Ein Werkzeug, das bei jedem /verdict eine versionierte
Datei verändern würde, erzeugte Diffs, die niemand verlangt hat: Du entscheidest
lokal, du veröffentlichst absichtlich.
Ein gemeinsamer Server oder ein bestehendes mem0
// ~/.thot/memory.json
{"remote": {"kind": "http", "base_url": "https://audit.equipe.example", "token": "…"}}
{"remote": {"kind": "mem0", "host": "http://localhost:8888", "api_key": "…"}}Das mem0-Backend spricht den selbst gehosteten Vertrag genau wie der Client von
Hermes Agent: Ein bereits für Hermes vorhandener Server dient Thot, ohne dass sich
etwas ändert.
Ein nicht erreichbarer entfernter Speicher kostet die Erinnerung an vergangene
Entscheidungen, niemals den Audit – aber er tut es nicht still: thot verdicts --where sagt, welcher stumm ist und warum.
Audits anderswo empfangen
Ein Audit, der um 03:00 Uhr endet, ist nichts wert, solange niemand benachrichtigt wird, und die zu benachrichtigende Person steht nicht vor dem Terminal.
thot gateway add ntfy topic=thot-$(openssl rand -hex 8) # le sujet EST le secret
thot gateway add telegram token=… chat_id=…
thot gateway allow telegram <ton-id> # obligatoire pour commander
thot gateway test
thot serve # écouter les commandesKanal | Ausgehend | Eingehend |
Telegram | ✓ | ✓ (Long Polling – kein Port zu öffnen) |
Discord · Slack | ✓ (Webhook) | — |
ntfy | ✓ | — (keine Identität: der Betreff genügt zum Veröffentlichen) |
✓ (SMTP) | — |
Die Benachrichtigungen benötigen keinen Daemon: Das Plugin gateway-notify
löst bei post_audit aus, und nur für einen nicht überwachten Audit.
Ein manuell gestarteter Audit erscheint bereits auf dem Bildschirm; jedes Mal zu
benachrichtigen, lehrt den Empfänger, den Kanal abzuschalten, was die einzige
Nachricht kostet, die zählte. Nichts Neues: Stille.
Was ein gestohlener Token ermöglicht
Der Daemon existiert nur für den Rückkanal, und sein Design hängt vor allem daran:
die Befehlssatz ist geschlossen –
status,audit,findings,verdict,help. Keine Shell, kein Schreiben, kein beliebiger Pfad;ein Audit kann nur ein bereits deklariertes Repository mit
thot schedule addanvisieren;der Eingang verlangt eine Zulassungsliste. Hermes bietet ein
ALLOW_ALL_USERSfür die Entwicklung an; Thot hat kein Äquivalent. Ohne Liste ist der Kanal nur ausgehend, undthot servesagt das.
~/.thot/gateway.json wird mit 0600 geschrieben – es enthält Bot-Token und
ein SMTP-Passwort. Die Umgebungsvariablen überschreiben es Feld für Feld,
unter den Namen von Hermes.
Geplante Audits
thot schedule add nuit ~/mon-projet --every daily --threshold high
thot schedule list
thot schedule run nuit # ce que le planificateur appelle
thot schedule remove nuitThot schreibt die launchd-Einheit (macOS) oder gibt dir die Crontab-Zeile und
überlässt dir die Aktivierung selbst – ein Werkzeug, das Hintergrundaufgaben still
installiert, ist ein Werkzeug, dem man nicht mehr glaubt.
Wenn launchd nicht kann. Auf macOS werden Berechtigungen pro Binärdatei
vergeben: Ein launchd-Agent kann den Zugriff auf ~/Desktop,
~/Documents oder ~/Downloads verweigert bekommen, und dann blockiert die
Einheit beim Start des Interpreters, ohne eine Zeile zu schreiben. Ein Prozess,
der aus deiner Sitzung gestartet wurde, behält den Zugriff dieser Sitzung, selbst
wenn er verwaist – das ist das dritte Mittel, und es verlangt nichts vom System:
thot schedule start # un planificateur dans ta session
thot schedule status # tourne-t-il, et quand est-il passé
thot schedule stop
thot schedule autostart # le relever au premier terminal après un redémarrageEr tritt vor jeder launchd-Einheit zurück, die launchctl als tatsächlich
ausgeführt deklariert: Zwei Planer für denselben Job bedeuten doppelte Arbeit und
doppelte Token. thot doctor sagt, welcher der beiden dient.
Ein geplanter Audit sagt nichts, solange nichts neu ist. Ein nächtlicher Bericht, der dieselben dreihundert Findings wiederholt, landet in einem Ordner, den niemand öffnet. Was nach oben kommt, ist der Diff: was seit dem letzten Mal aufgetaucht ist, über der Schwelle, abzüglich dessen, was bereits als uninteressant beurteilt wurde.
Plugins
Fünf Hooks, jeder, weil etwas Geliefertes ihn nutzt:
Hook | Wann |
| vor dem Bericht, zum Annotieren |
| Audit beendet – benachrichtigen, exportieren, archivieren |
| vor einem Schreiben des Agenten – gibt eine Warnung zurück |
| nach einem erfolgreichen Schreiben |
| eine Entscheidung wurde gerade gespeichert |
Ein Plugin ist ein Ordner mit plugin.yaml und __init__.py, in
~/.thot/plugins/ oder <repo>/.thot/plugins/ – die von Hermes Agent
verwendete Form. Ein Plugin, das abstürzt, kostet seine eigene Funktionalität und
sonst nichts: Sein Fehler wird protokolliert und von /plugins angezeigt.
Die des auditierten Repositories werden ohne deine Zustimmung nicht ausgeführt. Ein Plugin zu laden bedeutet, seinen Code hier unter deinem Konto auszuführen – und das auditierte Repository ist genau das, vor dem Thot sich hütet. Seine Plugins werden daher benannt, nie importiert, solange du sie nicht genehmigt hast:
thot plugins list <dépôt> # chargés, et refusés avec la raison
thot plugins trust <dépôt>/.thot/plugins/x # après l'avoir lu
thot plugins untrust <dépôt>/.thot/plugins/xDie Genehmigung betrifft den Inhalt, nicht den Namen: Thot speichert einen Fingerabdruck des Ordners, und jede Änderung widerruft ihn, indem sie es sagt.
Drei werden mitgeliefert:
Plugin | Was es tut |
| liest erneut, was das Modell schreibt, und meldet eine Warnung, wenn ein gefährliches Muster auftaucht. Nicht blockierend – ein falscher Positiv, der eine Sitzung blockiert, ist schlimmer als das Schreiben. |
| ein als |
| ein lokales JSONL-Journal jedes Audits, Urteils und Schreibens, in |
Prüfen, dass alles da ist
„Es funktioniert" ist eine Behauptung, und bei einem Programm aus drei Programmen ist das keine Behauptung, die man beim Wort nehmen sollte – besonders wenn sie vom Werkzeug selbst kommt.
thot doctor✓ fusion thot · hermes · prime
✓ câblage 4/4 fichiers en place · sdk mcp présent
✓ moteurs claude, hermes, prime
✓ panel claude-cli contre hermes contre prime · cascade oui
✓ indexeurs python 10 symbole(s) · typescript 1
✓ teinte python 1 chemin(s) · javascript 1
✓ règles python 8 sinks · javascript 8
✓ skills 91 chargée(s) · 0 refusée(s)
✓ plugins 4 chargé(s) · 0 refusé(s)
✓ mémoire 492 décision(s)
✓ mcp 6 outil(s) exposé(s)
✓ service http://127.0.0.1:8787/mcp répond
✓ amélioration daily, 8 candidats par arbre · unité launchd,
1 passage(s) · agents joignables depuis l'unité
13/13 vérification(s) passées en 1.83 sDatterter Schnappschuss, kein Vertrag: mémoire zählt die angesammelten Urteile
und kann nur wachsen, skills hängt davon ab, was auf der Maschine installiert
ist. Stabil ist die Form – jede Zeile trägt eine gemessene Zahl, und 12/12.
Die letzte Zeile ist die tatsächliche Ausgabe auf der Entwicklungsmaschine, und
sie wird so belassen: Das ist es, was die Kontrolle erzeugen soll. Das
1 passage(s) kommt von launchctl selbst, und es ist nicht dekorativ: Ein
launchctl list zeigt die geladene Einheit, ihr LastExitStatus ist 0 und ihr
Journal existiert nicht – drei Signale, die „alles gut" sagen für eine Aufgabe,
die nie gestartet ist. Die Ursache zu benennen ist besser, als grüne Zeilen zu
zählen.
Diese Prüfung musste selbst korrigiert werden. Sie verurteilte die Aufgabe wegen
der Form eines Pfads – „der Baum liegt unter ~/Desktop, also wird launchd
ihn nicht lesen können" – während macOS-Berechtigungen pro Binärdatei vergeben
werden: Der Interpreter der Einheit las den Baum, den /bin/sh verweigert
bekam. Eine Form ist ein Verdacht, ein Durchlauf ist eine Tatsache, und genau
die fragt sie nun ab.
Und eine Prüfung, die keine statische Inspektion hätte machen können:
thot doctor --agents # un appel modèle par agent installé✓ lecture · claude lit un fichier par chemin absolu
✓ écriture · claude n'a pas écrit cette fois
✓ outils · claude 10 outil(s), tous en lecture seule
✓ lecture · hermes lit un fichier par chemin absolu
✓ écriture · hermes peut écrire — aucun mode lecture seule
(`-t file` et `--safe-mode` ne restreignent pas les permissions)
✓ outils · hermes mcp__patch, mcp__read_file, mcp__search_files, mcp__write_file
✓ lecture · prime lit un fichier par chemin absolu
✓ écriture · prime peut écrire — outil unique : un noyau IPython
✓ outils · prime ipythonDie Schreibzeilen sind grün, obwohl sie eine unbequeme Fähigkeit ankündigen: Sie
berichten, was ist, nicht, was man sich wünscht. Zwei der drei Agenten können
schreiben, und kein Flag verhindert es – gemessen, indem man sie auffordert, eine
Datei zu erstellen, und dann auf die Festplatte schaut. Was nicht verhindert
werden kann, wird unmöglich zu übersehen gemacht: AuditResult.touched benennt,
was ein Durchlauf verändert hat, und die nächtliche Schleife schreit es auf
stderr.
Sie legt eine Datei in einem temporären Ordner ab und fragt ihren Inhalt ab. Sie existiert wegen eines realen Fehlers: Hermes öffnete keinen Pfad relativ zu seinem Arbeitsordner und antwortete mit einem Satz, der wie eine Ablehnung klang. Ein Drittel des Panels konnte keine Behauptung verifizieren, die auf einer zweiten Datei beruhte, und nichts anderes als das Ablegen einer Datei hätte es gezeigt.
Jede Zeile führt eine echte Operation aus und berichtet, was sie gemessen hat:
nicht „skills: konfiguriert", sondern „91 geladen, 0 abgelehnt". Die
Farb-Engine sucht einen Pfad in einer Stichprobe beider Sprachen, der MCP-Server
antwortet auf sein eigenes Protokoll. Eine Prüfung, die nicht laufen kann,
schlägt fehl, statt still zu bestehen: Eine grüne Zeile, die „nicht getestet"
bedeutet, ist schlimmer als eine rote. Nichts berührt das Netzwerk oder ein
Modell – thot doctor in einem Flugzeug gibt dieselbe Antwort wie in einem Büro.
Ausgabe ungleich null bei Fehlschlag, um in ein && oder eine CI zu passen.
Die permanente Verbesserung
Ein Audit, der zwanzig Kandidaten argumentiert und aufhört, lässt den Rest für
immer ohne Urteil. Ein Durchlauf ohne Budget läuft noch, wenn du dich wieder
hinsetzt. thot improve ist das Dazwischen: begrenzte Runden, jede auf die
Festplatte geschrieben, jede dort weitermachend, wo die vorherige aufgehört hat.
thot improve # un tour sur les trois arbres
thot improve --rounds 5 # jusqu'à ce qu'un tour ne juge plus rien
thot improve --every daily # la boucle devient permanenteDie geschriebene Einheit trägt ihr eigenes PATH. launchd gibt einem Job
/usr/bin:/bin:/usr/sbin:/sbin, cron noch weniger, und claude, hermes und
node sind in keinem dieser Ordner – sie leben unter ~/.local/bin.
Ohne das baute der nächtliche Durchlauf keine Engine, beurteilte nichts und
endete mit Code 0: launchd verzeichnete jede Nacht einen Erfolg, auf
unbestimmte Zeit. Ein solcher Job, der still scheitert, ist von einem
funktionierenden Job nicht zu unterscheiden, daher endet ein tiefer Durchlauf
ohne Agenten nun mit einem Fehler und sagt es.
Die Nachtversion meldet, was sie entschieden hat, nicht, was aufgetaucht ist. Der Unterschied zählt: Der Melde-Mechanismus geplanter Audits antwortet auf „Was gibt es Neues über der Schwelle", was die richtige Frage für einen Scan und die falsche für ein Urteil ist. Ein bereits im Bericht vorhandenes MEDIUM zu bestätigen, ist genau das, wofür die Schleife da ist — und es wäre niemandem gemeldet worden. Die Dateien, die ein Audit verändert hätte, werden im selben Journal vermerkt.
Ein Baum, der nichts mehr zu beurteilen hat, gibt seinen Anteil an den nächsten weiter. Gemessen am
realen Korpus: thot hat einen leeren Rückstand und prime nur einen einzigen Kandidaten, also
gab ein Budget von 20 pro Baum 40 für Bäume aus, die sie nicht nutzen konnten, während Hermes auf einhundertfünfzig wartete. Eine Runde von 20
wird zu einer Runde von 20, 20 und 60.
Eine dritte lässt sie schnell konvergieren: Fehlschläge werden gezählt. Ein Kandidat, dessen Agent sein Zeitlimit überschreitet oder dessen Modell sich weigert, sich festzulegen, behält seine Schwere — wird also zuerst in der nächsten Runde aufgegriffen, und in der übernächsten. Gemessen an einem Finding in einer Datei mit 1.660 Zeilen: vier Versuche bei drei Durchgängen, drei davon zahlen gegen dieselbe Wand. Nach zwei Fehlschlägen wandert er ans Ende der Warteschlange: weiterhin förderfähig, nie priorisiert. Ein Erfolg löscht den Zähler — eine Wand, die ein voller Nachmittag oder ein aufgebrauchtes Abonnement war, soll einem Finding nicht für immer folgen.
Zwei Eigenschaften lassen sie konvergieren, statt im Kreis zu laufen: Eine Widerlegung wird gespeichert, die nächste Auswahl überspringt sie also; eine Bestätigung wird bewusst nicht gespeichert — ein echter Defekt muss weiterhin erscheinen, bis jemand ihn behebt — die Schleife führt also ihren eigenen Satz bereits beurteilter Kennungen. Ohne das würde jede Runde nach der ersten ihr ganzes Budget darauf verwenden, das erneut zu argumentieren, was die erste gerade bestätigt hatte.
Sie endet mit dem, was zu tun ist, vor den Summen:
À REGARDER — 2 finding(s) :
[hermes] plugins/platforms/a2a/tools.py:83 — confirmé · prime
L'URL vient d'un argument d'outil, donc du modèle…
[prime] packages/coding-agent/…/state-snapshot.ts:163 — réfutation contestée · hermes
Le chemin dit fixe est construit depuis un identifiant non validé…
4 tour(s) · 83 jugement(s) (80 réfuté · 1 confirmé) · 157 candidat(s) sans décisionEine Widerlegung ist Haushalt; eine Bestätigung ist eine Neuigkeit. Eine angefochtene Widerlegung auch: Es ist das Programm, das sagt, es habe sich gefangen, bevor es etwas begräbt. Sie zu zählen, ohne sie zu benennen, schickt den Leser ins Journal greppen — was genau das ist, was jedes Mal passiert ist, während eines Tages.
Sie ändert nie Code. „Verbesserung" bedeutet hier, dass das Urteil des Programms über sich selbst klarer und billiger wird: weniger Kandidaten ohne Entscheidung, mehr Entscheidungen auf der Platte, jede dem Agenten zuschreibbar, der sie getroffen hat.
Das Thermometer und die Schleife, die es nutzt
Alles Vorhergehende misst Thot mit Thot. improve fragt ein Modell, ob ein
Finding real ist. evolve überwachte provenance, einen Bericht, den die Engine
über ihre eigene Ausgabe berechnet. Beide sind zirkulär, und der Kreis ist nichts
Akademisches: Der tiefe Durchgang hat 638 Urteile für 9 Bestätigungen bezahlt,
während eine mit −100 % bewertete Regel schlief — xml_unsafe_parse meldete
defusedxml, also genau das Gegenmittel, das seine eigene Meldung
empfahl. Nichts im Programm konnte das sehen.
thot bench durchbricht den Kreis. Es misst Thot gegen Code, der von jemand anderem als
vulnerabel oder sauber gekennzeichnet ist, zu gleichen Teilen, mit benannter
Schwachstellenklasse.
thot bench ~/.thot/bench # les suites présentes, catégorie par catégorie
thot bench ~/.thot/bench --json # ce que la boucle d'évolution lit
thot bench ~/.thot/bench --floor info # ce que le plancher de sévérité cacheDer Korpus ist nicht eingebettet — achtzehntausend Drittanbieterdateien haben in diesem Repository nichts zu suchen — der Pfad ist also immer angegeben, und jede Suite wird gegen den Fingerabdruck ihres Manifests geprüft. Ein Korpus, dessen Beschriftungen sich unter einer Messung bewegt haben, ist schlimmer als kein Korpus: Alle Zahlen seitdem sind falsch und nichts sagt es.
Die Note ist das J von Youden, TPR − FPR. Null ist ein Münzwurf,
+100 ist perfekt, und negativ bedeutet, dass die Regel invertiert ist. Präzision
und Recall hätten es nicht gesagt: Eine Regel ohne einen einzigen echten Positivfall hat eine
undefinierte Präzision, wird leer angezeigt und liest sich wie keine Daten — genau
so überlebt eine invertierte Regel. Das J hat dieses Loch nicht.
Zwei Arten zu betrügen, und beide verlieren. Weniger zu finden — was
provenance steigen ließ — lässt den TPR fallen. Alles zu melden ergibt TPR 100 %, FPR
100 %, J null: Der Korpus ist genau dafür 50/50 ausgewogen.
Gemessener Zustand, Standard-Mindestwert, drei Frameworks (django, fastapi, flask):
avant après
TPR 9.9 % 34.4 %
FPR 0.5 % 0.0 %
J de Youden +9.4 % +34.4 %
catégories actives 10 24
catégories négatives 0 0„Vorher" ist der Zustand des Programms in dem Moment, als das Thermometer zum ersten Mal existierte. „Nachher" ist derselbe Korpus, derselbe Mindestwert, derselbe Befehl. Das Hold-out bestätigt es: flask, ferngehalten, notiert +33,3 %, django, ferngehalten, +35,9 % — weniger als zwei Punkte vom Training, die Regeln funktionieren also auf Code, zu dessen Schreiben sie nicht beigetragen haben.
Nach Kategorie, was die Engine heute kann:
xxe · tlsverify · weakhash · weakrand · weakcipher · weakkeylength
hardcodedcreds · default_credentials · cleartexttransmit · errormessage
debug_code_production · cookie_no_httponly · cookie_no_samesite
securecookie · directory_listing_exposure +100,0 %
deserial +98,0 % cmdi +93,3 % codeinj +86,0 %
eval_injection +84,0 % sqli +82,7 % cloud_ssrf_metadata +78,7 %
ssrf +64,7 % pathtraver +56,0 % xss / basic_xss +26,7 %Null falsch positive über alle 18.300 Fälle, und keine negative Kategorie.
Der Ausgangspunkt war ssrf bei −8,0 % und xxe bei −100 %.
Was diese fünfundzwanzig Punkte hervorgebracht hat, in der Reihenfolge, in der es gemessen wurde:
Änderung | J |
Start | +9,4 % |
Web-Routen als Einstiegspunkte erkannt | +9,6 % |
Der Farbton folgt einem Wert in einem Container | +10,0 % |
Ein Ternär mit konstanten Zweigen transportiert nichts | +12,4 % |
Ein | inkl. |
SSRF-Wächter: Host-Whitelist, aufgelöster IP-Bereich | +14,3 % |
| +14,9 % |
Pfadeingrenzung und benannte Whitelist | +15,5 % |
| +16,3 % |
Zwölf einzeilige Musterregeln | +27,8 % |
Ein Muster zahlt den Barrierefreiheitsrabatt nicht mehr | +34,4 % |
Drei dieser Änderungen wurden nach der Messung abgelehnt, und das
Thermometer hat sie abgelehnt: Eine Datenbanklesung als unzuverlässige Quelle zu behandeln
(436 vulnerable Fälle, 343 saubere — so viel Rauschen wie Signal),
clickjacking (23 falsch positive in diesem Repository), und die fünf Regeln
für LDAP/XPath/NoSQL/SSTI/EL-Injektion, deren gemessenes J genau
0,000 ist: Sie ziehen genauso stark auf die gesunde Hälfte wie auf die andere.
Präzision kauft man nicht mit einem toten Winkel
Jeder oben erkannte Wächter wurde zuerst in einer Version vorgeschlagen, die den Score erhöhte und ein ausnutzbares Loch öffnete. Eine adversariale Sonde hat sie alle gefunden:
resolved = socket.gethostbyname(parsed.hostname or url)
if ipaddress.ip_address(resolved).is_private:
return "blocked", 403
os.system("curl -s " + url) # ← silencieux, et exploitableDer Wächter ist eine korrekte SSRF-Verteidigung und sagt nichts über Shell-Metazeichen, die noch in der Zeichenkette sind. Den Farbton überall zu bereinigen kaufte 174 falsch positive weniger und einen toten Winkel bei der Befehlsinjektion.
Daher die Unterscheidung, die die Engine trägt: Ein Wächter, der den Wert einschränkt
(literale Whitelist, fullmatch mit Aufzählung) weißt ihn für alles auf; ein Wächter, der das Ziel beweist
(Der Host ist erlaubt, die aufgelöste Adresse ist öffentlich, der Pfad ist eingegrenzt) gilt nur für die
betroffene Familie von Sinks. Dasselbe für bleach.clean, das HTML
neutralisiert und x; rm -rf / intakt lässt.
Vier adversariale Sonden sind in der Testsuite und melden alle korrekt. Eine zukünftige „Verbesserung", die eines dieser Löcher wieder öffnen würde, bricht einen benannten Test.
Das verbleibende Schweigen hat zwei Ursachen, und thot bench trennt sie, weil es
zwei verschiedene Arbeiten sind:
règle muette — elle existe et ne matche jamais : elle a un motif à élargir
aucune règle pour la classe : il y en a une à écrireDie Unterscheidung entscheidet über die Reihenfolge der Ziele — ohne sie sind die stummen Kategorien bei J = 0 exakt gleichauf und die Sortierung fällt auf die alphabetische Reihenfolge zurück.
Die Fusion, dort wo sie etwas ändert
Cascade.turn wählt einen Agenten aus und ruft ihn; er holt den anderen nur,
wenn der erste einen Fehler zurückgibt. Eine solche Runde ist konstruktionsbedingt auf das
Beste der beiden gedeckelt: Sie kann weniger verlieren, nie mehr gewinnen.
agent_apply machte dasselbe — eine Engine, im Singular.
thot evolve --fused lässt beide arbeiten, an verschiedenen Hälften desselben
Problems:
thot bench ~/.thot/bench # où ça fait mal
thot evolve --from-bench --fused \
--corpus ~/.thot/bench --hold-out flask # et on répare, en boucleHermes liest die Messung und schreibt eine Spezifikation. Er fasst keine Datei an. Seine Ausgabe ist eine Aussage über die Ursache: welche Regel, welche Zeile, warum diese Fälle.
Prime liest die Spezifikation und schreibt den Code. Ihm wird explizit gesagt, dass er sie ablehnen kann, wenn der Code ihr widerspricht — ein Ausführender, der nicht Nein sagen kann, ist ein Relais, und ein Relais fügt nichts hinzu.
Keiner der beiden entscheidet. Die Testsuite ist ein Mindestwert, der beschriftete Korpus ist das Urteil. Eine falsche Spezifikation, mit Überzeugung angewendet, lässt die Note fallen und wird Byte für Byte rückgängig gemacht.
Die Reihenfolge ist auch nicht willkürlich. Entwerfen-dann-bauen verifiziert sich an der Nahtstelle: Prime sieht die Argumentation von Hermes, bevor er sich darauf einlässt. Bauen-dann-neulesen erlaubt das nicht — wenn der zweite hinschaut, hat der erste bereits entschieden.
Die Ziele kommen aus der Messung, nicht aus einem getippten Satz. Bisher konnte die Schleife nur verfolgen, was ein Mensch bereits vermutete; ein aus dem Score konstruiertes Ziel ist das Programm, das sagt, wo es schwach ist, in Zahlen, die es nicht gewählt hat — und dieselben Zahlen sagen anschließend, ob die Antwort genutzt hat. Jedes Ziel trägt die Dateien, die fehlgeschlagen sind: Ein Agent, dem gesagt wird „xss ist bei 0 %", kann nur raten, ein Agent, dem drei fehlgeschlagene Dateien gegeben werden, hat ein Problem zu lösen.
Das Überlernen und was --hold-out wirklich kann
Eine Schleife, die auf einem Korpus benotet wird, hat nur eine echte Art zu betrügen: den Korpus
lernen. Eine Regel, die darauf kalibriert ist, wie BenchmarkTest01126 aussieht, lässt die
Note steigen und nützt niemandem, und von außen ist das ununterscheidbar
von echtem Fortschritt.
--hold-out flask nimmt eine Suite aus der Hauptzahl heraus und hält sie als
zweites Schutzgitter: Eine Änderung, die die Suiten bewegt, gegen die sie
optimiert wurde, und nicht die, die sie nie gesehen hat, hat gesagt, was sie ist. Die beiden
Zahlen werden darf_nicht_sinken gehalten.
Ihre Grenze, gemessen: Die drei Frameworks notieren innerhalb eines halben Punkts voneinander. Das fängt Überlernen an der Datei, nicht Überlernen an der Form des Benchmarks — ein generierter Korpus bleibt ein generierter Korpus, und eine Regel, die nur auf Demonstrationscode hilft, würde alle drei bestehen. Das Hold-out macht einen Betrug sichtbar; es macht den Korpus nicht repräsentativ.
Was die Schleife von einer Runde zur nächsten behält, steht in
~/.thot/evolve-log.jsonl. Ohne das, da sich die Messung in einer Runde kaum bewegt,
liest die nächste Runde dieselben schlechtesten Kategorien, reicht dieselben Dateien, und
erhält — vernünftigerweise — dieselbe bereits gebaute, gemessene und
annullierte Spezifikation: --rounds 5 wäre ein fünfmal versuchter Versuch, fünfmal teurer,
mit dem Anschein, beschäftigt zu sein.
Es ist kein Orakel. Ein Fix kann grün sein, das J steigen lassen und trotzdem schlecht bleiben — das ist Überlernen, und die Literatur zur automatischen Programmreparatur spricht nur davon. Der Korpus ist ein Beweis, dass man auf ihm Fortschritt gemacht hat. Die Schleife meldet, was sie geändert hat, damit ein Mensch anderer Meinung sein kann.
Skills — die Methoden, die Thot kennt
Eine Skill ist eine einmal geschriebene Methode: eine SKILL.md mit YAML-Frontmatter.
Es ist das Format von Hermes Agent und Prime Agent, eine für einen der beiden
geschriebene Skill lädt sich hier also ohne Änderung, und das Gegenteil
ist wahr.
Thot bringt die vollständige Bibliothek von Hermes Agent mit (MIT — siehe
NOTICE.md): 90 geladene Methoden, 117 weitere verfügbar.
thot skills list # les 91 chargées
thot skills search pentest # y compris la bibliothèque optionnelle
thot skills install ast-grep # activer une optionnelle
thot skills show plan # ce que lirait le modèleGeladene Kategorien: audit, security, software-development, github,
devops, research, mlops, productivity, creative, apple, email,
media, note-taking, smart-home, social-media,
autonomous-ai-agents.
Das Modell entdeckt sie mit dem Werkzeug skills — das mit einem Index von
Namen antwortet, solange man ihm kein Stichwort gibt, weil zweihundert
Beschreibungen kein Katalog sind — und liest die zutreffende mit skill. In
einer Sitzung zeigt dir /skills dasselbe.
Eine importierte Methode, die ein hier nicht vorhandenes Werkzeug zitiert
(delegate_task, browser_navigate…), wird unverändert ausgeliefert, mit
einer Notiz, welche fehlen und was stattdessen zu verwenden ist. Das Vorgehen
überträgt sich sogar dann, wenn sich der Werkzeugaufruf nicht überträgt.
Hinzufügen
~/.thot/skills/<nom>/SKILL.md # partout où tu travailles
<repo>/.thot/skills/<nom>/SKILL.md # versionné avec ce dépôt---
name: ma-méthode
description: Ce qu'elle fait et quand s'en servir.
---
# Ma méthode
Les étapes, dans l'ordre.Beide Anordnungen sind akzeptiert: ein flacher Ordner (Prime Agent) oder nach Kategorien gruppiert (Hermes Agent). Ein Name, der bereits existiert, ersetzt die eingebaute Version — geeignet, um eine mitgelieferte Methode anzupassen, ohne sie zu forken.
Eine vom geprüften Repository bereitgestellte Methode wird zuerst analysiert
Eine SKILL.md ist Text, der dem Modell als Anweisung übergeben wird. Die
Repositories, die Thot liest, sind per Definition die, für die niemand
verantwortlich ist. Ein feindliches Repository, das .thot/skills/x/SKILL.md
ablegt, würde einen Teil des Briefings schreiben.
Die Wache von Hermes Agent wird hierher übertragen und gilt für alles, was aus dem Repository kommt: Injection, Exfiltration, Persistenz, Obfuskation.
▲ 1 skill(s) fourni(s) par ce dépôt ont été refusés — ils seraient passés
au modèle comme instructions.
pwn curl vers l'extérieur ; accès à ~/.thot ; « ignore previous
instructions »thot skills scan <dossier> stellt dieselbe Frage auf Anfrage. Was Thot
selbst ausliefert, wird nicht analysiert: Es liegt auf der Platte, weil das
Programm installiert ist, nicht weil ein Repository es angefordert hat.
Benutzerdefinierte Befehle
Eine Markdown-Datei ist ein Befehl. Die Grammatik ist die von Prime Agent, Claude Code und Codex — nichts Neues zu lernen.
---
description: Relire un fichier sans rien modifier.
argument-hint: <chemin>
---
Relis $1 et dis-moi ce qui cloche. Ne modifie rien.In .thot/commands/revue.md erzeugt das /revue src/app.py. Substitutionen:
$1, $2…, $@, $ARGUMENTS, ${@:2}, ${@:2:3}. Ein Argument wird nie
neu interpretiert. Die Befehle des Repositories durchlaufen dieselbe Wache wie
seine Skills.
Drei werden mitgeliefert: /triage (den Eintrag benennen oder ohne Folge
einstufen), /harden (Test, der zuerst fehlschlägt, dann Korrektur),
/regress (das Audit-Diff gegen eine Git-Referenz).
MCP-Server
Der Katalog von Hermes Agent, zwanzig verifizierte Server:
thot mcp list # le catalogue, et ce qui est déjà connecté
thot mcp show sentry
thot mcp add linearDie Installation wird an das offizielle CLI delegiert, das bereits OAuth und die Token-Erneuerung besitzt — Thot hat keinen Grund, einen zweiten Tresor zu halten, der leaken könnte. Es sagt explizit, dass registriert nicht autorisiert ist, und welcher Befehl die Arbeit abschließt.
Die Werkzeuge des Modells
Die Klassiker — lesen, schreiben, bearbeiten, einen Befehl ausführen. Jedes Schreiben und jede Ausführung verlangt Bestätigung, und das ist nicht konfigurierbar.
Und vier, die nur Thot gehören, kostenlos, weil sie die Karte abfragen und nicht das Modell:
Werkzeug | Antwort |
| die Dateien des Projekts |
| Datei, Zeilen und Parameter einer Funktion |
| wer was aufruft, und die Distanz zu einem Einstiegspunkt |
| die Pfade der Färbung Quelle → Senke |
Im Konto-Modus werden diese vier dem offiziellen CLI über
thot.mcp_server bereitgestellt — ein MCP-Server im Nur-Lese-Modus, der
nichts schreiben oder ausführen kann.
Wenn das Modell sucht, wer process_payment aufruft, fragt es den Graphen ab
und erhält die vollständige Antwort — statt drei Dateien auf gut Glück zu
greppen.
Nur-Audit-Modus
Der Analyse-Kern lässt sich auch ohne Modell, ohne Netzwerk, ohne Kosten verwenden:
thot init /chemin/du/repo --owner "Ton Nom" # autorisation, une fois
thot audit /chemin/du/repo --paths # chemins de teinte complets
thot audit . --all # y compris le bruit faible
thot audit . --json --out rapport.json
thot audit . --out rapport.sarif # SARIF 2.1, format déduit du nom
thot audit . --fail-on high # code 1 en CISARIF — in eine Kette einsteigen, die bereits existiert
Ein Bericht, den keine Pipeline lesen kann, lebt in einem einzigen Terminal. GitHub code scanning, GitLab, Azure DevOps und die Editoren lesen alle SARIF 2.1, und zwei Eigenschaften von Thot sind dort mehr wert als anderswo.
Die Identität eines Findings ist Regel + Datei + Symbol + Fingerabdruck des
Rumpfs, niemals die Zeile — genau das verlangt partialFingerprints. Ein
Dashboard, das mit Zeilennummern gefüttert wird, öffnet alle seine Tickets
erneut, sobald jemand oben in einer Datei einen Import hinzufügt; mit diesem
gefüttert, nicht.
Und ein Färbungspfad ist eine Folge von Positionen, was codeFlows rendert:
Der Leser klickt von der Quelle zur Senke, statt dem Werkzeug aufs Wort zu
glauben. Ein Finding ohne Pfad trägt keinen codeFlows-Schlüssel — ein leerer
Fluss wird als Färbungspfad ohne Schritt gerendert, was wie eine kaputte
Analyse gelesen wird und nicht wie eine Musterübereinstimmung.
Ein Finding, das vom Panel widerlegt wurde, wird nicht gelöscht: Es geht mit
einer begründeten suppressions mit. Ein Dashboard, das es nie sieht, kann
nicht unterscheiden zwischen „niemand hat geschaut" und „jemand hat geschaut
und entschieden" — und das zweite ist der ganze Sinn des Panels.
- run: thot audit . --out thot.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: thot.sarif }Unterstützte Analyse — --deep
Die deterministische Analyse beantwortet „könnten diese Daten möglicherweise
fließen?". Sie ist erschöpfend, kostenlos, und das ist nicht die Frage, die
man einen Prüfer bezahlt zu entscheiden. --deep stellt die teure Frage, nur
an die Kandidaten, die sie verdient haben:
thot audit . --deep # 20 pires candidats, 4 en parallèle
thot audit . --deep --budget 50 # plus large
thot audit . --deep --parallel 8 # plus viteZwei Durchgänge, bewusst gegensätzlich:
Die Sonde muss einen konkreten Eingang benennen, der den gefährlichen Punkt erreicht. Keine Allgemeinheit über die Schwachstellenklasse — eine URL, ein Wert, eine Wirkung.
Die Widerlegung erhält dieses Szenario mit der einzigen Mission, es zu zerstören: vorgelagerte Validierung, Aufrufer, der nur Konstanten übergibt, Typ, der den angenommenen Eingang verbietet. Im Zweifel widerlegt sie.
Ein Finding überlebt nur, wenn eine zweite feindliche Lektüre desselben Codes
es nicht zu töten vermag. confirmed bedeutet dann etwas.
In einer Sitzung dasselbe: /audit deep.
Die Engine wird automatisch gewählt — dein Claude-Konto über das offizielle CLI, wenn es verbunden ist (die Analysen laufen parallel, auf deinem Abonnement), sonst ein API-Schlüssel.
Was das Audit nicht lesen darf
# .thotignore, à la racine du dépôt
vendor/
*.generated.py
tests/fixtures/Die eingebauten Ausschlüsse decken ab, was jedes Repository hat —
node_modules, build, .venv. .thotignore deckt ab, was nur dieses
Repository weiß: eingebettete Dokumentation, ein generierter Client, ein
Ordner mit absichtlich kaputten Fixtures. Sie zu auditieren erzeugt keine
Findings, es erzeugt Rauschen an genau der Stelle, wo die Findings wären.
Deine eigenen Regeln
Der eingebaute Katalog kennt die Standardbibliothek. Er kennt nicht den
Wrapper, den dein Team um subprocess geschrieben hat, die Warteschlange, die
dein Dienst konsumiert, noch den Validator, der bei dir einen Wert sicher
macht. Ohne einen Ort, wo man es sagt, irrt sich jedes Audit eines echten
Systems an denselben drei Stellen.
# <repo>/.thot/rules/team.yaml — versionné avec le code
# ~/.thot/rules/*.yaml — ce que tu sais, partout où tu travailles
sinks:
- id: sink.team.run_shell
patterns: [run_shell, shellutil.run_shell]
impact: critical
description: Wrapper shell interne (shell=True)
match_mode: bare # qualified | method | bare | prefix
sources:
- id: source.queue
patterns: [msg.payload]
description: File de messages
match_mode: prefix # couvre msg.payload.decode(...)
sanitizers: [validate_host, team.escape]Dieselbe Datei trägt die JavaScript-Regeln, unter einem Schlüssel js: —
ein Team-Wrapper existiert in der Regel in beiden Sprachen, und die Deklaration
zu trennen ist der Weg, auf dem eine Hälfte veraltet.
js:
sinks:
- id: sink.js.team
names: [runShell, sh] # comparés au dernier segment, ou qualifiés
impact: critical
description: Notre wrapper shell
needs: [child_process] # ne se déclenche que si le fichier l'importe
sources:
- id: source.js.queue
patterns: [job.payload]
description: File de messages
sanitizers: [escapeArg]Was das Modell anfordert, ist eine nicht vertrauenswürdige Eingabe
Die Quellen sind Ausdrücke — sys.argv, os.environ. Das deckt ein
Programm ab, das man startet, und verfehlt ein Programm, das man aufruft: Das
Werkzeug eines Agenten erhält seine nicht vertrauenswürdige Eingabe in
benannten Parametern, die von einem Register aus dem gefüllt werden, was
ein Modell angefordert hat, und kein Ausdruck erscheint irgendwo im Rumpf.
Die gemessenen Kosten, das nicht zu modellieren: vier SSRF an einem Nachmittag, alle über ein Werkzeugargument erreicht, keines von der Färbung gefunden — sie wurden von Musterregeln gefunden, die eine Form erkennen und nichts beweisen.
entry_sources:
- id: entry.tool
patterns: [tools.image_gen] # les fonctions qu'un registre appelle
parameters: [args] # facultatif : lesquels de leurs paramètres
description: Arguments remplis par le modèle
match_mode: prefixStandardmäßig leer, und bewusst: Welche Funktionen ein Register aufruft, ist
eine Tatsache über ein Repository, und es zu erraten würde eine Quelle unter
jeden Parameter jedes Programms legen. Gemessen an Hermes, die beiden Extreme:
eine Regel, die die Pakete plugins und tools benennt, offenbart 19
bewiesene Pfade, von denen mehrere über-approximiert sind (die base_url,
die ein Helper aus der Konfiguration erhält, ist nicht nicht vertrauenswürdig);
eine Regel, die den Parameter args benennt, offenbart null, weil die
Handler von Hermes benannte Parameter nehmen und kein Wörterbuch. Die richtige
Regel benennt die tatsächlichen Einstiegspunkte — und es ist an ihren Autoren,
sie zu kennen.
Eine Regel, die eine eingebaute id übernimmt, ersetzt sie — geeignet, um
eine Senke herabzustufen, die das Team bewusst akzeptiert hat, ohne Thot zu
patchen. Eine fehlerhaft formatierte Datei stoppt das Audit und benennt die
Datei und den fehlerhaften Schlüssel, statt ein Fehlen von Findings
anzunehmen.
Die Unterdrückungen
Eine Unterdrückung ist die einzige Sicherheitsaussage, die kein Werkzeug
erneut liest — auch dieses hier nicht, konstruktionsbedingt. # nosec,
# noqa: S310, // eslint-disable … security/…: Es ist eine Aussage über den
Code, einmal geschrieben, die die Aufrufer überlebt, die sie beschrieb.
Zweimal in demselben Audit, hier, war sie falsch:
Unterdrückung | was sie behauptete | was wahr war |
| das Schema ist kontrolliert | es stoppte |
| die URL kommt aus der Konfiguration | einer der Aufrufer liest sie aus einem Werkzeugargument, also vom Modell |
Thot meldet sie daher als Klasse, in LOW, mit dem Muster daneben
geschrieben. Das Finding sagt nicht „diese Zeile ist gefährlich": Es sagt
„niemand hat den Grund erneut gelesen, warum sie entschuldigt wurde". Bei
einem --deep-Durchgang ist es ein Agent, der prüft, ob das Muster noch
hält.
Für Python werden die echten Kommentar-Token gelesen, keine Muster — ein
regulärer Ausdruck unterscheidet # nosec in einem Kommentar nicht von
demselben Text, der in einer Docstring zitiert wird, und die Docstring dieses
Moduls zitiert zwei.
Eine Unterdrückung, die auf eine Zeile gesetzt wird, die dieses Audit meldet, ist nicht dasselbe Objekt: Es ist eine Aussage, die ein lebendes Finding widerspricht, geschrieben von jemandem, der dieselbe Zeile gelesen und anders geschlossen hat. Sie wird eine Stufe höher gemeldet und sagt es. Gemessen an Hermes: 7 von 45 — und drei der an jenem Tag gelesenen Unterdrückungen waren falsch.
Gemessen: 0 bei Thot, 0 bei Prime, 45 bei Hermes.
Kalibrierung
Präzision zählt so viel wie Erkennung. Bewusst nicht gemeldet werden:
subprocess.run(cmd)ohneshell=True— keine Shell liest den Befehl.cursor.execute("… ?", params)— wörtliche Abfrage, gebundene Parameter.Ein Wert, der durch
int(),shlex.quote(),os.path.basename(),html.escape()geht — diese Aufrufe brechen die Kontaminationskette.Ein Defekt, den kein Einstiegspunkt erreicht, wird automatisch herabgestuft — aber nur, wenn Einstiegspunkte gefunden wurden. Ohne einen ist der Umfang unbekannt, nicht null, und nichts wird über diese Unwissenheit begraben.
payload.get(...)ist nichtrequests.get(...).
Größenordnung, gemessen an Hermes Agent (4 457 Python-Dateien): 98 s, 365 Findings, davon 25 high — 3 über dem Standard-Schwellenwert, sobald die Erinnerung angewendet wird.
Welchen Parameter das Argument füllt
Ein Helper, dessen ein Parameter eine Senke erreicht, macht nicht alles gefährlich, was man ihm übergibt. Die Engine paarte den Aufrufer jedoch mit der Gesamtheit der Senken des Callee, ohne zu sehen, wo das Argument landete:
def helper(safe, cmd):
os.system(cmd) # seul `cmd` atteint le shell
helper(sys.argv[1], "ls") # la donnée va dans `safe` — et c'était rapportéDie Position wird nun am Aufrufort gelesen, und der Name für ein benanntes Argument. Das tat die JavaScript-Engine auf ihrer Seite bereits.
Gemessen, sobald die Ausführung möglich war: Von 83 238 angefragten
Auflösungen bei der Analyse von Thot und hermes/tools bezeichnen 90 % einen
einzigen Parameter und 9,8 % keinen; 0,1 % bleiben offen (f(*rest),
f(**options)) und behalten die breite Antwort. Der Callee bot im Durchschnitt
2,68 Parameter, also verschwinden fast zwei Drittel des Suchraums — die
Analyse von hermes/tools sinkt von 11,4 s auf 9,5 s.
Was die Anzahl der Findings betrifft, allerdings: keinerlei Änderung. Thot, prime und hermes/tools liefern exakt dieselben 151 Kandidaten vor und nachher, davon null erfundene – die Sicherheitseigenschaft hält in der Praxis. Vier Tönungspfade werden kürzer, mehr nicht. Die oben korrigierte Form ist real und der Test beweist es, aber sie tritt in keinem der drei gemessenen Bäume auf: Dieser Fix kauft Geschwindigkeit und korrektere Pfade, nicht weniger Rauschen.
Zwei Formen sagen nichts aus: helper(*args) breitet eine unbekannte Anzahl von Werten aus, und helper(**options) benennt keinen davon. Dort behält die Engine die breite Antwort bei, die sie zuvor gab – die beibehaltene Menge ist immer enthalten in der vorherigen, also kann Verfeinern nur einen Finding entfernen, niemals einen erfinden.
Der Empfänger wird aufgrund der Signatur des Callee übersprungen, nicht aufgrund der Syntax des Aufrufs: Runner().go(x) führt zu einem go ohne Punkt, und Cls.m(obj, x) wie obj.m(x) sind beide ein Attribut. Da eine Methode fast immer gebunden aufgerufen wird, entscheidet self an der Spitze der Parameter – der ungebundene Aufruf ist die einzige Form, die einen Tick zu kurz gelesen wird.
Was der Graph nicht verfolgen kann
Ein Defekt, der über einen Pfad erreicht wird, den die Analyse nicht auflöst – ein Handler in einer Dispatch-Tabelle, eine dekorierte View, ein Aufruf auf einer Variable mit unbekanntem Typ – ist kein unerreichbarer Defekt. Thot unterscheidet die beiden:
HANDLERS = {"run": run_command} # aucun appel : le graphe ne voit rien
@app.route("/ping") # enregistré à l'import par le décorateur
sandbox.run("pytest") # plusieurs `run` répondent à ce nomIn allen drei Fällen ist die Reichweite unbekannt, nicht null, und der Finding behält eine leichte Strafe, statt begraben zu werden. Auf Hermes: dieselben 365 Findings, aber 60 steigen eine Stufe auf. Eine Funktion, die niemand aufruft und die niemand erwähnt, bleibt dagegen korrekt abgewertet – sonst würde der Filter aufhören, ein Filter zu sein.
Grenzen
Die Tönung über Dateien hinweg ist Python vorbehalten: JavaScript und TypeScript werden indexiert, innerhalb eines Funktionskörpers und bis zu einem Helper derselben Datei verfolgt, und enden dort mangels aufgelöstem Call-Graphen – die Tabelle oben sagt es Zeile für Zeile. Die Musterregeln dagegen gelten überall.
Ohne --deep ist jeder Finding PLAUSIBLE: statisch erkannt, noch nicht durch Ausführung bewiesen. Mit --deep hat ein confirmed-Finding eine gegnerische Widerlegung überstanden – das ist noch kein Ausführungsbeweis, der mit dem Repro kommt. Und das Fehlen eines Findings ist kein Beweis für das Fehlen eines Defekts: dynamisches Dispatching, Reflexion und Metaprogrammierung entgehen der Analyse.
Die Volltextsuche in Sessions hört auf, bei konstanten Kosten zu bleiben, sobald SQLite-Datenbanken betroffen sind, deren fts5 die rowid nicht zurückverfolgen kann: Die Engine sortiert dort die gesamte Treffermenge, statt bei zwanzig abzuschneiden. Gemessen an der, die CPython 3.12 mitbringt – 6 588 Anweisungen gegenüber 27 443, wenn das Korpus verfünffacht wird, während eine fähige Engine bei 2 215 und dann 2 698 bleibt. Ein Test verifiziert das und nennt die Version, wenn sie es nicht erlaubt.
Entwicklung
cd thot
uv run pytest -qEditable installiert: Der Quellcode gilt sofort. Wenn sich dagegen pyproject.toml ändert (neue Abhängigkeit), muss uv tool install --editable --from . thot --force erneut ausgeführt werden.
Der deterministische Kern (codemap, taint, scope, scoring, store, report) hängt von keinem Agenten ab und berührt kein Netzwerk – ein Test verifiziert das und lässt die Suite fehlschlagen, falls sich das ändert.
Spec und Pläne: docs/superpowers/.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Remote MCP for A2A dependency inspector MCP, structured receipts, audit logs, and reviewer-ready evi
Static MCP manifest and tool-policy security preflight with signed input-redacted receipts
A paid remote MCP for CodeG, built to return verdicts, receipts, usage logs, and audit-ready JSON.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nobodyohm-web/thot'
If you have feedback or need assistance with the MCP directory API, please join our Discord server