98 KiB
Hugin — Konzept für die Entwicklung
Hugin („Gedanke“) und Munin („Gedächtnis“) sind die beiden Raben Odins. Munin speichert, was Agenten wissen. Hugin legt fest, wie sie arbeiten.
Stand: 19.10.2026 · Status: Stufen 0–4 umgesetzt, Stufe 5 offen (→ 20)
Inhalt
- Kurzfassung
- Ausgangslage und Problem
- Ziele und Nicht-Ziele
- Begriffe · Namen im Code
- Zielwerkzeuge: Claude Code und OpenCode
- Das Quellformat
- Die Übersetzer
- Prüfung (Linter)
- Lebenszyklus und Freigabe
- Geltung, Abos, Pflichtpakete, Namen
- Verteilung: das Programm
hugin - MCP-Server
- Weboberfläche
- Architektur
- Datenmodell
- Sicherheit
- Kompatibilitätstests
- Betrieb
- Zusammenspiel mit Munin
- Ausbaustufen
- Entscheidungen und offene Fragen
- Quellen und was neu zu prüfen ist
1. Kurzfassung
Hugin ist ein zentrales Register für alles, was das Verhalten von KI-Agenten steuert: Skills, Hausregeln, Befehle, Subagenten und MCP-Anbindungen.
- Autoren legen Pakete an. Jede Änderung ist eine neue, unveränderliche Version.
- Eine zweite Person prüft und gibt frei, bevor eine Version bei den Teams ankommt.
- Ein Paket wird einmal in einem neutralen Format geschrieben. Hugin übersetzt es für Claude Code und für OpenCode. Beide Werkzeuge werden voll unterstützt, andere nicht.
- Die Teams holen die Pakete mit dem Kommandozeilenprogramm
hugin syncin ihre Repositories. Welche Pakete in welcher Version, steht in einer Datei im Repo. - Server in Python (FastAPI, Jinja2, HTMX, hauseigenes CSS-Framework), CLI in Go. Konzepte wie Munin: SQLite oder PostgreSQL, Tenants, Projekte und Keys, Audit, Hilfe-System, Markdown-Export, MCP-Endpunkt. Später in ein anderes FastAPI-Projekt einbettbar (→ 14.3).
2. Ausgangslage und Problem
Teams arbeiten mit Claude Code und OpenCode. Jedes Team schreibt eigene
Anweisungen (CLAUDE.md, AGENTS.md), eigene Skills und eigene Subagenten und
kopiert sie von Repository zu Repository. Daraus folgt:
| Problem | Folge |
|---|---|
| Wildwuchs | Fünf Fassungen derselben Anweisung „So nutzt du Munin“; niemand weiß, welche aktuell ist. |
| Keine Prüfung | Ein Skill kann Agenten anweisen, Befehle auszuführen oder Daten zu verschicken. Er ist faktisch Code, wird aber nie reviewt. |
| Keine Verbesserung von oben | Findet ein Team eine bessere Anweisung, erreicht sie die anderen nicht. |
| Kein Überblick | Niemand weiß, welche Skills es gibt, wer sie nutzt und in welcher Fassung. |
| Zwei Werkzeuge | Was für Claude Code geschrieben ist, funktioniert in OpenCode oft nur halb und umgekehrt. Teams pflegen doppelt oder gar nicht. |
3. Ziele und Nicht-Ziele
Ziele
- Ein Ort für Skills, Regeln, Befehle, Subagenten und MCP-Anbindungen, mit Suche.
- Geprüft: Nichts erreicht die Teams ohne Freigabe durch eine zweite Person.
- Versioniert und nachvollziehbar: Jede Version ist unveränderlich, mit Prüfsumme, Diff zur Vorgängerversion und Audit, wer was wann freigegeben hat.
- Claude Code und OpenCode gleichwertig: Ein Paket, beide Werkzeuge. Die Weboberfläche zeigt für jede Version, was in welchem Werkzeug ankommt.
- Einfach für die Teams: Eine kleine Datei im Repo, ein Befehl
hugin sync. Eigene Dateien der Teams werden nie angefasst. - Rückkanal: Hugin weiß, welches Projekt welche Version nutzt, und Agenten wie Menschen können Rückmeldung zu einem Paket geben.
- Betrieb wie Munin: Wer Munin betreibt, kann Hugin betreiben.
Nicht-Ziele
- Andere Werkzeuge (Codex, Cursor, Copilot …) werden nicht unterstützt. Die Übersetzer-Architektur ließe es zu, es ist aber ausdrücklich nicht geplant.
- Keine Ausführung: Hugin führt keine Agenten aus und startet keine Skripte.
- Kein Marktplatz für draußen: Hugin ist nur intern.
- Keine persönlichen Pakete: Hugin schreibt nicht in Nutzerordner
(
~/.claude/,~/.config/opencode/). Pakete gibt es nur öffentlich oder je Tenant bzw. Projekt. - Kein Ersatz für Munin: Wissen („Service X spricht nur IPv4“) gehört nach Munin. Anweisungen („Prüfe vor jedem Deploy die Changelogs“) gehören nach Hugin.
- Keine Bearbeitung von Hand in den Repos: Was Hugin verwaltet, wird über Hugin
geändert.
hugin syncüberschreibt Änderungen von Hand an verwalteten Dateien, nachdem es gewarnt hat.
4. Begriffe
| Begriff | Bedeutung |
|---|---|
| Paket | Eine verwaltete Einheit, z. B. der Skill pub-munin-nutzen. Hat einen Besitzer und eine Art. |
| Art | Was das Paket ist: skill, rules, command, subagent, mcp, hook. |
| Version | Ein unveränderlicher Stand eines Pakets mit Semver-Nummer (1.4.0) und Prüfsumme. |
| Ziel | Ein unterstütztes Werkzeug: claude (Claude Code) oder opencode (OpenCode). |
| Übersetzer | Erzeugt aus dem Quellformat die Dateien für ein Ziel. |
| Kompatibilitätsstufe | portable, translated, claude-only, opencode-only (→ Abschnitt 5.2). |
| Risiko | text, tools, scripts, permissions (→ Abschnitt 16). Bestimmt, wie streng die Prüfung ist. |
| Tenant | Abgeschlossener Bereich, z. B. eine Organisationseinheit. Sieht nur seine eigenen Pakete und die öffentlichen. Entspricht einem Workspace in Munin. |
| Geltung | Wer das Paket sieht und abonnieren darf: public, tenant, project. |
| Kanal | stable (Standard) oder test (für Teams, die Neues früh wollen). |
| Abo | Eintrag in .hugin.yaml eines Repos: dieses Paket, dieser Kanal oder Versionsbereich. |
| Pflichtpaket | Ein Paket, das jedes Projekt einer Geltung bekommt und nicht abbestellen kann. |
| Verwaltete Datei | Eine Datei, die hugin sync erzeugt hat und die es pflegt. Erkennbar am Kopfkommentar und an hugin.lock. |
4.1 Namen im Code
Das Konzept, die Hilfe und die Weboberfläche sind deutsch. Code, Felder, Werte, Befehle, API-Pfade und Datenbank sind englisch. Die wichtigsten Zuordnungen:
| Begriff | im Code | Begriff | im Code |
|---|---|---|---|
| Paket | package |
Freigabe / freigeben | approval / approve |
| Art | kind |
Prüfer, Prüfung | reviewer, review |
| Geltung (öffentlich, Tenant, Projekt) | scope (public, tenant, project) |
Entwurf / einreichen | draft / submit |
| Besitzer | owner |
zurückgeben | return |
| Beschreibung | description |
veraltet / reaktivieren | deprecated / reactivate |
| Änderung | changes |
Kanal stabil / test | channel stable / test |
| Ziele | targets |
Risiko (Werkzeuge, Skripte, Rechte) | risk (tools, scripts, permissions) |
| Zusatz | extra |
Kompatibilität (portabel, übersetzt) | compatibility (portable, translated) |
| Argumente | arguments |
Pflichtpaket | mandatory |
| Fähigkeit (lesen, schreiben) | capability (read, write) |
Abo / abbestellt | subscription / unsubscribed |
| Modell (schnell, gründlich, erben) | model (fast, thorough, inherit) |
Auslieferung | delivery |
| Kopfzeilen | headers |
Rückmeldung | feedback |
| Regeln, Befehl | rules, command |
Untergrenze | floor |
| Werkzeug | tool |
Befund | finding |
| Zugriff | access |
Abgleich | reconcile |
CLI: hugin init, sync, check, status, login, new, validate, try,
upload, search, import.
5. Zielwerkzeuge: Claude Code und OpenCode
5.1 Grundentscheidung: Wer ist der Standard?
Die naheliegende Idee wäre: „OpenCode ist Standard, Claude Code liest das mit.“
Es ist aber umgekehrt. OpenCode liest viele Claude-Formate mit
(.claude/skills/, CLAUDE.md als Ersatz für AGENTS.md, ~/.claude/skills/).
Claude Code liest keine OpenCode-Ordner (.opencode/…). Würde Hugin nur
OpenCode-Dateien schreiben, sähe Claude Code die meisten davon nicht.
Deshalb gilt in Hugin:
| Frage | Entscheidung |
|---|---|
| Woran richtet sich die Strenge? | An der jeweils strengeren Regel; das ist meist OpenCode, z. B. bei Namen und Feldern. Was diese Regeln erfüllt, funktioniert in beiden. |
| Wo liegen Skills? | In .claude/skills/, weil beide Werkzeuge diesen Ort lesen. Eine Datei, kein Doppel. Nutzt ein Repo nur OpenCode, in .opencode/skills/ (→ 7.1). |
| Wo liegt der Rest? | Je Werkzeug am eigenen Ort, erzeugt von den Übersetzern. |
| Was ist mit Zusatzfunktionen von Claude Code? | Erlaubt, aber nur als ausdrücklicher Zusatz für das Ziel claude (→ Abschnitt 6.3). Der Kern eines Pakets bleibt portabel. |
Das schränkt kaum ein. Man verzichtet im Kern nur auf die Komfortfelder von Claude Code
(allowed-tools, context: fork, model im Skill …). Wer sie braucht, setzt sie im
Block targets.claude. Das Paket bleibt dann für OpenCode nutzbar, nur ohne diesen Komfort.
5.2 Kompatibilitätsstufen
| Stufe | Bedeutung | Beispiel |
|---|---|---|
portable |
Eine erzeugte Datei, beide Werkzeuge lesen sie. | Skill nur mit name und description |
translated |
Hugin erzeugt je Werkzeug eine eigene Fassung. | Subagent, Befehl mit Argumenten, MCP-Eintrag |
claude-only |
Nutzt etwas, das es in OpenCode nicht gibt. | Claude-Hook in settings.json |
opencode-only |
Nutzt etwas, das es in Claude Code nicht gibt. | OpenCode-Plugin in TypeScript |
Hugin berechnet die Stufe automatisch (→ Abschnitt 8) und zeigt sie im Katalog.
Ziel: fast alles portable oder translated; …-only ist die begründete Ausnahme.
5.3 Abbildung je Art
Skills
| Claude Code | OpenCode | |
|---|---|---|
| Ort im Projekt | .claude/skills/<name>/SKILL.md |
.opencode/skills/, liest auch .claude/skills/ und .agents/skills/ |
| Ort für den Nutzer | ~/.claude/skills/ |
~/.config/opencode/skills/, liest auch ~/.claude/skills/ |
| Suche im Projekt | auch verschachtelt <unterordner>/.claude/skills/ |
wandert vom Arbeitsordner nach oben bis zur Git-Wurzel |
| Pflichtfelder | nur description empfohlen, name wird sonst der Ordnername |
name und description |
| Name | großzügig | ^[a-z0-9]+(-[a-z0-9]+)*$, 1–64 Zeichen, gleich dem Ordnernamen |
| Beschreibung | bis 1.536 Zeichen (mit when_to_use) |
1–1.024 Zeichen |
| Zusatzfelder | viele (allowed-tools, disable-model-invocation, user-invocable, context, agent, model, effort, paths, hooks, arguments, argument-hint, shell …) |
nur license, compatibility, metadata; Eigenes unter metadata (opencode/slash, opencode/autoinvoke wirken in OpenCode 1 nicht, → STUFE0.md) |
| Unbekannte Felder | werden still ignoriert | werden still ignoriert (Stufe 0) |
| Laden | automatisch nach Beschreibung oder per /name; nur von Hand mit disable-model-invocation |
über das Werkzeug skill, nach Beschreibung, oder per /name; immer beides (Stufe 0) |
| Rechte | allowed-tools im Skill |
permission.skill in opencode.json |
Für Hugin: Skills sind die einfachste Art. Mit dem gemeinsamen Kern
(name, description, license, compatibility, metadata) sind sie portable.
Regeln (Hausanweisungen)
| Claude Code | OpenCode | |
|---|---|---|
| Hauptdatei | CLAUDE.md |
AGENTS.md, sonst CLAUDE.md als Ersatz |
| Weitere Dateien einbinden | @path/to/file.md in der CLAUDE.md |
instructions in opencode.json: Pfade, Globs, auch URLs |
AGENTS.md |
ab 2.1.277, aber nur wenn es keine CLAUDE.md gibt |
nativ; OpenCode 2 liest nur noch AGENTS.md, nicht mehr CLAUDE.md |
Für Hugin: Hugin schreibt Regeln in eigene Dateien unter .hugin/rules/
und bindet sie ein, ohne die Inhalte der Teams anzufassen:
- in der
CLAUDE.mdüber einen verwalteten Block mit@-Verweisen, - in
opencode.jsonüber einen Eintrag.hugin/rules/*.mdininstructions.
Einzelheiten → Abschnitt 7.2.
Befehle (Slash-Commands)
| Claude Code | OpenCode | |
|---|---|---|
| Ort | .claude/commands/<name>.md (weiter unterstützt) oder als Skill |
.opencode/commands/<name>.md |
| Felder | wie bei Skills, außer name und paths |
description, agent, subtask, model |
| Alle Argumente | $ARGUMENTS |
$ARGUMENTS |
| Einzelne Argumente | $0, $1 … ab 0, oder benannt über arguments + $name |
$1, $2 … ab 1 |
| Shell-Ausgabe einfügen | !`befehl` |
!`befehl` |
| Datei einfügen | @path |
@path |
Stolperfalle: Derselbe Text mit $1 meint in Claude Code das zweite,
in OpenCode das erste Argument. Hugin erlaubt im Quellformat deshalb nur
$ARGUMENTS oder benannte Argumente, die der Übersetzer umschreibt.
Subagenten
| Claude Code | OpenCode | |
|---|---|---|
| Ort | .claude/agents/*.md |
.opencode/agents/*.md |
| Name | Feld name |
Dateiname |
| Pflicht | name, description |
description |
| Werkzeuge | tools (Erlaubtliste), disallowedTools |
permission: edit, bash, webfetch … je allow, ask, deny (tools ist veraltet) |
| Modell | sonnet, opus, haiku, fable, volle ID oder inherit |
provider/model-id |
| Art | immer Subagent | primary, subagent, all |
| Eigenes | permissionMode, skills, mcpServers, memory, isolation, effort … |
temperature, steps, hidden, color, top_p … |
Für Hugin: Subagenten sind immer translated. Das Quellformat beschreibt sie
neutral über Fähigkeitsklassen und Modellklassen (→ Abschnitt 7.4).
MCP-Anbindungen
| Claude Code | OpenCode | |
|---|---|---|
| Datei | .mcp.json |
opencode.json, Schlüssel mcp |
| Entfernter Server | "type": "http", "url": …, "headers": {…} |
"type": "remote", "url": …, "headers": {…} |
| Umgebungsvariablen | ${VAR} |
{env:VAR} |
| MCP-Werkzeuge | ja | ja |
| MCP-Prompts und Ressourcen | ja | in der Doku nicht erwähnt |
Für Hugin: MCP-Pakete sind translated. Keys stehen nie im Paket, nur der Name
der Umgebungsvariablen. Hugin verlässt sich nur auf MCP-Werkzeuge.
Hooks, Plugins, Rechte
| Claude Code | OpenCode | |
|---|---|---|
| Mechanismus | Hooks als Shell-Befehle in .claude/settings.json |
Plugins in JS/TS (.opencode/plugins/ oder npm), Ereignisse wie tool.execute.before |
| Rechte | permissions in .claude/settings.json |
permission in opencode.json |
Für Hugin: grundverschieden, also nicht übersetzbar. Hooks und Plugins gibt es
nur als claude-only- bzw. opencode-only-Pakete. Zwei Pakete, die dasselbe tun,
lassen sich als Paar verknüpfen (→ Abschnitt 6.5). Rechte verwaltet Hugin in der
ersten Fassung gar nicht; sie bleiben Sache der Teams.
5.4 Werkzeugversionen als Untergrenze
Beide Werkzeuge entwickeln sich schnell. Viele Funktionen, auf die Hugin baut, gibt es erst ab einer bestimmten Version. Beispiele:
- dass OpenCode
.claude/skills/mitliest, - die
metadata-Schlüsselopencode/slashundopencode/autoinvoke, - benannte Argumente (
arguments) in Claude Code.
Hat ein Entwickler eine ältere Version, kommt der Skill zwar an, wird aber nicht gefunden oder verhält sich anders. Das ist ein stiller Fehler, der schwer zu finden ist.
Untergrenze heißt: Hugin legt fest, welche Version mindestens installiert sein muss, z. B. „Claude Code ≥ 2.x, OpenCode ≥ 1.y“.
- Die Untergrenze steht in der Hugin-Konfiguration; der Plattform-Admin pflegt sie.
hugin syncundhugin checklesen die installierten Versionen (claude --version,opencode --version) und warnen, wenn eine zu alt ist. Mittool_check: strictin.hugin.yaml(oder--strict) brichtsyncstattdessen ab.- Ein Paket kann eine höhere Untergrenze verlangen, wenn es eine neuere
Funktion nutzt (Feld
min_versionim Manifest). - Der Linter kennt eine Tabelle „Feld oder Funktion → ab Version“ und setzt die Untergrenze eines Pakets automatisch.
- Die Untergrenze wird nur angehoben, wenn Hugin eine neue Funktion wirklich braucht.
Die konkreten Versionsnummern stehen in 5.5.
5.5 Untergrenzen (Stand 18.10.2026)
Ermittelt aus dem Changelog von Claude Code und den Release-Notes von OpenCode. Aktuell: Claude Code 2.1.302, OpenCode 1.19.3 und OpenCode 2.
Claude Code
| Funktion, die Hugin nutzt | ab |
|---|---|
Skills (.claude/skills/) |
2.0.20 |
disallowedTools in Subagenten |
2.0.30 |
Skills in Unterordnern (<unterordner>/.claude/skills/) |
2.1.6 |
effort in Skills und Befehlen |
2.1.80 |
paths als YAML-Liste |
2.1.84 |
| Beschreibung bis 1.536 Zeichen | 2.1.105 |
volle Modell-IDs in Subagenten (model: claude-…) |
2.1.74 |
benannte Argumente (arguments, $name) |
nicht im Changelog; wirken in 2.1.303 (Stufe 0), ältere Versionen ungeprüft |
AGENTS.md wird gelesen, wenn es keine CLAUDE.md gibt |
2.1.277 (für Hugin nicht nötig) |
Untergrenze Claude Code: 2.1.105, vorläufig. Sie steigt, falls die benannten
Argumente später kamen. Prüfen: ältere Version installieren und
TOOLS=claude scripts/tool-smoke-test.sh.
OpenCode 1
| Funktion, die Hugin nutzt | ab |
|---|---|
| Kopfzeilen für MCP-Server | 0.3.18 (zuverlässig ab 1.0.138) |
| Agent Skills | 1.0.186 |
liest .claude/skills/ |
1.0.208 |
permission statt tools in Agenten |
1.1.1 |
| Claude-Kompatibilität abschaltbar | 1.1.7 |
liest .agents/skills/ |
1.1.50 |
| Frontmatter mit CRLF (Windows) | 1.2.11 |
metadata-Schlüssel opencode/slash, opencode/autoinvoke |
wirken nicht (1.19.3, Stufe 0); Skills sind immer per /name aufrufbar |
Untergrenze OpenCode 1: 1.2.11, vorläufig. Die Windows-Korrektur ist der Grund.
OpenCode 2 (seit 11.10.2026, Wechsel mit opencode upgrade --major; beide
Fassungen heißen opencode)
| Thema | Auswirkung auf Hugin |
|---|---|
| Skills | liest weiter .claude/skills/ und .agents/skills/; name ist nur noch Anzeige, die ID kommt aus dem Pfad; opencode/autoinvoke und disable-model-invocation werden verstanden, opencode/slash ist nicht dokumentiert |
CLAUDE.md |
wird nicht mehr gelesen, nur AGENTS.md. Für Hugin unkritisch, weil Regeln über instructions eingebunden werden (→ 7.2) |
instructions in opencode.json |
unverändert |
| Agenten und Befehle | neue Felder (permissions als geordnete Liste, bash → shell, subtask → subagent); alte Frontmatter wird automatisch übersetzt |
| MCP | Server stehen unter mcp.servers, enabled wird zu disabled. Ob die alte Form noch gelesen wird, ist offen → Stufe 0 |
| Plugins | V1-Plugins laufen nicht. Betrifft plugin-Pakete (Stufe 5d) und später hook-Pakete; ungeprüft |
Folge: Hugin unterstützt vorerst OpenCode 1 und 2. Die Übersetzer schreiben
Agenten und Befehle im Format von OpenCode 1, das beide verstehen. Für MCP (und
später Plugins) braucht es vielleicht eine eigene Fassung für OpenCode 2; das ist
ungeprüft, solange OpenCode 2 nicht getestet ist (STUFE0.md, Frage 10). Das CLI meldet dafür die erkannte Hauptversion (opencode@1 oder
opencode@2) an /bundle.
6. Das Quellformat
Ein Paket ist ein Ordner. Darin liegen das Manifest hugin.yaml und der Inhalt.
So kann ein Autor ein Paket lokal mit seinem Werkzeug entwickeln und dann hochladen.
6.1 Ordnerstruktur
pub-munin-nutzen/
├── hugin.yaml # Manifest: wer, was, welche Version, welche Ziele
├── SKILL.md # Inhalt (bei skill); Frontmatter erzeugt Hugin
├── referenz/
│ └── beispiele.md # Hilfsdateien, werden mitverteilt
└── skripte/
└── pruefen.sh # Skripte: Risiko „skripte“, strengere Prüfung
Je Art heißt die Inhaltsdatei anders:
| Art | Inhaltsdatei | Bemerkung |
|---|---|---|
skill |
SKILL.md |
plus beliebige Hilfsdateien |
rules |
RULES.md |
reiner Text |
command |
COMMAND.md |
Prompt-Vorlage mit Platzhaltern |
subagent |
AGENT.md |
Systemprompt des Subagenten |
mcp |
keine | alles steht im Manifest |
hook |
claude/… oder opencode/… |
Inhalt je Werkzeug, nicht übersetzt |
In der Inhaltsdatei steht keine Frontmatter. Alle Angaben stehen im Manifest, die Übersetzer erzeugen daraus die Frontmatter je Werkzeug. So gibt es genau eine Stelle für Metadaten.
6.2 Das Manifest
# hugin.yaml
id: pub-munin-nutzen # = Name in beiden Werkzeugen, Regeln → 8
kind: skill
version: 1.4.0
owner: team-plattform # Projekt, das das Paket pflegt
scope: public # public | tenant | project
description: >
Wann und wie du Munin nutzt: vor neuer Arbeit recall, nach Erkenntnissen
remember. Nutze diesen Skill immer, wenn du in einem Projekt mit Munin arbeitest.
license: intern # optional
min_version: # optional, sonst die allgemeine Untergrenze
claude: ""
opencode: ""
changes: > # Pflicht ab Version 2: was ist neu?
Hinweis auf das neue Werkzeug „related“ ergänzt.
targets: [claude, opencode] # Standard: beide
Hugin setzt selbst, der Autor setzt sie nicht:
risk(vom Linter ermittelt, → 16)compatibility(vom Linter ermittelt, → 5.2)checksum- Status, Freigabe, Kanal
6.3 Zusätze je Werkzeug
Was nur ein Werkzeug kann, steht in einem eigenen Block. Der Kern bleibt portabel.
extra:
claude:
allowed-tools: [mcp__munin__recall, mcp__munin__remember]
paths: ["**/*.go"]
In OpenCode ist jeder Skill auch per /name aufrufbar; opencode/slash ist nicht nötig.
Einen Skill nur von Hand aufrufbar zu machen, geht in OpenCode 1 nicht: opencode/autoinvoke
und disable-model-invocation werden übergangen (geprüft mit 1.19.3, → STUFE0.md).
In Claude Code wirkt disable-model-invocation: true.
- Der Linter kennt für jedes Werkzeug die erlaubten Felder. Unbekannte lehnt er ab.
- Zusätze können die Kompatibilitätsstufe ändern. Beispiel:
context: forkbei Claude Code verlangt eine eigene Fassung je Werkzeug →translated.
6.4 Beispiele je Art
Befehl mit benannten Argumenten
id: pub-release-notiz
kind: command
version: 1.0.0
owner: team-plattform
scope: public
description: Schreibt eine Release-Notiz für eine Version.
arguments: [version, zielgruppe] # benannt, Reihenfolge = Position
<!-- COMMAND.md -->
Schreibe eine Release-Notiz für Version {{version}}, gedacht für {{zielgruppe}}.
Lies dazu die Commits seit dem letzten Tag:
!`git log --oneline $(git describe --tags --abbrev=0)..HEAD`
{{name}} ist der neutrale Platzhalter von Hugin. Daneben ist nur $ARGUMENTS erlaubt.
Subagent
id: pub-sicherheits-review
kind: subagent
version: 2.1.0
owner: team-sicherheit
scope: public
description: >
Prüft Änderungen auf Sicherheitsprobleme. Nach Änderungen an
Authentifizierung, Eingabeverarbeitung oder Kryptografie einsetzen.
capability: read # read | write | shell | web; kombinierbar als Liste
model: thorough # fast | standard | thorough | inherit
extra:
claude:
effort: high
opencode:
mode: subagent
temperature: 0.1
MCP-Anbindung
id: pub-munin-anbindung
kind: mcp
version: 1.0.0
owner: team-plattform
scope: public
description: Bindet Munin als MCP-Server an.
server:
name: munin
url: https://munin.intern/mcp
headers:
Authorization: "Bearer {{env:MUNIN_KEY}}" # neutral, nie ein echter Key
6.5 Paare
Zwei Pakete, die dasselbe für verschiedene Werkzeuge tun, typischerweise Hooks:
id: pub-prod-schutz-claude
kind: hook
targets: [claude]
pair: pub-prod-schutz-opencode
Hugin zeigt Paare zusammen an, warnt, wenn ein Projekt nur eine Hälfte abonniert hat und beide Werkzeuge nutzt, und verlangt bei einer neuen Version der einen Hälfte eine Bestätigung, dass die andere noch passt.
7. Die Übersetzer
Ein Übersetzer je Ziel und Art. Ein Übersetzer ist eine reine Funktion: Version + Hugin-Konfiguration → Liste von (Pfad, Inhalt). Er schreibt nichts selbst. Das hat zwei Vorteile:
- Es gibt sie nur einmal, im Server (→ 14.2). Das CLI wendet nur ihr Ergebnis an, die Weboberfläche zeigt damit vorher, was im Repo ankommt.
- Übersetzer sind einfach mit Tabellentests zu prüfen: Eingabe → erwartete Dateien.
7.1 Skills
portable: eine Datei.claude/skills/<id>/SKILL.mdmit Frontmatter ausname,description,license,metadata, dazu die Hilfsdateien. Beide Werkzeuge lesen sie.- Mit Zusätzen für Claude: OpenCode übergeht unbekannte Felder wie
allowed-toolsstill (Stufe 0, Frage 1). Die Felder kommen mit in dieselbe Datei, die Stufe bleibtportable. Folge: Felder, die das Verhalten ändern, wirken nur in Claude Code, etwadisable-model-invocation(Linter-Hinweis H023). - Ausnahme
OPENCODE_DISABLE_CLAUDE_CODEoder…_SKILLS: Hat ein Team den Claude-Support in OpenCode abgeschaltet, liest OpenCode.claude/skills/nicht. Dafür gibt es in.hugin.yamldie Einstellungopencode_reads_claude: false. Hugin schreibt die Skills dann zusätzlich nach.opencode/skills/. - Nur OpenCode (
toolsohneclaude): Skills nur nach.opencode/skills/, nichts unter.claude/. Der Server leitet das aus den Werkzeugen der Anfrage ab. Grund:.opencode/skills/ist der eigene Ort von OpenCode und hängt nicht davon ab, ob der Claude-Support an ist; ein.claude/-Ordner in einem Repo ohne Claude Code verwirrt nur. Unter OpenCode 1.19.3 verdeckte zudem ein globaler Skill (~/.config/opencode/skills/) einen gleichnamigen aus.claude/skills/im Repo; ab 1.19.4 gewinnt an beiden Orten der Skill im Repo (geprüft mitopencode debug skill).
7.2 Regeln
Erzeugte Dateien:
.hugin/rules/pub-hausregeln.md
.hugin/rules/team-alpha-go-stil.md
Einbindung für Claude Code: Hugin pflegt in der CLAUDE.md einen verwalteten Block
und legt die Datei an, falls es sie nicht gibt:
<!-- hugin:begin — von hugin sync verwaltet, nicht von Hand ändern -->
@.hugin/rules/pub-hausregeln.md
@.hugin/rules/team-alpha-go-stil.md
<!-- hugin:end -->
Einbindung für OpenCode: Hugin ergänzt in opencode.json den Eintrag
".hugin/rules/*.md" in instructions, falls er fehlt. Andere Einträge bleiben unberührt.
Ergebnisse aus Stufe 0 (STUFE0.md):
- OpenCode liest bei fehlender
AGENTS.mdzwar dieCLAUDE.md, löst deren@-Verweise aber nicht auf. Die Regeln kommen also nicht doppelt an; eine eigeneAGENTS.mdist nicht nötig. opencode.jsondarf Kommentare (JSONC) enthalten. Das CLI erhält sie beim Ändern.
7.3 Befehle
| Claude Code | OpenCode | |
|---|---|---|
| Datei | .claude/commands/<id>.md |
.opencode/commands/<id>.md |
{{name}} wird zu |
$name, dazu arguments: [...] in der Frontmatter |
$<position ab 1> |
$ARGUMENTS |
bleibt | bleibt |
Beispiel {{version}} (erstes Argument): Claude $version, OpenCode $1.
.claude/commands/ statt eines Skills, weil OpenCode Skills aus .claude/skills/
mitliest. Ein Befehl als Skill käme dort als zusätzlicher, vom Modell automatisch
aufrufbarer Skill an. Geprüft (STUFE0.md): OpenCode 1 liest .claude/commands/ nicht.
7.4 Subagenten
Fähigkeitsklassen:
| Klasse | Claude Code (tools) |
OpenCode (permission) |
|---|---|---|
read |
Read, Grep, Glob |
edit: deny, bash: deny, webfetch: deny, websearch: deny |
write |
+ Edit, Write |
edit: allow |
shell |
+ Bash |
bash: ask (bewusst nicht allow) |
web |
+ WebFetch, WebSearch |
webfetch: allow, websearch: allow |
Die Werkzeug- und Rechtenamen sind in Stufe 0 geprüft und stehen als Tabelle im Code
(TOOLS und PERMISSIONS in den Übersetzern). Ändern sie sich, ändert man nur
diese Tabelle.
Modellklassen: Der Plattform-Admin legt die Zuordnung in der Hugin-Konfiguration fest:
models:
claude:
fast: haiku
standard: sonnet
thorough: opus
opencode:
fast: anthropic/claude-haiku-5-5
standard: anthropic/claude-sonnet-5-5
thorough: anthropic/claude-opus-5-5
inherit wird in Claude Code zu model: inherit, in OpenCode fehlt model einfach.
Eine neue Modellgeneration bedeutet: Zuordnung ändern, hugin sync aufrufen, fertig.
An den Paketen ändert sich nichts.
Erzeugte Dateien: .claude/agents/<id>.md mit name, description, tools, model
und den Zusätzen; .opencode/agents/<id>.md mit description, mode
(Standard subagent), permission, model und den Zusätzen.
Geprüft (STUFE0.md): OpenCode 1 liest .claude/agents/ nicht mit; es gibt keine Doppel.
7.5 MCP-Anbindungen
- Claude Code: Hugin ergänzt in
.mcp.jsonuntermcpServers.<name>den Eintrag"type": "http",url,headers. Aus{{env:VAR}}wird${VAR}. - OpenCode: Hugin ergänzt in
opencode.jsonuntermcp.<name>den Eintrag"type": "remote",url,headers,"enabled": true. Aus{{env:VAR}}wird{env:VAR}. - Hugin ändert nur Schlüssel, die es selbst angelegt hat (vermerkt in
hugin.lock). Gibt es schon einen gleichnamigen Server, den Hugin nicht angelegt hat, brichtsyncmit einer klaren Meldung ab, statt ihn zu überschreiben.
7.6 Hooks
Keine Übersetzung. Der Inhalt aus claude/ bzw. opencode/ wird übernommen:
- Claude Code: Hook-Einträge in
.claude/settings.json, nur in Schlüsseln, die Hugin verwaltet; dazu das Skript unter.hugin/hooks/<id>/. - OpenCode: Plugin-Datei unter
.opencode/plugins/<id>.ts.
Hooks haben immer mindestens das Risiko scripts (→ 16).
8. Prüfung (Linter)
Der Linter läuft beim Hochladen jeder Version auf dem Server und lokal mit
hugin validate. Er liefert Fehler (Version wird nicht angenommen) und Hinweise.
Jede Regel hat eine Kennung, damit Meldungen und Doku zusammenpassen.
| Kennung | Prüft | Stufe |
|---|---|---|
H001 |
id passt auf ^[a-z0-9]+(-[a-z0-9]+)*$, 1–64 Zeichen |
Fehler |
H002 |
id beginnt mit dem Präfix der Geltung (→ 10.4) |
Fehler |
H003 |
description 1–1.024 Zeichen, nicht mehr der Platzhalter aus Vorlage oder Import |
Fehler |
H004 |
version ist Semver und größer als die letzte |
Fehler |
H005 |
changes ist ab der zweiten Version gesetzt |
Fehler |
H006 |
die Inhaltsdatei der Art ist vorhanden (SKILL.md, RULES.md …) |
Fehler |
H007 |
die Inhaltsdatei hat keine Frontmatter | Fehler |
H008 |
Art hook erst ab Stufe 5 |
Fehler |
H010 |
keine nummerierten Platzhalter ($0, $1 …) im Inhalt |
Fehler |
H011 |
alle {{name}} sind in arguments deklariert und umgekehrt |
Fehler |
H012 |
keine werkzeugspezifischen Variablen im Inhalt (${CLAUDE_SKILL_DIR} …) |
Fehler |
H020 |
Felder in extra.claude und extra.opencode kennt das Werkzeug für diese Art |
Fehler |
H021 |
Werkzeugnamen im Text (Read, Bash, read, bash …) |
Hinweis: neutral formulieren, z. B. „lies die Datei“ statt „nutze Read“ |
H022 |
Verweise auf Hilfsdateien sind relativ und existieren | Fehler |
H023 |
Felder ohne Wirkung in OpenCode 1: „nur von Hand“ (disable-model-invocation, opencode/autoinvoke) und opencode/slash (→ STUFE0.md) |
Hinweis |
H024 |
auffällige Anweisungen an den Agenten, z. B. „ignoriere vorherige Anweisungen“ (Prompt-Injection, → 16.2) | Hinweis |
H025 |
unsichtbare Unicode-Zeichen (Zero-Width, Bidi, Tag-Zeichen): Prüfer sehen sie nicht, das Modell schon | Fehler |
H030 |
Skripte: Typ erkannt (sh, ps1, py …) | Hinweis bei nur einem Betriebssystem |
H031 |
kein Geheimnis im Paket (Muster für Keys, Passwörter, Tokens) | Fehler |
H032 |
{{env:…}} statt echter Werte in MCP-Kopfzeilen |
Fehler |
H033 |
ein mcp-Paket hat einen Block server |
Fehler |
H034 |
capability eines Subagenten: nur read, write, shell, web |
Fehler |
H035 |
model eines Subagenten: nur fast, standard, thorough, inherit |
Fehler |
H040 |
Risiko ermitteln (→ 16) | – |
H041 |
Kompatibilitätsstufe ermitteln (→ 5.2) | – |
H042 |
Mindestversion aus genutzten Feldern ermitteln (→ 5.4); als Hinweis gemeldet, in der Version gespeichert und im Bundle als Hinweis ausgeliefert | Hinweis |
H043 |
Risiko scripts oder permissions nur mit persönlichem Key (hp_…, → 14.5) |
Fehler |
H050 |
Paar: die andere Hälfte existiert und hat die passende Gegenrichtung | Fehler |
Die Kompatibilitätsstufe wird nicht geraten. Die Übersetzer laufen probehalber:
Kommt für beide Ziele dieselbe Datei am selben Ort heraus, ist das Paket portable.
9. Lebenszyklus und Freigabe
9.1 Status einer Version
Entwurf ──einreichen──▶ in Prüfung ──freigeben──▶ freigegeben ──▶ veraltet
▲ │
└───────zurückgeben──────┘ (mit Kommentar)
- Entwurf: Nur Autor und Besitzer sehen ihn. Er ist änderbar, bekommt aber bei jeder Änderung eine neue Prüfsumme.
- In Prüfung: Ab hier unveränderlich. Prüfer sehen Diff, Vorschau je Werkzeug, Linter-Ergebnis und Risiko.
- Freigegeben: Erreicht die Teams beim nächsten
hugin sync. - Veraltet: Bleibt abrufbar, damit
hugin.lockimmer funktioniert.syncmeldet Veraltetes und schlägt die Nachfolgeversion vor. - Daneben kann ein Paket insgesamt veraltet sein, mit Hinweis auf einen Nachfolger.
9.2 Wer darf freigeben
| Risiko | Freigaben | Wer |
|---|---|---|
text |
1 | ein Prüfer der Geltung, nicht der Autor |
tools |
1 | wie oben |
scripts |
2 | zwei Prüfer, keiner davon der Autor |
permissions |
2 | wie oben, einer davon Admin der Geltung |
Öffentliche Pakete gibt ein Prüfer frei, den der Plattform-Admin dafür benannt hat.
Voraussetzung: Für das Vier-Augen-Prinzip muss Hugin Personen unterscheiden können, nicht nur Projekte (→ 14.5). Bis Stufe 4 gilt eine vereinfachte Regel: eine Freigabe mit einem Prüfer-Key, der vom Autor-Key getrennt vergeben ist.
9.3 Versionen und Kanäle
- Semver:
- Major: Argumente, Name oder Verhalten ändern sich so, dass Nutzer etwas anpassen müssen.
- Minor: Ergänzungen.
- Patch: Tippfehler und Klarstellungen.
- Kanal
stable: neueste freigegebene Version. - Kanal
test: Eine freigegebene Version kann alstestmarkiert werden. Sie erreicht nur Abos auf@testund wird später aufstablehochgestuft. - Abos auf Versionsbereiche:
@^1(alles 1.x),@1.4.0(fest). Bei@stablegilt: Major-Sprünge kommen nicht automatisch.syncmeldet sie, der Nutzer bestätigt mithugin sync --major.
10. Geltung, Abos, Pflichtpakete, Namen
10.1 Geltung
Zwei Ebenen: eine öffentliche für alle und darunter die Tenants, die voneinander nichts sehen. Innerhalb eines Tenants kann ein Paket zusätzlich auf ein Projekt beschränkt sein.
| Geltung | Wer sieht und abonniert | Wer pflegt |
|---|---|---|
public |
alle Tenants | Plattform-Team und benannte Autoren |
tenant |
alle Projekte dieses Tenants, sonst niemand | Projekte des Tenants |
project |
nur dieses Projekt | das Projekt selbst |
Ein Tenant sieht also genau: die öffentlichen Pakete und seine eigenen. Das gilt
für alle Zugänge gleich: Weboberfläche, API, /bundle, MCP-Suche und Export
(→ 15.1). Ein Paket kann nicht für ausgewählte Tenants freigegeben werden; wer
das braucht, legt es im jeweiligen Tenant an oder macht es öffentlich.
10.2 Abos
Abos stehen im Repository (.hugin.yaml, → 11.2), nicht auf dem Server.
Damit sind sie versioniert und im Code-Review sichtbar. Der Server erfährt sie über
die Nutzungsmeldung von sync.
10.3 Pflichtpakete
Plattform-Admin (für öffentliche Pakete, dann in allen Tenants) und Tenant-Admin (für seinen Tenant) können
Pakete als Pflicht markieren. hugin sync holt sie immer, auch wenn sie in
.hugin.yaml fehlen, und verweigert ein Abbestellen. Gedacht für wenige Dinge,
etwa Sicherheitsregeln und die Munin-Anbindung.
10.4 Namensräume
Weder Claude Code noch OpenCode kennen außerhalb von Plugins Namensräume für Skills und Agenten. Feste Präfixe verhindern Kollisionen mit den eigenen Dateien der Teams:
| Geltung | Präfix | Beispiel |
|---|---|---|
public |
pub- |
pub-munin-nutzen |
tenant |
<tenant-slug>- |
vertrieb-angebot-pruefen |
project |
<projekt-slug>- |
shop-api-release |
Tenant-Slugs dürfen nicht pub, hugin, admin oder api heißen; das wird beim
Anlegen eines Tenants geprüft.
Das Präfix gehört zur id und zählt zu den 64 Zeichen. Tenant- und
Projekt-Slugs sollten deshalb kurz sein.
11. Verteilung: das Programm hugin
Ein einzelnes Go-Programm ohne Abhängigkeiten für macOS, Linux und Windows. Es ist für zwei Gruppen gedacht: Teams (abonnieren, abgleichen) und Autoren (entwickeln, prüfen, hochladen).
11.1 Befehle
| Befehl | Für | Tut |
|---|---|---|
hugin init --server … --project … |
Teams | legt .hugin.yaml an, mit tools: auto (Werkzeuge werden bei jedem sync an den Dateien im Repo erkannt) |
hugin sync |
Teams | holt die abonnierten und die Pflichtpakete, übersetzt, schreibt, aktualisiert hugin.lock |
hugin sync --major |
Teams | erlaubt Major-Sprünge |
hugin check |
Teams | ändert nichts; meldet neue Versionen, veraltete Pakete, Handänderungen an verwalteten Dateien, zu alte Werkzeugversionen. Rückgabewert ≠ 0 bei Befund, gut für Skripte |
hugin status |
Teams | listet Abos, installierte Versionen und Kompatibilität |
hugin search [text] [--kind …] |
beide | sucht im Katalog in ID und Beschreibung |
hugin new <art> <id> |
Autoren | legt einen Paketordner mit Manifest-Gerüst an |
hugin validate [ordner] |
Autoren | schickt den Ordner an den Server zum Prüfen (ohne Speichern); Linter-Ergebnis und Vorschau der erzeugten Dateien je Werkzeug |
hugin try [ordner] |
Autoren | installiert den Entwurf in das aktuelle Repo, ohne hugin.lock zu ändern, zum Ausprobieren mit Claude Code und OpenCode. Führt .hugin/trial.lock; hugin try --remove räumt wieder auf |
hugin upload [ordner] |
Autoren | lädt als Entwurf hoch |
hugin import --prefix <p> |
Autoren | wandelt vorhandene Dateien des Repos in Paketordner um, OpenCode zuerst: .opencode/, opencode.json, AGENTS.md, danach .claude/, .mcp.json, CLAUDE.md. Werkzeugeigene Felder (z. B. permission) bleiben samt Reihenfolge in extra; feste Werte in MCP-Kopfzeilen werden zu {{env:…}}; lokale MCP-Server, Plugins und Symlinks bleiben draußen. Ändert im Repo nichts; Übersicht in IMPORT.md |
hugin login |
beide | speichert den Key im Schlüsselbund des Betriebssystems, sonst Umgebungsvariable HUGIN_KEY |
11.2 .hugin.yaml im Repository (wird eingecheckt)
server: https://hugin.intern
project: shop # Projekt-Slug in Hugin
tools: auto # auto | [claude] | [opencode] | [claude, opencode]
opencode_reads_claude: true # false, wenn OPENCODE_DISABLE_CLAUDE_CODE gesetzt ist
tool_check: warn # warn | strict (bricht bei zu alten Werkzeugen ab)
packages:
- pub-munin-nutzen@stable
- pub-sicherheits-review@^2
- shop-api-release@test
unsubscribed: [] # hier stehen nur Nicht-Pflichtpakete
tools: auto erkennt die Werkzeuge an vorhandenen Dateien (.claude/,
CLAUDE.md, .mcp.json für Claude Code; .opencode/, opencode.json, AGENTS.md
für OpenCode). Gibt es keine Hinweise, erzeugt Hugin für beide. Liegt nur eine
CLAUDE.md im Repo, gilt nur Claude Code; wer beide nutzt, trägt sie dann fest ein.
11.3 hugin.lock (wird eingecheckt)
# Von hugin sync erzeugt. Nicht von Hand ändern.
packages:
pub-munin-nutzen:
version: 1.4.0
checksum: sha256:3f1c…
files:
.claude/skills/pub-munin-nutzen/SKILL.md: sha256:a91e…
.claude/skills/pub-munin-nutzen/referenz/beispiele.md: sha256:77b0…
pub-munin-anbindung:
version: 1.0.0
checksum: sha256:c02d…
keys:
.mcp.json: [mcpServers.munin]
opencode.json: [mcp.munin]
Damit weiß sync:
- welche Dateien und JSON-Schlüssel ihm gehören,
- ob jemand eine verwaltete Datei von Hand geändert hat (Prüfsumme weicht ab),
- was beim Abbestellen zu löschen ist.
11.4 Verwaltete Dateien
-
Markdown-Dateien bekommen einen Kopfkommentar. Bei Dateien mit Frontmatter steht er als YAML-Kommentar in der Frontmatter, damit die Datei gültig bleibt:
--- # Verwaltet von Hugin (pub-munin-nutzen 1.4.0). Änderungen über hugin.intern. name: pub-munin-nutzen description: … --- -
CLAUDE.md: nur der Block zwischenhugin:beginundhugin:end. -
JSON-Dateien: nur die Schlüssel aus
hugin.lock. Reihenfolge und Formatierung der übrigen Inhalte bleiben erhalten (→ prüfen bei JSONC, 7.2). -
Handänderung entdeckt:
synczeigt den Diff und bricht ab. Mit--overwritesetzt es die Datei zurück.
11.5 Ablauf von hugin sync
.hugin.yamllesen, Werkzeuge erkennen, Werkzeugversionen prüfen (Warnung).- Beim Server
POST /api/v1/bundleaufrufen: abonnierte und Pflichtpakete gegen Kanäle und Bereiche → konkrete Versionen, übersetzt für die erkannten Werkzeuge. - Für Versionen, deren Prüfsumme nicht in
hugin.locksteht, die übersetzten Operationen für die erkannten Werkzeuge laden (→ 14.2). Prüfsummen kontrollieren. - Handänderungen an verwalteten Dateien suchen → bei Befund abbrechen.
- Schreiben, Abbestelltes entfernen,
hugin.lockneu schreiben. - Nutzung an den Server melden: Projekt, Repo-Kennung, Werkzeuge mit Versionen,
installierte Paketversionen. Die Repo-Kennung ist die Git-Remote-URL
(
git remote get-url origin), vereinheitlicht: ohne Zugangsdaten, ohne.git, SSH- und HTTPS-Form angeglichen (git.intern/shop/api). Ohne Git-Remote: der Pfad des Repos relativ zum Benutzerordner. - Zusammenfassung ausgeben: neu, aktualisiert, entfernt, Hinweise.
Schlägt Schritt 2 oder 3 fehl (kein Netz), ändert sync nichts. Ein halber Zustand
darf nie entstehen.
12. MCP-Server
Hugin bietet einen MCP-Endpunkt, wie Munin. Agenten in beiden Werkzeugen
erreichen ihn, weil beide MCP-Werkzeuge unterstützen. Er dient der Suche und dem
Rückkanal, nicht der Verteilung. Verteilt wird über hugin sync, weil nur
installierte Skills die eingebaute Skill-Logik der Werkzeuge nutzen.
| Werkzeug | Tut |
|---|---|
search_packages |
„Gibt es schon einen Skill für X?“: sucht in ID und Beschreibung freigegebener Pakete (→ 13, Suche) |
get_package |
liefert den Inhalt einer Version, z. B. damit der Agent einen Skill einmalig nutzt, ohne ihn zu installieren |
report_feedback |
Rückmeldung zu einem Paket: unklar, falsch, veraltet, Verbesserungsvorschlag; mit Version und Werkzeug |
list_subscriptions |
was dieses Projekt abonniert hat und ob es Neues gibt |
Kein Werkzeug zum Schreiben oder Freigeben. Pakete entstehen nur über Autoren und Prüfer.
13. Weboberfläche
Vom Server gerendert mit Jinja2 und HTMX, gestaltet mit dem hauseigenen
CSS-Framework (static/css-framework/). Kein JS-Framework, kein eigener Build-Schritt.
- Seiten bauen auf dem Basis-Template des Frameworks auf (Sidebar-Layout, Topnav, Theme-Umschalter).
- HTMX für Teilaktualisierungen: Filter und Suche im Katalog, Tabs für Diff und
Vorschau, Freigabe-Dialoge, Formularprüfung (422 → Fragment mit Fehlern). Ein
Endpunkt liefert bei
HX-Requestnur das Fragment, sonst die ganze Seite. - Komponenten kommen nur aus dem Framework (
components.css, Tokens austokens.css). Fehlt etwas, wird es im Framework ergänzt, nicht in Hugin. - CSP streng, ohne Inline-Skripte und Inline-Styles. Das Framework muss dafür angepasst werden (→ 21.2).
- Online-Hilfe unter
/help, auch ohne Anmeldung: Themen je Zielgruppe als Markdown, Suche, Kontext-Links von den Arbeitsseiten. Die Tabelle der Linter-Regeln entsteht aus dem Code (hugin_core.linter.RULES).
| Seite | Inhalt |
|---|---|
| Katalog | alle sichtbaren Pakete; Filter nach Art, Geltung, Kompatibilität, Risiko; Suche |
| Paket | Beschreibung, Besitzer, Versionen, Kanäle, Nutzung (wie viele Projekte, welche Versionen), Rückmeldungen |
| Version | Inhalt, Diff zur Vorgängerversion, Vorschau je Werkzeug (die erzeugten Dateien nebeneinander), Linter-Ergebnis, Risiko, Freigaben |
| Prüfungen | Posteingang der Prüfer: was wartet auf mich? |
| Mein Projekt | Abos laut letzter Meldung, veraltete Versionen, Pakete mit neuen Versionen, Werkzeugversionen der Teams |
| Überblick (Tenant) | wie bei Munin: was zu tun ist, z. B. Prüfungen offen, Projekte auf veralteten Versionen, Pakete ohne Nutzung |
| System (Plattform) | wie bei Munin, plus Modellzuordnung, Werkzeug-Untergrenzen, Pflichtpakete |
| Projekte, Keys, Tenants | wie bei Munin |
| Hilfe | für Teams, Autoren, Prüfer, Admins |
Suche (Katalog, hugin search, MCP): einfache Textsuche (LIKE, ohne Groß- und
Kleinschreibung) über ID und Beschreibung, dazu die Filter. Keine Volltextsuche: Der
Katalog bleibt klein, und die Beschreibung ist ohnehin so geschrieben, dass sie
sagt, wofür ein Paket da ist. Reicht das später nicht, lässt sich Volltext im
Store nachrüsten, ohne Schnittstellen zu ändern.
Bearbeiten in der Weboberfläche ist bewusst schlicht: Manifest und Textdateien
direkt editieren; mehrere Dateien als ZIP hochladen. Der bevorzugte Weg für Autoren
ist lokal mit hugin new, hugin validate, hugin try und hugin upload.
14. Architektur
14.1 Überblick
┌──────────────── Entwickler-Rechner ────────────────┐
│ Repo: .hugin.yaml, hugin.lock, erzeugte Dateien │
│ hugin (CLI, Go) ──────┐ │
│ Claude Code / OpenCode ──┐ │
└────────────────────────┼──┼────────────────────────┘
│ │ MCP (suchen, Rückmeldung)
HTTP-API │ │
┌────────────────────────▼──▼────────────────────────┐
│ Hugin-Server (Python, FastAPI) │
│ Weboberfläche (Jinja2, HTMX) · HTTP-API · MCP · │
│ Linter · Übersetzer · Auflösung · Audit · Export │
│ │ │
│ SQLite oder PostgreSQL │
└────────────────────────────────────────────────────┘
14.2 Technik und Aufteilung
| Teil | Technik |
|---|---|
| Server | Python 3.12+, FastAPI, Uvicorn; Projekt und Abhängigkeiten mit uv |
| Modelle, Prüfung | Pydantic v2 (Manifest, API-Schemas) |
| Datenbank | SQLAlchemy 2 + Alembic; SQLite für kleinen Betrieb, PostgreSQL sonst |
| Weboberfläche | Jinja2, HTMX, CSS-Framework (→ 13) |
| MCP | offizielles MCP-Python-SDK, in FastAPI unter /mcp eingehängt |
| CLI | Go, eine Binärdatei ohne Abhängigkeiten für macOS, Linux und Windows |
| Tests | pytest (Server), go test (CLI) |
Übersetzer und Linter nur im Server
Server und CLI sind in verschiedenen Sprachen geschrieben. Linter und Übersetzer gibt es deshalb nur einmal, im Server. Das CLI übersetzt nicht selbst.
| Server | CLI |
|---|---|
| Manifest lesen, Linter, Risiko, Kompatibilität | Werkzeuge und ihre Versionen erkennen |
| Auflösung: Abos + Pflicht + Kanäle → Versionen | .hugin.yaml lesen, hugin.lock schreiben |
| Übersetzer → Liste von Operationen | Operationen anwenden, Handänderungen erkennen |
| Vorschau in der Weboberfläche | Schlüsselbund, Anmeldung |
Ein Übersetzer liefert keine fertigen Repo-Dateien, sondern Operationen, die das CLI anwendet. So bleiben Dateien der Teams unberührt, auch wenn der Server sie nie sieht:
| Operation | Bedeutung | Beispiel |
|---|---|---|
file |
Datei mit diesem Inhalt anlegen oder ersetzen | .claude/skills/pub-munin-nutzen/SKILL.md |
block |
verwalteten Block in einer Datei setzen | hugin-Block in CLAUDE.md |
json |
Schlüssel in einer JSON-Datei setzen | mcpServers.munin in .mcp.json |
Folge: hugin validate und hugin try brauchen den Server (POST /api/v1/validate,
→ 14.6). Für ein internes Werkzeug ist das vertretbar; hugin sync mit
unveränderter hugin.lock arbeitet wie bisher ohne Übersetzung.
Verhältnis zu Munin
Munin ist in Go geschrieben; Code lässt sich nicht übernehmen. Übernommen werden die Konzepte: Tenants (in Munin: Workspaces), Projekte, Keys mit Rollen, Audit, Hilfe-System mit Zielgruppen, Überblicks- und Systemseite, Markdown-Export. Sie werden in Python neu gebaut.
14.3 Paketstruktur und Einbettung
hugin/
├── server/ # Python, ein uv-Projekt
│ ├── pyproject.toml
│ ├── src/
│ │ ├── hugin_core/ # ohne FastAPI und DB, einzeln nutzbar
│ │ │ ├── package/ # Quellformat: Manifest (Pydantic), Ordner ↔ Version
│ │ │ ├── linter/ # Regeln H001 ff.
│ │ │ ├── translators/ # je Ziel und Art; reine Funktionen → Operationen
│ │ │ │ ├── claude/
│ │ │ │ └── opencode/
│ │ │ └── resolution/ # Abos + Pflicht + Kanäle → konkrete Versionen
│ │ └── hugin_app/ # FastAPI-Schicht
│ │ ├── store/ # SQLAlchemy-Modelle und Zugriffe
│ │ ├── auth/ # Keys, Personen, Rollen, Sitzungen, CSRF
│ │ ├── audit/
│ │ ├── api/ # /api/v1
│ │ ├── mcp/ # MCP-Werkzeuge
│ │ ├── web/ # Routen der Weboberfläche
│ │ ├── docs/ # Online-Hilfe
│ │ ├── export/
│ │ ├── migrations/ # Alembic
│ │ ├── templates/
│ │ └── static/ # CSS-Framework (aus dist/), htmx
│ └── tests/
├── cli/ # Go
│ ├── cmd/hugin/
│ └── internal/
│ ├── api/ # Client für /api/v1
│ ├── reconcile/ # Operationen anwenden, hugin.lock
│ ├── tools/ # Erkennung, Versionsprüfung
│ ├── keystore/ # Schlüsselbund des Betriebssystems
│ └── importer/ # hugin import
├── static/css-framework/ # Quelle des CSS-Frameworks
└── scripts/
hugin_core darf nichts aus hugin_app importieren (geprüft mit import-linter).
Einbettung in ein anderes Projekt
Hugin soll später in ein anderes FastAPI-Projekt eingebaut werden können. Dafür:
- Kern einzeln nutzbar:
hugin_corehat keine Abhängigkeit zu FastAPI oder DB. - Router statt fester App:
hugin_appstellt Router bereit (api_router(),web_router(),mcp_app()). Die eigenständige App setzt sie nur zusammen; ein anderes Projekt hängt sie mitinclude_routerunter einem Präfix ein. - Auth austauschbar: Die Router fragen Identität und Rollen über eine Schnittstelle ab (FastAPI-Dependency). Hugin bringt die Umsetzung mit Keys und Personen mit (→ 14.5); ein Gastprojekt kann seine eigene einsetzen.
- Datenbank teilbar: Tabellen mit Präfix
hugin_oder eigenes PostgreSQL-Schema, eigene Alembic-Versionstabelle. Die Sitzung kommt als Dependency vom Gastprojekt. - Templates überschreibbar: Jinja2 sucht zuerst in den Templates des
Gastprojekts, dann in denen von Hugin; das Layout (
base.html) lässt sich so ersetzen. - Pfade nur über
url_for, nie fest im Template, damit ein Präfix funktioniert.
14.4 Rollen
| Rolle | Hängt an | Darf |
|---|---|---|
read |
Projekt-Key | Katalog, sync, Nutzung melden, Rückmeldung geben; typisch für das CLI und die Agenten |
author |
Person (ab Stufe 4), vorher Projekt-Key | Entwürfe für Pakete seines Projekts anlegen und einreichen |
reviewer |
Person (ab Stufe 4), vorher Projekt-Key | Versionen in seiner Geltung freigeben oder zurückgeben, nicht die eigenen |
| Tenant-Admin | bis Stufe 4 Tenant-Key (ht_), danach Person |
Projekte und Keys im Tenant anlegen, Pflichtpakete im Tenant, ab Stufe 4 Personen anlegen und Prüfer benennen |
| Plattform-Admin | wie bei Munin | Tenants anlegen, Modellzuordnung, Untergrenzen, öffentliche Pflichtpakete, Prüfer für öffentliche Pakete |
14.5 Identität: Maschinen und Personen
Das Problem
In Munin hängt der Zugang an Keys, und ein Key gehört zu einem Projekt, nicht zu einem Menschen. Den Projekt-Key nutzen der Agent und oft mehrere Leute im Team gemeinsam. Munin weiß also, mit welchem Key etwas geschah, aber nicht, welcher Mensch es war. Für Wissenseinträge reicht das: Ist etwas falsch, korrigiert man es.
Bei Hugin reicht es nicht. Ein Paket ist Lieferkette (→ 16), und die Freigabe ist der eigentliche Schutz. Sie muss zwei Dinge leisten:
- Vier Augen: Der Autor darf nicht selbst freigeben. Bei riskanten Paketen stimmen zwei verschiedene Menschen zu.
- Verantwortung: Im Audit steht „freigegeben von Anna Schmidt am 12.11.“, nicht
„freigegeben mit Key
shop-pruefer“. Fragt die Revision, wer ein Skript in 50 Repos verteilt hat, braucht es einen Namen.
Mit Projekt-Keys geht beides nicht sicher:
- Hat Anna den Autor- und den Prüfer-Key, reicht sie ein und gibt sich selbst frei.
- Teilen sich drei Leute einen Prüfer-Key, zählen zwei Freigaben als vier Augen, obwohl es ein Mensch gewesen sein kann.
Wer Person sein muss
| Handlung | Wer | Identität |
|---|---|---|
hugin sync, Suche, Nutzung melden |
CLI, Agent | Projekt-Key genügt |
| Rückmeldung zu einem Paket | Agent oder Mensch | Projekt-Key genügt; Quelle wird vermerkt |
| Entwurf hochladen, einreichen | Autor | Person |
| Freigeben, zurückgeben | Prüfer | Person |
| Pflichtpakete, Prüfer benennen, Personen verwalten | Tenant-Admin | Person |
| Modellzuordnung, Untergrenzen, öffentliche Pflichtpakete | Plattform-Admin | HUGIN_ADMIN_TOKEN (→ 16.3), im Audit als „Plattform-Admin“ |
Grundsatz: Maschinen nutzen Projekt-Keys, Menschen in der Freigabekette eine eigene Identität.
Möglichkeiten
| Persönliche Keys | SSO (OIDC: Keycloak, Entra ID) | |
|---|---|---|
| Wie | Tenant-Admin legt Personen mit Name und Mail an; jede bekommt einen eigenen Key (hp_…) für Web und CLI |
Anmeldung mit dem Firmenkonto; Gruppen aus dem Verzeichnis werden zu Rollen |
| Vorteile | keine neue Infrastruktur, Muster wie Munin, schnell gebaut | echte Identität, Austritte wirken automatisch, keine Keys für Menschen |
| Nachteile | Identität nur so gut wie die Vergabe; weitergegebene Keys hebeln sie aus; Austritte von Hand sperren | Abhängigkeit vom Identity Provider (IdP), Abstimmung mit der IT; für Munin bewusst zurückgestellt |
Entscheidung
- Stufe 1–3: ohne Personen. Eine Freigabe; Autor- und Prüfer-Key werden
getrennt vergeben. Die Schwäche steht offen in der Doku. Das genügt für den
Start mit wenigen Paketen vom Risiko
text, etwapub-munin-nutzen. Pakete mit Risikoscriptsoderpermissionsnimmt Hugin bis Stufe 4 nicht an. - Stufe 4: persönliche Keys.
- Rollen
authorundreviewerhängen an der Person, je Projekt oder Tenant. - Vier Augen werden erzwungen: Prüfer ≠ Autor; bei
scriptsundpermissionszwei verschiedene Personen. - Im Audit stehen Namen.
- Persönliche Keys laufen ab (Standard 180 Tage) und tauchen wie bei Munin im Überblick auf („läuft bald ab“, „seit 90 Tagen ungenutzt“). So fallen vergessene Keys ausgeschiedener Personen auf.
- Wer eine Person sperrt, behält ihre bisherigen Freigaben im Audit; nur neue Handlungen sind ausgeschlossen.
- Rollen
- Später: SSO als zweiter Anmeldeweg für dieselben Personen, als gemeinsames Thema mit Munin. Munin hätte auch etwas davon: Im Audit stünde dann, wer in der Weboberfläche einen Eintrag geändert hat.
Damit SSO später ohne Umbau andockt, gibt es die Tabelle people (→ 15)
von Stufe 1 an, mit einem Feld external_id für die spätere Kennung aus dem IdP.
Bis Stufe 4 bleibt sie leer.
Anmeldung mit persönlichem Key
- Weboberfläche: Anmeldeseite wie bei Munin. Die Sitzung trägt die Person; alle Handlungen im Audit nennen sie.
- CLI:
hugin loginspeichert den persönlichen Key im Schlüsselbund.hugin uploadnutzt ihn;hugin syncnutzt weiterhin den Projekt-Key ausHUGIN_KEYbzw. dem Schlüsselbund-Eintrag des Projekts. - Agenten bekommen nie einen persönlichen Key. Ein Agent kann über MCP keine Entwürfe einreichen oder freigeben (→ 12).
14.6 HTTP-API für das CLI
Schlicht, JSON, versioniert unter /api/v1/:
| Methode und Pfad | Zweck |
|---|---|
POST /api/v1/bundle |
Abos auflösen und ausliefern: ZIP mit hugin-bundle.json und Dateien, gleiches Format wie 14.7 |
GET /api/v1/packages/{id}/versions/{v} |
Version im Quellformat als ZIP (für Autoren und Export) |
POST /api/v1/validate |
Paketordner prüfen und übersetzen, ohne zu speichern (hugin validate) |
POST /api/v1/trial?tools=claude:2.1.302,opencode:1.19.3 |
Bundle für einen ungespeicherten Paketordner (hugin try) |
GET /api/v1/config |
Modellzuordnung, Untergrenzen, Pflichtpakete |
POST /api/v1/usage |
Meldung von sync |
GET /api/v1/search?q= |
Katalogsuche |
POST /api/v1/drafts |
Upload eines Entwurfs (Rolle author) |
Authentifizierung über Authorization: Bearer <key> (→ 16.3). Lesende
Endpunkte und usage nehmen Projekt-Keys; drafts verlangt ab Stufe 4 einen
persönlichen Key (→ 14.5).
14.7 Auslieferung für Integrationen
Eine weitere Integration holt Pakete ohne das Go-CLI ab: ein PowerShell-Skript unter Windows. Windows PowerShell 5.1 ist vorausgesetzt, PowerShell 7 ist nicht verfügbar. Umsetzung nach dem CLI (→ 20, Stufe 2).
Präfix und Key
- Alle Endpunkte unter
/api/v1/delivery/. - Eigener Integrations-Key (
hi_…, → 16.3), neben Projekt-Key und persönlichem Key: gilt für genau einen Tenant, optional auf Projekte eingeschränkt; Rolledelivery(nur lesen, ausliefern, Nutzung melden; kein Hochladen, keine Freigabe, kein MCP); einzeln widerrufbar, läuft ab; im Audit mit dem Namen der Integration; mit Rate-Limit.
Endpunkte
| Methode und Pfad | Zweck |
|---|---|
GET /status |
Dienst ok, Hugin-Version, Gültigkeit und Geltung des Keys, Werkzeug-Untergrenzen |
GET /packages |
Übersicht der freigegebenen Pakete: Art, aktuelle Version je Kanal, Kompatibilität, Risiko; Filter kind, channel, changed_since |
GET /packages/{id} |
Details und Versionen |
POST /bundle |
Auslieferung als ZIP (siehe unten) |
POST /usage |
Meldung nach der Installation, optional (→ 11.5, Schritt 7) |
GET /client.ps1 |
Referenz-Skript für die Installation, von Hugin gepflegt |
POST /resolve |
Auflösen ohne Übersetzung, für Plattformen (siehe unten) |
GET /packages/{id}/versions/{version}/source |
Eine lieferbare Version im Quellformat als ZIP |
POST /feedback |
Rückmeldung zu einem Paket, wie report_feedback über MCP |
Plattformen als Abnehmer
Neben Claude Code, OpenCode und dem PowerShell-Skript nutzt eine Plattform Hugin, die Pakete selbst abbildet, statt Dateien für ein Werkzeug zu schreiben: der AI Workspace Bifröst (Regeln in den System-Prompt, Skills über eigene Tools, Befehle als Slash-Befehle im Chat). Sie braucht keine übersetzten Bundles, sondern:
POST /resolve: gleiche Anfrage wie/bundleohnetoolsundoptions(packages,unsubscribed,mandatory,major,installed). Antwort je Paket:id,kind,version,checksum,risk,channel,mandatory,deprecated,successor,description,arguments; dazumajor_available. Gleiche Auflösung wie beim Bundle (→ 9.3), Pflichtpakete eingeschlossen.GET /packages/{id}/versions/{version}/source:hugin.yamlund Inhaltsdateien als ZIP (wie der Download, → 14.6), deterministisch; KopfzeileX-Hugin-Checksummit der Prüfsumme der Version. Die Plattform schreibt Version und Prüfsumme fest und prüft beim Laden ihre gespeicherte Kopie.POST /feedback:package,version,kind(unclear,wrong,deprecated,suggestion),text,client(z. B.ai-workspace). Quelleagent, im Tenant des Keys; der Agent der Plattform hat keinen Projekt-Key und damit kein MCP.- Nutzung meldet die Plattform wie andere Integrationen über
POST /usage, mit dem Client-Namenai-workspace/<workspace>alsrepo.
Was die Plattform aus Risiko und Art macht (z. B. keine Skills mit Skripten), entscheidet sie selbst; Hugin-Inhalte verleihen dort keine Rechte.
Das Format ist verbindlich in contract/ festgelegt; Server und Clients
testen gegen dieselben Dateien:
| Datei | Inhalt |
|---|---|
contract/bundle-request.schema.json |
Anfrage an /bundle |
contract/hugin-bundle.schema.json |
hugin-bundle.json im ZIP |
contract/examples/packages/ |
Beispielpakete im Quellformat |
contract/examples/bundles/ |
goldene Bundles, vom Server erzeugt (pytest --update), vom CLI angewendet |
Anfrage an /bundle:
{ "packages": ["pub-munin-nutzen@stable", "pub-sicherheits-review@^2"],
"tools": { "claude": "2.1.302", "opencode": "1.19.3" },
"mandatory": true,
"installed": { "pub-munin-nutzen": "1.3.0" } }
Aus den Werkzeugversionen leitet der Server die Ziele ab (claude, opencode@1,
opencode@2). Aus installed berechnet er, was zu entfernen ist. ETag und
If-None-Match → 304, wenn sich nichts geändert hat.
Inhalt des ZIP
hugin-bundle.json # Installationsanleitung
files/.claude/skills/pub-munin-nutzen/SKILL.md
files/…
Ein Bundle beschreibt den vollständigen Soll-Zustand der angefragten Pakete. Die Operationen werden der Reihe nach angewendet:
| Operation | Bedeutung |
|---|---|
remove |
Datei entfernen, die eine frühere Version angelegt hat |
json_remove |
JSON-Schlüssel bzw. Listeneintrag entfernen |
file |
Datei aus files/ schreiben, mit Prüfsumme |
json |
JSON-Schlüssel setzen (mode: set) oder Eintrag in einer Liste sicherstellen (mode: list_item); Schlüssel als Liste, z. B. ["mcpServers", "munin"] |
block |
verwalteten Block setzen; leerer content entfernt ihn |
{
"format": 1,
"generated": "2026-10-18T12:00:00Z",
"tools": ["claude", "opencode@1"],
"packages": [{ "id": "pub-munin-nutzen", "version": "1.4.0", "checksum": "sha256:…",
"kind": "skill", "risk": "text", "compatibility": "portable", "mandatory": false }],
"operations": [
{ "op": "file", "package": "pub-munin-nutzen", "path": ".claude/skills/pub-munin-nutzen/SKILL.md",
"source": "files/.claude/skills/pub-munin-nutzen/SKILL.md", "sha256": "sha256:…" },
{ "op": "json", "package": "pub-munin-anbindung", "file": ".mcp.json",
"key": ["mcpServers", "munin"], "value": { "…": "…" } },
{ "op": "json", "file": "opencode.json", "key": ["instructions"],
"mode": "list_item", "value": ".hugin/rules/*.md" },
{ "op": "block", "file": "CLAUDE.md", "block": "hugin",
"content": "@.hugin/rules/pub-hausregeln.md\n" }
],
"hints": [
{ "kind": "env_variable", "name": "MUNIN_KEY", "package": "pub-munin-anbindung" }
]
}
package fehlt bei gemeinsamen Einträgen, die Hugin für mehrere Pakete pflegt.
create (optional bei json und block) verweist auf eine fertige Datei für
den Fall, dass die Zieldatei noch nicht existiert.
Vorgaben für PowerShell 5.1
| Punkt | Vorgabe |
|---|---|
| Encoding | UTF-8 ohne BOM schreiben: [IO.File]::WriteAllText($pfad, $text, (New-Object Text.UTF8Encoding $false)). Nie Set-Content/Out-File (schreiben in 5.1 nicht UTF-8 ohne BOM) |
| Zeilenenden | LF wie geliefert, nicht nach CRLF wandeln |
| JSON | ConvertTo-Json immer mit -Depth 20 (sonst still abgeschnitten); formatiert die Datei um und verliert Kommentare. Deshalb json nur für Dateien ohne Kommentare, sonst Abbruch mit Hinweis |
| Prüfsummen | Get-FileHash -Algorithm SHA256 gegen sha256 im Bundle |
| ZIP | Expand-Archive in einen leeren Temp-Ordner, erst nach erfolgreicher Prüfung kopieren |
| TLS | vor dem ersten Aufruf [Net.ServicePointManager]::SecurityProtocol = 'Tls12' |
| HTTP | Invoke-RestMethod/Invoke-WebRequest mit -UseBasicParsing |
| Zustand | eigene Lock-Datei wie hugin.lock (→ 11.3), damit Handänderungen erkannt und Abbestelltes entfernt werden kann |
Das Format hugin-bundle.json ist versioniert (format). Das Go-CLI nutzt
denselben Endpunkt /bundle, damit es nur einen Auslieferungsweg gibt.
15. Datenmodell
Eine Datenbank (SQLite oder PostgreSQL) für alle Tenants. Jede Tabelle mit
Tenant-Bezug hat eine Spalte tenant_id. Öffentliche Pakete haben tenant_id = NULL.
Bei Einbettung in ein anderes Projekt tragen alle Tabellen das Präfix hugin_
(→ 14.3); unten ist es weggelassen.
Plattform
| Tabelle | Wichtige Spalten |
|---|---|
tenants |
id, slug (kurz, Präfix für Paket-IDs), name, locked_since, created |
projects |
id, tenant_id, slug, name, created |
keys |
id, kind (project / integration / tenant / person), tenant_id, project_id (bei project), person_id (bei person), name, roles, projects (Einschränkung bei integration, leer = alle), key_hash, prefix (erste Zeichen zur Anzeige), expires_at, last_used, revoked_since, created, created_by |
settings |
key, value (JSON): Modellzuordnung, Untergrenzen der Werkzeuge |
sessions |
id (SHA-256 des Cookie-Tokens), key_id (leer = Plattform-Admin), csrf, created, expires_at, revoked_since, ip, admin_fingerprint (Admin-Sitzung endet, wenn HUGIN_ADMIN_TOKEN wechselt) — Sitzungen der Weboberfläche, serverseitig widerrufbar (→ 16.3) |
people |
id, tenant_id, name, mail, external_id (leer bis SSO), locked_since, created, created_by |
people_roles |
person_id, role (author, reviewer, tenant_admin), tenant_id, project_id (leer = ganzer Tenant) |
people und people_roles werden von Stufe 1 an angelegt, aber erst ab
Stufe 4 genutzt (→ 14.5). Persönliche Keys stehen in keys mit kind = person.
Pakete
| Tabelle | Wichtige Spalten |
|---|---|
packages |
id (Text, z. B. pub-munin-nutzen), tenant_id (NULL = öffentlich), kind, scope, owner_project, deprecated_since, successor, created |
mandatory_packages |
package_id, tenant_id (NULL = in allen Tenants), set_by, time |
versions |
package_id, version, status, channel, manifest (JSON), checksum, risk, compatibility, min_version_claude, min_version_opencode, linter (JSON), author_person, author_key, created, submitted, approved |
contents |
checksum (Schlüssel), content (BLOB), size |
files |
version_id, path, checksum → contents |
approvals |
version_id, reviewer_person, reviewer_key, decision (approve / return), comment, time |
contents speichert jeden Dateiinhalt nur einmal; unveränderte Hilfsdateien kosten
bei einer neuen Version keinen Platz.
…_person ist bis Stufe 4 leer, dann steht dort nur der Key. Ab Stufe 4 ist
…_person Pflicht; die Regeln „Prüfer ≠ Autor“ und „zwei verschiedene Personen“
werden über diese Spalten geprüft.
Paket-IDs sind über alle Tenants eindeutig. Das ergibt sich aus dem Präfix (→ 10.4) und wird zusätzlich per Unique-Index erzwungen.
Rückkanal und Audit
| Tabelle | Wichtige Spalten |
|---|---|
usage |
tenant_id, project_id, key_id, repo (Kennung aus Git-Remote, Pfad oder Client-Name), package_id, version, tools (JSON: Werkzeug → Version), last |
feedbacks |
tenant_id, package_id, version, source (agent / human), tool, kind, text, status, time |
audit_events |
id, time, tenant_id (NULL = Plattform), actor_kind (person / key / admin), actor_id, action (z. B. version.approved), target_kind, target_id, data (JSON), ip |
Rückmeldungen zu einem öffentlichen Paket tragen den Tenant der Meldenden. Der Besitzer des Pakets sieht den Text, aber nicht, aus welchem Tenant er kommt; der Plattform-Admin sieht beides.
Versionen und Dateien werden nie geändert oder gelöscht, nur ihr Status wechselt. Ausnahme: Entwürfe, die nie eingereicht wurden.
15.1 Trennung der Tenants
Mit einer gemeinsamen Datenbank hängt die Trennung allein am Code. Deshalb:
- Ein Zugriffsweg: Routen greifen nie direkt auf Tabellen zu, sondern über die
Store-Schicht. Jede Funktion dort verlangt einen
Access(Tenant, Projekt, Rollen) und filtert selbst:tenant_id = :tenant OR tenant_id IS NULLfür Pakete,tenant_id = :tenantfür alles andere. - Der Zugriff kommt aus dem Key oder der Sitzung, nie aus einem Parameter der Anfrage. Ein Tenant-Slug in der URL dient nur der Anzeige und wird gegen den Zugriff geprüft.
- Fremde Objekte gibt es nicht: Eine ID aus einem anderen Tenant liefert
404, nicht403, damit sich nicht erraten lässt, was existiert. - Öffentliche Pakete sind für Tenants nur lesbar. Ändern, Freigeben und Pflicht setzen darf nur, wer öffentliche Pakete pflegt (→ 10.1, 14.4).
- Optional in PostgreSQL: Row-Level-Security als zweite Sicherung; die
Anwendung setzt dazu je Anfrage
SET LOCAL hugin.tenant_id. - Test: Eine Testsuite legt zwei Tenants mit gleich benannten Inhalten an und
ruft jeden Endpunkt (Web, API,
/bundle, MCP, Export) mit dem Key des einen auf. Nichts aus dem anderen darf auftauchen. Neue Endpunkte werden automatisch erfasst (Routen aus der FastAPI-App gelesen); ein Endpunkt ohne Testfall lässt den Test fehlschlagen.
16. Sicherheit
Ein Paket ist Lieferkette: Wer ein weit verbreitetes Paket ändern kann, steuert viele Agenten in vielen Repositories.
16.1 Risiko-Einstufung (automatisch)
| Risiko | Wann |
|---|---|
text |
nur Anweisungen, keine Skripte, keine Rechte |
tools |
verweist auf MCP-Werkzeuge oder enthält !`befehl` in Befehlen |
scripts |
enthält ausführbare Dateien oder ist ein Hook |
permissions |
gewährt Rechte: allowed-tools, permission mit allow, Fähigkeitsklasse shell oder write |
Das höchste zutreffende Risiko gilt. Es bestimmt die Zahl der Freigaben (→ 9.2).
16.2 Maßnahmen
- Prüfsummen auf Version und Datei;
synclehnt bei Abweichung ab. - Unveränderliche Versionen. Was freigegeben ist, kann nicht still ersetzt werden.
- Audit aller Statuswechsel, Freigaben, Pflicht-Markierungen und Konfigurationsänderungen.
- Keine Geheimnisse in Paketen (Linter
H031,H032). - Skripte werden in der Weboberfläche vollständig angezeigt, nie nur verlinkt.
- Nur verwaltete Dateien:
syncschreibt nur unter den bekannten Pfaden und nur Schlüssel aushugin.lock. Pfade im Paket werden gegen „aus dem Repo ausbrechen“ geprüft (.., absolute Pfade, Symlinks und unter Windows Junctions, nichts unter.git/). Dateinamen, die nicht auf allen Systemen gehen (Punkt oder Leerzeichen am Ende,<>:"|?*,CON,NUL…), lehnt schon der Upload ab. - Uploads begrenzt: Request-Bodies bis
HUGIN_MAX_UPLOAD_MB(Standard 40), geprüft vor der Anmeldung; Pakete bis 32 MB entpackt, 4 MB je Datei, 500 Dateien. - Key nur an den eigenen Server: Ist
HUGIN_SERVERgesetzt, sendet das CLI einen Key ausHUGIN_KEYnur an diesen Server, auch wenn ein Pull Requestserver:in.hugin.yamländert. In CI daher beide Variablen setzen. Im Schlüsselbund ist der Key ohnehin an die Adresse gebunden. - Prompt-Injection im Inhalt: Prüfer sehen den Text, wie der Agent ihn sieht. Hinweis im Linter bei auffälligen Mustern, z. B. „ignoriere vorherige Anweisungen“ oder versteckte Unicode-Zeichen.
- Später: Signatur der Versionen, damit das CLI auch einem kompromittierten Server nicht blind vertraut.
16.3 Keys, Anmeldung, Sitzungen
| Art | Präfix | Gehört zu | Zweck |
|---|---|---|---|
| Projekt-Key | hk_ |
Projekt eines Tenants | CLI (sync, check), Agenten über MCP |
| Tenant-Key | ht_ |
Tenant | Verwaltung des Tenants (Rolle tenant_admin) bis Stufe 4; vergibt nur der Plattform-Admin; danach durch Personen ersetzt |
| Integrations-Key | hi_ |
Tenant, optional auf Projekte eingeschränkt | Auslieferung an Integrationen (→ 14.7), nur unter /api/v1/delivery/; übrige API, MCP und Weboberfläche lehnen ihn ab |
| Persönlicher Key | hp_ |
Person (ab Stufe 4) | Web und CLI für Autoren, Prüfer, Admins |
| Admin-Token | – | Plattform | Umgebungsvariable HUGIN_ADMIN_TOKEN; nur für die Plattform-Verwaltung |
- Format: Präfix + 32 Byte Zufall, Base32 ohne Füllzeichen. Der Key wird nur einmal beim Anlegen angezeigt.
- Speicherung: nur SHA-256 des Keys (Keys sind lang und zufällig, ein langsamer Hash ist nicht nötig) plus die ersten acht Zeichen zur Anzeige. Vergleich in konstanter Zeit.
- Übertragung:
Authorization: Bearer <key>, nie in der URL. - Ablauf: persönliche Keys 180 Tage, Integrations-Keys 365 Tage, Projekt-Keys ohne Ablauf, aber mit Hinweis im Überblick nach 90 Tagen ohne Nutzung.
- Weboberfläche: Anmeldung mit persönlichem Key (bis Stufe 4: mit Projekt-Key
der Rolle
authoroderreviewer). Danach eine Sitzung als signiertes,HttpOnly-,Secure-,SameSite=Lax-Cookie, 8 Stunden gültig, serverseitig widerrufbar. - CSRF: Token je Sitzung in jedem Formular und als Header
X-CSRF-Tokenfür HTMX-Anfragen (überhx-headersim Basis-Template). Die API mit Bearer-Key braucht kein CSRF-Token. - Rate-Limit je Key bzw. Sitzung (
HUGIN_RATE_LIMIT, Standard 600 je Minute); nachHUGIN_FAILED_ATTEMPTSfalschen Keys (Standard 20 in 5 Minuten) antwortet Hugin der IP mit 429, für API, Anmeldung und MCP. Gezählt wird je Server-Prozess; bei mehreren Pods gilt die Grenze je Pod. - Client-IP hinter dem Router:
X-Forwarded-Forgilt nur von Adressen inHUGIN_PROXY_IPS(Standard127.0.0.1). In OpenShift auf das Netz des Routers setzen, z. B.10.128.0.0/14; sonst sehen Sperre und Audit nur die IP des Routers, und 20 falsche Keys sperren alle.*nur, wenn der Pod ausschließlich über den Router erreichbar ist, denn sonst kann jeder seine IP vorgeben. - Admin-Sitzungen sind an das Admin-Token gebunden: Nach einem Wechsel von
HUGIN_ADMIN_TOKENgelten bestehende Sitzungen sofort nicht mehr. - Nur verschlüsselt: Das CLI sendet Keys nur über
https://oder an den eigenen Rechner, auch bei Weiterleitungen; zum TestenHUGIN_ALLOW_HTTP=1. Der PowerShell-Client warnt beihttp://. - Kein Browser-Cache: Antworten außer
/static/mitCache-Control: no-store(neue Keys erscheinen einmal auf einer Seite).
16.4 Content Security Policy
Strenge CSP ohne 'unsafe-inline' und ohne 'unsafe-eval', gesetzt per Middleware
für jede Antwort der Weboberfläche:
default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;
connect-src 'self'; form-action 'self'; frame-ancestors 'none'; base-uri 'self';
object-src 'none'
Nonces reichen nicht, weil sie bei style-Attributen nicht helfen. Deshalb wird
das CSS-Framework CSP-fest gemacht. Das kommt allen Projekten zugute, die es nutzen.
| Heute im Framework | Künftig |
|---|---|
onclick="toggleSidebar()" usw. |
data-action="toggle-sidebar" (+ data-target); ein gemeinsamer Listener in framework.js per Event-Delegation, funktioniert auch für Inhalte, die HTMX nachlädt |
Inline-Skript für das Theme im <head> |
eigene Datei theme-init.js, im <head> ohne defer geladen (kein Aufflackern) |
Toast: innerHTML mit style="color: …" |
Aufbau per DOM, Farbe über Klasse toast-icon-{type}, Text über textContent (behebt zugleich eine XSS-Lücke) |
el.style.… für Animationen |
unter CSP erlaubt, sauberer als Klasse (.toast-leaving) |
style="…" in Beispielen und Templates |
Utility-Klassen (z. B. min-w-0, Größen für Skeletons) |
HTMX wird so konfiguriert:
<meta name="htmx-config"
content='{"includeIndicatorStyles": false, "allowEval": false, "allowScriptTags": false}'>
Die Styles für .htmx-indicator kommen aus dem Framework. hx-on wird nicht
verwendet (braucht eval).
17. Kompatibilitätstests
Beide Werkzeuge lassen sich ohne Oberfläche starten (claude -p …, opencode run …).
Zwei Ebenen, beide als manuelle Routine (es gibt keine CI):
17.1 Übersetzer-Tests (pytest, schnell, immer)
Tabellentests je Art und Ziel: Manifest + Inhalt → erwartete Dateien.
Goldene Bundles unter contract/examples/bundles/, erzeugt aus den Beispielpaketen in
contract/examples/packages/. Teil von pytest; die CLI-Tests wenden dieselben Bundles an.
17.2 Rauchtests mit den echten Werkzeugen (vor Releases)
Das Skript scripts/tool-smoke-test.sh startet einen Hugin, veröffentlicht Testpakete
mit Erkennungswörtern (scripts/tool-smoke/packages/), installiert sie per hugin sync
in ein leeres Test-Repo und fragt beide Werkzeuge. hugin sync statt hugin try, weil
try nur ein Paket auf einmal installiert. Die Modellaufrufe laufen ohne Lesewerkzeuge,
damit die Antwort aus dem geladenen Kontext kommt und nicht aus den Dateien im Repo:
| Prüfung | Wie |
|---|---|
| Skill wird gefunden | Werkzeug nach der Liste seiner Skills fragen, Name muss vorkommen |
| Skill wird geladen | Beispiel-Anfrage aus dem Paket, Ausgabe auf Erkennungswort prüfen |
| Befehl mit Argumenten | Befehl mit zwei Argumenten aufrufen, beide müssen richtig ankommen |
| Subagent vorhanden | Werkzeug nach seinen Agenten fragen |
| Regeln wirken | Frage, deren Antwort nur in den Regeln steht |
| MCP eingetragen | claude mcp get, opencode mcp list |
| MCP verbunden | Claude: Werkzeug search_packages des Test-Hugin aufrufen; OpenCode: Status connected |
Das Ergebnis ist eine Tabelle Paket × Werkzeug × Version. Es landet als Markdown in
COMPATIBILITY.md und wird vor jedem Hugin-Release und nach jedem größeren
Update eines Werkzeugs neu erzeugt.
Die Rauchtests brauchen Zugänge zu beiden Werkzeugen und kosten Tokens. Sie
gehören deshalb nicht in pytest oder go test.
18. Betrieb
Plattform: OpenShift. Deployment, Ingress, Datenbank und Sicherung der Datenbank verantwortet der Betrieb; sie sind nicht Teil dieses Konzepts. Hugin liefert dafür:
- ein Container-Image ohne Zustand im Dateisystem (alles in der Datenbank), lauffähig als beliebiger Benutzer ohne Root (OpenShift-Standard),
- Konfiguration nur über Umgebungsvariablen (
HUGIN_DATABASE_URL,HUGIN_ADMIN_TOKEN,HUGIN_BASE_URL,HUGIN_DOWNLOAD_DIR…), - Health-Endpunkte
/healthz(lebt) und/readyz(Datenbank erreichbar, Migrationen aktuell) für die Probes, - Migrationen als eigener Schritt (
hugin-server migrate), z. B. als Job vor dem Rollout, nicht beim Start jedes Pods, - Logs als JSON auf stdout.
Ergänzend:
- Sicherung: Die Dateien liegen als BLOB in der Datenbank; deren Sicherung (Betrieb) ist also vollständig.
- Export:
hugin-server export <ordner> [--commit], z. B. als CronJob in einen Git-Ordner; auch als ZIP auf der Systemseite. - Export: täglich alle freigegebenen Versionen im Quellformat
(
public/<package>/<version>/…bzw.<tenant-slug>/<package>/<version>/…). Ein Git-Repository darauf ergibt eine lesbare Historie und eine Notfall-Verteilung: Im Notfall kann man Pakete von Hand aus dem Export kopieren. - Systemseite: zusätzlich offene Prüfungen, Untergrenzen der Werkzeuge, Verteilung der Werkzeugversionen über alle Projekte (wer ist zu alt?).
- Ausfall: Fällt Hugin aus, laufen alle Agenten weiter, denn die Dateien liegen
im Repo. Nur
syncgeht nicht. Hugin ist deshalb nicht kritisch im Betrieb, anders als Munin. - Versionen des CLI:
scripts/build-cli.sh <version> <download-ordner>baut für macOS, Linux und Windows; der Server bietet die neueste Version auf der Seite „Programm hugin“ an (HUGIN_DOWNLOAD_DIR).hugin checkmeldet ein CLI älter alsHUGIN_CLI_MIN_VERSIONals Befund und weist auf neuere Versionen hin.
19. Zusammenspiel mit Munin
- Erstes Paket:
pub-munin-nutzen(Skill), dazupub-munin-anbindung(MCP), beide als Pflichtpakete. Damit nutzen alle Agenten Munin einheitlich und richtig. Das ist der direkteste Hebel auf die Wissensqualität in Munin. - Gleiche Tenants und Projekte: Ein Hugin-Tenant entspricht einem Munin-Workspace, mit denselben Slugs. In der ersten Fassung werden sie getrennt gepflegt. Ein gemeinsamer Identitäts- und Key-Dienst ist ein eigenes, späteres Thema.
- Rückmeldungen zu
pub-munin-nutzenzeigen, wo Agenten Munin falsch verstehen. Daraus entstehen Verbesserungen an Munin selbst (Werkzeugbeschreibungen, Hilfe). - Abgrenzung im Alltag:
Gehört nach Munin Gehört nach Hugin „Die Staging-DB wird jeden Sonntag zurückgesetzt.“ „Bevor du eine Migration schreibst, prüfe in Munin, ob es Besonderheiten der DB gibt.“ Fakten, Erkenntnisse, Entscheidungen Arbeitsweisen, Abläufe, Rollen, Werkzeug-Anbindungen
20. Ausbaustufen
Jede Stufe endet mit etwas Nutzbarem und einer Abnahme.
Umsetzungsstand (18.10.2026):
| Stufe | Stand |
|---|---|
| 0 Machbarkeit | für OpenCode 1 geklärt (STUFE0.md); offen: Untergrenze für benannte Argumente, OpenCode 2 |
| 1 Kern: Skills und Regeln | umgesetzt: Server, Weboberfläche, CLI (sync, check, status) |
| 2 Befehle, Subagenten, MCP | umgesetzt, inkl. Autoren-Befehle im CLI (new, validate, try, upload) |
| 2b Auslieferung für Integrationen | umgesetzt, inkl. hugin-install.ps1; mit PowerShell 7 getestet, mit 5.1 noch nicht; für Plattformen (AI Workspace) zusätzlich resolve, source, feedback (25.10.2026) |
| 3 Rückkanal | umgesetzt: Nutzung, Rückmeldungen, MCP-Endpunkt, Überblick, Systemseite |
| 4 Absicherung | umgesetzt: Personen, persönliche Keys, vier Augen nach Risiko, Kanal test, Audit mit Namen, Rauchtest-Skript, COMPATIBILITY.md (alle Prüfungen grün in Claude Code 2.1.303 und OpenCode 1.19.3) |
| 5 Lokale Server, Plugins, Werkzeugspezifisches | 5b und 5d (ohne npm-Abhängigkeiten) umgesetzt (25.10.2026), Windows von Hand offen; geplant: 5a Windows-Prüfung, 5c fertige Programme; danach 5e Hooks, Paare, Signaturen |
Stufe 0 — Machbarkeit prüfen (vor dem ersten Code)
Offene Verhaltensfragen mit den aktuellen Versionen beider Werkzeuge klären. Dazu genügen ein Test-Repo und Handarbeit.
- Ignoriert OpenCode unbekannte Felder in der Frontmatter einer
SKILL.md(z. B.allowed-tools), oder warnt es bzw. lehnt es ab? - Was tut OpenCode, wenn derselbe Skill-Name in
.claude/skills/und.opencode/skills/liegt? - Liest OpenCode
.claude/commands/oder.claude/agents/mit? - Löst OpenCode
@-Verweise in einerCLAUDE.mdauf, wenn es keineAGENTS.mdgibt? - Claude Code liest
AGENTS.mdab 2.1.277, wenn es keineCLAUDE.mdgibt (bestätigt). Offen: Verhalten, wenn beide da sind. - Erlaubt
opencode.jsonKommentare (JSONC)? - Genaue Werkzeugnamen (Claude) und Rechtenamen (OpenCode) für die Fähigkeitsklassen.
- Untergrenzen sind ermittelt (→ 5.5). Offen: ab wann Claude Code benannte
Argumente (
arguments,$name) kennt und ab wann OpenCode diemetadata-Schlüsselopencode/slashundopencode/autoinvokeversteht. - Unterstützt OpenCode MCP-Prompts und -Ressourcen? (Nur zur Information; Hugin baut nicht darauf.)
- OpenCode 2: Liest es MCP-Einträge in der alten Form (
mcp.<name>,enabled)? Kennt esopencode/slash? Ignoriert es unbekannte Felder in der Frontmatter von Skills? Gibt es Doppel, wenn ein Skill in.claude/skills/und.opencode/skills/liegt (die ID kommt jetzt aus dem Pfad)?
Stand: Fragen 1, 2, 3, 6, 7 und MCP sind für OpenCode 1 geklärt, ohne Modellaufruf
(scripts/stage0.sh); 4, 5 und 8 (Claude Code) mit scripts/tool-smoke-test.sh.
opencode/slash und opencode/autoinvoke wirken in OpenCode 1.19.3 nicht (stage0.sh
und tool-smoke-test.sh). Offen: ab welcher Version Claude Code benannte Argumente kennt,
und alles zu OpenCode 2 (Frage 10).
Ergebnis: STUFE0.md mit Antworten, Versionen und den nötigen Änderungen an
diesem Konzept.
Stufe 1 — Kern: Skills und Regeln
- Projektgerüst (
server/mit uv,cli/mit Go); Speicher, Auth, Audit, Web und Hilfe nach dem Vorbild von Munin neu bauen. - CSS-Framework CSP-fest machen (→ 21.2).
- Quellformat für
skillundrules, Manifest-Parser. - Linter (Regeln für diese Arten).
- Übersetzer für Skills und Regeln, beide Ziele, mit Tabellentests.
- Lebenszyklus mit einer Freigabe, noch ohne Personen (→ 14.5): Autor- und
Prüfer-Key getrennt; nur Pakete mit Risiko
textundtools. - Tabelle
peopleschon anlegen, aber noch nicht nutzen. - Weboberfläche: Katalog, Paket, Version mit Diff und Vorschau je Werkzeug, Prüfungen.
- CLI:
init,sync,status,validate,upload. - HTTP-API.
- Erstes Paket:
pub-munin-nutzen.
Abnahme: Ein Test-Repo bekommt per hugin sync einen Skill und eine Regeldatei;
beide funktionieren nachweislich in Claude Code und OpenCode.
Stufe 2 — Übersetzung: Befehle, Subagenten, MCP
- Befehle mit benannten Argumenten.
- Subagenten mit Fähigkeits- und Modellklassen; Modellzuordnung auf der Systemseite.
- MCP-Anbindungen; Bearbeiten von JSON-Dateien mit verwalteten Schlüsseln.
pub-munin-anbindungals Paket.
Abnahme: Rauchtests (→ 17.2) für alle Arten grün in beiden Werkzeugen.
Stufe 2b — Auslieferung für Integrationen
- Endpunkte unter
/api/v1/delivery/, Integrations-Key, Bundle-Format (→ 14.7). - Referenz-Skript
client.ps1für Windows PowerShell 5.1. - Für Plattformen: Auflösen ohne Übersetzung, Quellformat je Version, Rückmeldung (→ 14.7).
Stufe 3 — Rückkanal
- Nutzungsmeldung, Seite „Mein Projekt“, Überblick für Tenant-Admins.
- MCP-Server (
search_packages,get_package,report_feedback,list_subscriptions). hugin checkmit Handänderungen, neuen Versionen, Werkzeugversionen.- Untergrenzen der Werkzeuge.
Stufe 4 — Absicherung
- Persönliche Keys (→ 14.5): Personen anlegen und sperren, Rollen je Projekt oder Tenant, Anmeldung in Web und CLI, Ablauf und Hinweise im Überblick.
- Vier-Augen-Prinzip nach Risiko; ab jetzt auch Pakete mit
scriptsundpermissions. - Audit mit Namen.
- Kanäle
stableundtest, Versionsbereiche,--major. - Pflichtpakete.
hugin try, Rauchtest-Skript,COMPATIBILITY.md.- Export, Sicherung, Systemseite vollständig.
Stufe 5 — Lokale Server, Plugins, Werkzeugspezifisches
Anforderung: MCP-Server nicht nur anbinden, sondern samt Code verteilen und lokal betreiben, dazu OpenCode-Plugins. Zielplattformen der Clients sind Windows und Linux; entwickelt wird auf macOS. Windows wird von Hand geprüft (kein Windows-CI).
Geprüft mit OpenCode 1.19.4 (25.10.2026, ohne Modellaufruf):
- Lokaler MCP-Server mit
"type": "local", "command": ["hugin", "mcp", "run", "<id>"]: OpenCode findethuginüber denPATHund löst{env:NAME}inenvironmentauf. Arbeitsordner ist der aktuelle Ordner, auch ein Unterordner des Repos. - Plugins:
.ts-Dateien unter.opencode/plugins/(auch.opencode/plugin/) werden geladen. OpenCode legt beim Start selbst.opencode/package.jsonundnode_modulesan.
5a Windows als Grundlage
- Alle Pfade im Repo mit
/im Bundle; das CLI wandelt beim Schreiben. Prüfen unter Windows:instructionsinopencode.json, Zeilenenden, Pfade über 260 Zeichen (venv unter dem Benutzerordner). scripts/windows-test.ps1und eine Checkliste inTESTEN.mdfür die Prüfung von Hand: Go-Tests,hugin syncmit allen Arten, echtes OpenCode (debug skill,agent list,mcp list), lokaler Server startet.hugin-install.ps1lernt alles, washugin syncneu lernt.
5b Lokale MCP-Server mit Python
- Manifest:
server.localmitruntime: python,entry(Modul oder Skript) undenv(Namen der Umgebungsvariablen). Im Paketrequirements.txtmit Hashes und der Code. Risiko mindestensscripts. - Ablage außerhalb des Repos:
~/.local/share/mcp/<id>/<version>/(Windows:%USERPROFILE%\.local\share\mcp\…), änderbar mitHUGIN_MCP_HOME. Jede Version in einem eigenen Ordner; mehrere Repos teilen sie. hugin sync(undhugin try) legt dort eine venv an (python -m venv; Python ausHUGIN_PYTHON, unter Windowspy -3, sonst das neuestepython3.x, mindestenslocal.python) und installiert mitpip install --require-hashes -r requirements.txt. pip liest diepip.conf(Windowspip.ini) mit dem Artifactory selbst; ohne Anmeldung.- In
opencode.jsonbzw..mcp.jsonnurhugin mcp run <id>, nie ein Pfad: gleich auf jedem Rechner, einzuchecken.hugin mcp runsucht das Repo nach oben, liest die Version aushugin.lockund startet den Server aus der venv; Ein- und Ausgabe gehen durch (Windows: Kindprozess stattexec). hugin checkmeldet eine fehlende Installation;hugin mcp pruneräumt Versionen auf, die kein Repo mehr nutzt (Repos merkt sichhuginin.hugin-reposder Ablage; Ordner ohne.hugin-install.jsonbleiben unberührt).- Geprüft (macOS, OpenCode 1.19.4): Beispiel ohne Abhängigkeiten und ein Server mit dem
Python-SDK
mcp(30 Abhängigkeiten über pip mit Hashes) verbinden; Aufruf aus einem Unterordner bekommt die Umgebungsvariablen.
5c Fertige Programme
- Manifest:
runtime: binarymitartifactsje Plattform (windows-amd64,linux-amd64, optionaldarwin-arm64zum Ausprobieren):urlundsha256. Die Dateien bleiben im Artifactory, nicht in Hugin. hugin synclädt nur die passende Datei, prüft die Prüfsumme (sonst Abbruch) und legt sie in den Ordner aus 5b, unter Windows als.exe.- Linter: Fehler bei fehlender oder falscher Prüfsumme, Hinweis, wenn Windows oder Linux fehlt. Prüfer sehen Adressen und Prüfsummen.
5d OpenCode-Plugins
Umgesetzt (25.10.2026), zunächst ohne npm-Abhängigkeiten:
- Art
plugin, nur OpenCode:targets: [opencode]ist Pflicht (H038). Inhaltsdateiindex.ts, dazu weitere.ts/.js-Dateien. - Im Repo: das Paket unter
.opencode/plugins/<id>/, dazu die Startdatei.opencode/plugins/<id>.tsmitexport * from "./<id>/index.ts". So bleiben die relativen Importe des Pakets gültig, und Hilfsdateien gelten nicht als eigene Plugins. - Importe nur aus dem Paket,
node:…,bunund@opencode-ai/plugin(H039); das letzte trägt OpenCode selbst in.opencode/package.jsonein. - Risiko
permissions: Ein Plugin läuft in OpenCode mit allen Rechten und sieht jeden Werkzeugaufruf; zwei Freigaben, eine vom Tenant-Admin.
Geprüft mit OpenCode 1.19.4:
- Geladen werden nur Dateien direkt in
.opencode/plugins/(und.opencode/plugin/), keine Unterordner. Jede exportierte Funktion der geladenen Datei wird aufgerufen, auch überexport *; importierte Hilfsdateien nicht. opencode debug configlädt die Plugins und listet sie unterplugin;--purelädt keine.ctx.worktreeist der Wurzelordner des Repos, auch aus einem Unterordner..opencode/package.json,node_modulesund.opencode/.gitignore(schließt beides aus) legt OpenCode selbst an, sobald es im Repo läuft.- Ein Plugin mit Syntaxfehler übergeht OpenCode ohne Meldung, auch mit
--print-logs. - Werkzeug aus einem Plugin (
tool()aus@opencode-ai/plugin) ruft das Modell auf.
Später:
- npm-Abhängigkeiten als verwaltete Schlüssel in
.opencode/package.json(aus dem Artifactory, wenn npm so eingestellt ist). Achtung:.opencode/.gitignorevon OpenCode schließtpackage.jsonaus; feste Versionen ohne^, Konflikte zwischen Paketen. - OpenCode 2 führt V1-Plugins nicht aus (→ 5.5): eigene Fassung nötig, sobald Teams umsteigen.
5e Danach
- Hook-Pakete (Claude Code), Paare.
- Signaturen.
- Optional: native Verteilwege (Claude-Plugin-Marktplatz, OpenCode über npm oder
instructions-URL), falls Teams das wollen.
21. Entscheidungen und offene Fragen
21.1 Getroffen
| Entscheidung | Begründung |
|---|---|
| Name Hugin | Gegenstück zu Munin: Gedanke und Gedächtnis |
| Nur Claude Code und OpenCode, beide voll | Das sind die Werkzeuge im Haus; andere sind nicht nötig |
| Neutrales Quellformat mit Übersetzern statt „ein Werkzeug ist Standard“ | Claude Code liest keine OpenCode-Ordner; OpenCode liest Claude-Ordner. Nur Übersetzer decken beide sauber ab (→ 5.1) |
Strengere Regel gewinnt, Skills liegen in .claude/skills/ |
eine Datei für beide, keine Doppel |
| Server in Python/FastAPI mit Jinja2, HTMX und CSS-Framework; CLI in Go | später in ein anderes FastAPI-Projekt einbettbar; CLI bleibt eine Binärdatei ohne Abhängigkeiten |
| Konzepte wie Munin: SQLite/PostgreSQL, Tenants, Keys, Audit, MCP | ein Betriebsmodell, bekannte Bedienung |
| Übersetzer nur im Server, CLI wendet Operationen an | keine doppelte Umsetzung in zwei Sprachen (→ 14.2) |
Auslieferung für Integrationen über /api/v1/delivery/ mit eigenem Key, ZIP + hugin-bundle.json; Client in PowerShell 5.1 |
Vorgabe der Integration; ein Bundle-Format für CLI und Skript (→ 14.7) |
| Datenbank führend, Export nach Git als Sicherung | Prüfung und Freigabe in der eigenen Weboberfläche; kein Git-Server mit Review nötig |
| Abos im Repository, nicht auf dem Server | versioniert, im Code-Review sichtbar |
Verteilung über hugin sync, MCP nur für Suche und Rückmeldung |
nur installierte Skills nutzen die eingebaute Skill-Logik beider Werkzeuge |
| Maschinen mit Projekt-Keys, Menschen in der Freigabekette als Personen | Vier Augen und Verantwortung im Audit brauchen Namen; sync und Agenten nicht (→ 14.5) |
| Personen über persönliche Keys ab Stufe 4, SSO später gemeinsam mit Munin | keine neue Infrastruktur nötig; Tabelle people mit external_id hält den Weg zu SSO offen |
Bis Stufe 4 nur Pakete mit Risiko text und tools |
ohne echte vier Augen keine Skripte und keine Rechte verteilen |
| Zwei Ebenen: öffentlich und Tenants; Tenants sehen nur Öffentliches und Eigenes | klare Trennung ohne Freigabelisten je Paket (→ 10.1) |
Eine Datenbank mit tenant_id statt einer Datenbank je Tenant |
einfacher mit SQLAlchemy, einbettbar in andere Projekte; Trennung zentral im Code (→ 15.1) |
Strenge CSP ohne 'unsafe-inline'; das CSS-Framework wird CSP-fest gemacht |
Nonces helfen nicht bei style-Attributen; das Framework profitiert auch in anderen Projekten (→ 16.4) |
| Keine persönlichen Pakete, nur öffentlich oder je Tenant/Projekt | weniger Pfade, keine Nutzerordner (→ 3) |
Keine Volltextsuche in Stufe 1, LIKE über ID und Beschreibung |
Katalog bleibt klein; im Store nachrüstbar (→ 13) |
| Untergrenzen vorläufig: Claude Code 2.1.105, OpenCode 1.2.11 | aus Changelogs ermittelt; Stufe 0 hat nur aktuelle Versionen geprüft (Claude Code 2.1.303, OpenCode 1.15.4 und 1.19.3). Endgültig erst nach einem Rauchtest mit den Untergrenzen selbst (→ 5.5) |
| OpenCode 1 und 2 werden unterstützt | OpenCode 2 ist seit 11.10.2026 verfügbar, viele Teams sind noch auf 1 (→ 5.5) |
Zu alte Werkzeugversion: warnen, abbrechen nur mit tool_check: strict |
Teams nicht blockieren, wer will, kann es erzwingen (→ 5.4) |
| Repo-Kennung = vereinheitlichte Git-Remote-URL; Integrationen ohne Git melden einen Client-Namen | intern ohnehin bekannt, genau genug für die Übersicht (→ 11.5) |
| Betrieb in OpenShift, Deployment außerhalb dieses Konzepts | wird vom Betreiber übernommen (→ 18) |
Lokale MCP-Server starten über hugin mcp run <id>, Code unter ~/.local/share/mcp/ |
Pfade sind je Rechner verschieden; so bleibt opencode.json gleich und einzuchecken, und die Version kommt aus hugin.lock (→ 20, Stufe 5b) |
Python über venv und pip, Artifactory aus pip.conf; Programme als Adresse und Prüfsumme |
so ist es im Haus eingerichtet; große Dateien bleiben im Artifactory (→ 20, Stufe 5b, 5c) |
| Clients: Windows und Linux, Prüfung unter Windows von Hand | Zielplattformen; entwickelt wird auf macOS (→ 20, Stufe 5a) |
21.2 Offen
Keine offenen Entscheidungen. Offen sind noch zwei Prüfungen (STUFE0.md): ab welcher Version Claude Code benannte Argumente kennt (davon hängt die Untergrenze ab) und das Verhalten von OpenCode 2. Dazu Stufe 5 (→ 20).
22. Quellen und was neu zu prüfen ist
Stand der Dokus: 17.10.2026. Beide Werkzeuge ändern sich schnell. Nach größeren
Updates neu nachsehen und scripts/stage0.sh sowie scripts/tool-smoke-test.sh
laufen lassen.
| Thema | Quelle |
|---|---|
| OpenCode Skills | https://opencode.ai/docs/skills/ |
OpenCode Regeln (AGENTS.md, instructions, Claude-Kompatibilität) |
https://opencode.ai/docs/rules/ |
| OpenCode Agenten | https://opencode.ai/docs/agents/ |
| OpenCode Befehle | https://opencode.ai/docs/commands/ |
| OpenCode MCP-Server | https://opencode.ai/docs/mcp-servers/ |
| OpenCode Plugins | https://opencode.ai/docs/plugins/ |
| OpenCode 2: Umstieg von V1 | https://opencode.ai/v2/docs/migrate-v1/ |
| OpenCode 2: Skills | https://opencode.ai/v2/docs/skills/ |
| OpenCode Release-Notes | https://github.com/sst/opencode/releases |
| Claude Code Changelog | https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md |
| Claude Code Skills | https://code.claude.com/docs/en/skills |
| Claude Code Subagenten | https://code.claude.com/docs/en/sub-agents |
Claude Code Memory (CLAUDE.md, @-Import) |
https://code.claude.com/docs/en/memory |
| Claude Code MCP | https://code.claude.com/docs/en/mcp |
| Claude Code Hooks | https://code.claude.com/docs/en/hooks |
| Agent-Skills-Standard | https://agentskills.io |
Unbestätigt zum Stand dieses Dokuments (nur Berichte Dritter):
- dass Claude Code ab 2.1.277
AGENTS.mdvon selbst liest, solange keineCLAUDE.mdda ist.
In der Doku nicht beantwortet und deshalb in Stufe 0 selbst geprüft (STUFE0.md):
- Fragen 1–8 aus Abschnitt 20; offen sind 9 (nur zur Information) und 10 (OpenCode 2).