1909 lines
98 KiB
Markdown
1909 lines
98 KiB
Markdown
# 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
|
||
|
||
1. [Kurzfassung](#1-kurzfassung)
|
||
2. [Ausgangslage und Problem](#2-ausgangslage-und-problem)
|
||
3. [Ziele und Nicht-Ziele](#3-ziele-und-nicht-ziele)
|
||
4. [Begriffe](#4-begriffe) · [Namen im Code](#41-namen-im-code)
|
||
5. [Zielwerkzeuge: Claude Code und OpenCode](#5-zielwerkzeuge-claude-code-und-opencode)
|
||
6. [Das Quellformat](#6-das-quellformat)
|
||
7. [Die Übersetzer](#7-die-übersetzer)
|
||
8. [Prüfung (Linter)](#8-prüfung-linter)
|
||
9. [Lebenszyklus und Freigabe](#9-lebenszyklus-und-freigabe)
|
||
10. [Geltung, Abos, Pflichtpakete, Namen](#10-geltung-abos-pflichtpakete-namen)
|
||
11. [Verteilung: das Programm `hugin`](#11-verteilung-das-programm-hugin)
|
||
12. [MCP-Server](#12-mcp-server)
|
||
13. [Weboberfläche](#13-weboberfläche)
|
||
14. [Architektur](#14-architektur)
|
||
15. [Datenmodell](#15-datenmodell)
|
||
16. [Sicherheit](#16-sicherheit)
|
||
17. [Kompatibilitätstests](#17-kompatibilitätstests)
|
||
18. [Betrieb](#18-betrieb)
|
||
19. [Zusammenspiel mit Munin](#19-zusammenspiel-mit-munin)
|
||
20. [Ausbaustufen](#20-ausbaustufen)
|
||
21. [Entscheidungen und offene Fragen](#21-entscheidungen-und-offene-fragen)
|
||
22. [Quellen und was neu zu prüfen ist](#22-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 sync`**
|
||
in 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
|
||
|
||
1. **Ein Ort** für Skills, Regeln, Befehle, Subagenten und MCP-Anbindungen, mit Suche.
|
||
2. **Geprüft:** Nichts erreicht die Teams ohne Freigabe durch eine zweite Person.
|
||
3. **Versioniert und nachvollziehbar:** Jede Version ist unveränderlich, mit Prüfsumme,
|
||
Diff zur Vorgängerversion und Audit, wer was wann freigegeben hat.
|
||
4. **Claude Code und OpenCode gleichwertig:** Ein Paket, beide Werkzeuge.
|
||
Die Weboberfläche zeigt für jede Version, was in welchem Werkzeug ankommt.
|
||
5. **Einfach für die Teams:** Eine kleine Datei im Repo, ein Befehl `hugin sync`.
|
||
Eigene Dateien der Teams werden nie angefasst.
|
||
6. **Rückkanal:** Hugin weiß, welches Projekt welche Version nutzt, und Agenten
|
||
wie Menschen können Rückmeldung zu einem Paket geben.
|
||
7. **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/*.md` in `instructions`.
|
||
|
||
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üssel `opencode/slash` und `opencode/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 sync` und `hugin check` lesen die installierten Versionen
|
||
(`claude --version`, `opencode --version`) und **warnen**, wenn eine zu alt ist.
|
||
Mit `tool_check: strict` in `.hugin.yaml` (oder `--strict`) bricht `sync`
|
||
stattdessen ab.
|
||
- Ein Paket kann eine **höhere** Untergrenze verlangen, wenn es eine neuere
|
||
Funktion nutzt (Feld `min_version` im 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
|
||
|
||
```yaml
|
||
# 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.
|
||
|
||
```yaml
|
||
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: fork` bei
|
||
Claude Code verlangt eine eigene Fassung je Werkzeug → `translated`.
|
||
|
||
### 6.4 Beispiele je Art
|
||
|
||
#### Befehl mit benannten Argumenten
|
||
|
||
```yaml
|
||
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
|
||
```
|
||
|
||
```markdown
|
||
<!-- 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
|
||
|
||
```yaml
|
||
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
|
||
|
||
```yaml
|
||
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:
|
||
|
||
```yaml
|
||
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.md` mit
|
||
Frontmatter aus `name`, `description`, `license`, `metadata`, dazu die Hilfsdateien.
|
||
Beide Werkzeuge lesen sie.
|
||
- **Mit Zusätzen für Claude:** OpenCode übergeht unbekannte Felder wie `allowed-tools`
|
||
still (Stufe 0, Frage 1). Die Felder kommen mit in dieselbe Datei, die Stufe bleibt
|
||
`portable`. Folge: Felder, die das Verhalten ändern, wirken nur in Claude Code, etwa
|
||
`disable-model-invocation` (Linter-Hinweis H023).
|
||
- **Ausnahme `OPENCODE_DISABLE_CLAUDE_CODE` oder `…_SKILLS`:** Hat ein Team den
|
||
Claude-Support in OpenCode abgeschaltet, liest OpenCode `.claude/skills/` nicht.
|
||
Dafür gibt es in `.hugin.yaml` die Einstellung `opencode_reads_claude: false`.
|
||
Hugin schreibt die Skills dann zusätzlich nach `.opencode/skills/`.
|
||
- **Nur OpenCode** (`tools` ohne `claude`): 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 mit `opencode 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:
|
||
|
||
```markdown
|
||
<!-- 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.md` zwar die `CLAUDE.md`, löst deren
|
||
`@`-Verweise aber nicht auf. Die Regeln kommen also nicht doppelt an; eine eigene
|
||
`AGENTS.md` ist nicht nötig.
|
||
- `opencode.json` darf 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:
|
||
|
||
```yaml
|
||
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.json` unter `mcpServers.<name>` den Eintrag
|
||
`"type": "http"`, `url`, `headers`. Aus `{{env:VAR}}` wird `${VAR}`.
|
||
- **OpenCode:** Hugin ergänzt in `opencode.json` unter `mcp.<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, bricht
|
||
`sync` mit 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.lock` immer funktioniert. `sync` meldet
|
||
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 als `test` markiert werden. Sie
|
||
erreicht nur Abos auf `@test` und wird später auf `stable` hochgestuft.
|
||
- **Abos auf Versionsbereiche:** `@^1` (alles 1.x), `@1.4.0` (fest).
|
||
Bei `@stable` gilt: Major-Sprünge kommen **nicht** automatisch. `sync` meldet sie,
|
||
der Nutzer bestätigt mit `hugin 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)
|
||
|
||
```yaml
|
||
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)
|
||
|
||
```yaml
|
||
# 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:
|
||
|
||
```markdown
|
||
---
|
||
# Verwaltet von Hugin (pub-munin-nutzen 1.4.0). Änderungen über hugin.intern.
|
||
name: pub-munin-nutzen
|
||
description: …
|
||
---
|
||
```
|
||
|
||
- **`CLAUDE.md`:** nur der Block zwischen `hugin:begin` und `hugin: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:** `sync` zeigt den Diff und bricht ab. Mit `--overwrite`
|
||
setzt es die Datei zurück.
|
||
|
||
### 11.5 Ablauf von `hugin sync`
|
||
|
||
1. `.hugin.yaml` lesen, Werkzeuge erkennen, Werkzeugversionen prüfen (Warnung).
|
||
2. Beim Server `POST /api/v1/bundle` aufrufen: abonnierte und Pflichtpakete gegen
|
||
Kanäle und Bereiche → konkrete Versionen, übersetzt für die erkannten Werkzeuge.
|
||
3. Für Versionen, deren Prüfsumme nicht in `hugin.lock` steht, die übersetzten
|
||
Operationen für die erkannten Werkzeuge laden (→ 14.2). Prüfsummen kontrollieren.
|
||
5. Handänderungen an verwalteten Dateien suchen → bei Befund abbrechen.
|
||
6. Schreiben, Abbestelltes entfernen, `hugin.lock` neu schreiben.
|
||
7. 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.
|
||
8. 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-Request` nur das Fragment, sonst die ganze Seite.
|
||
- **Komponenten** kommen nur aus dem Framework (`components.css`, Tokens aus
|
||
`tokens.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_core` hat keine Abhängigkeit zu FastAPI oder DB.
|
||
- **Router statt fester App:** `hugin_app` stellt Router bereit (`api_router()`,
|
||
`web_router()`, `mcp_app()`). Die eigenständige App setzt sie nur zusammen; ein
|
||
anderes Projekt hängt sie mit `include_router` unter 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:
|
||
|
||
1. **Vier Augen:** Der Autor darf nicht selbst freigeben. Bei riskanten Paketen
|
||
stimmen zwei *verschiedene* Menschen zu.
|
||
2. **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
|
||
|
||
1. **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`, etwa `pub-munin-nutzen`.
|
||
Pakete mit Risiko `scripts` oder `permissions` nimmt Hugin bis Stufe 4 **nicht an**.
|
||
2. **Stufe 4: persönliche Keys.**
|
||
- Rollen `author` und `reviewer` hängen an der Person, je Projekt oder Tenant.
|
||
- Vier Augen werden erzwungen: Prüfer ≠ Autor; bei `scripts` und `permissions` zwei
|
||
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.
|
||
3. **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 login` speichert den persönlichen Key im Schlüsselbund.
|
||
`hugin upload` nutzt ihn; `hugin sync` nutzt weiterhin den Projekt-Key aus
|
||
`HUGIN_KEY` bzw. 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; Rolle
|
||
`delivery` (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 `/bundle` ohne `tools` und `options`
|
||
(`packages`, `unsubscribed`, `mandatory`, `major`, `installed`). Antwort je Paket:
|
||
`id`, `kind`, `version`, `checksum`, `risk`, `channel`, `mandatory`, `deprecated`,
|
||
`successor`, `description`, `arguments`; dazu `major_available`. Gleiche Auflösung
|
||
wie beim Bundle (→ 9.3), Pflichtpakete eingeschlossen.
|
||
- **`GET /packages/{id}/versions/{version}/source`:** `hugin.yaml` und Inhaltsdateien
|
||
als ZIP (wie der Download, → 14.6), deterministisch; Kopfzeile `X-Hugin-Checksum` mit
|
||
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`). Quelle `agent`, 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-Namen `ai-workspace/<workspace>` als `repo`.
|
||
|
||
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`:
|
||
|
||
```json
|
||
{ "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 |
|
||
|
||
```json
|
||
{
|
||
"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 NULL` für Pakete,
|
||
`tenant_id = :tenant` fü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`,
|
||
nicht `403`, 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; `sync` lehnt 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:** `sync` schreibt nur unter den bekannten Pfaden und
|
||
nur Schlüssel aus `hugin.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_SERVER` gesetzt, sendet das CLI einen
|
||
Key aus `HUGIN_KEY` nur an diesen Server, auch wenn ein Pull Request `server:` 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 `author` oder `reviewer`). 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-Token` für
|
||
HTMX-Anfragen (über `hx-headers` im Basis-Template). Die API mit Bearer-Key
|
||
braucht kein CSRF-Token.
|
||
- **Rate-Limit** je Key bzw. Sitzung (`HUGIN_RATE_LIMIT`, Standard 600 je Minute); nach
|
||
`HUGIN_FAILED_ATTEMPTS` falschen 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-For` gilt nur von Adressen in
|
||
`HUGIN_PROXY_IPS` (Standard `127.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_TOKEN` gelten bestehende Sitzungen sofort nicht mehr.
|
||
- **Nur verschlüsselt:** Das CLI sendet Keys nur über `https://` oder an den eigenen
|
||
Rechner, auch bei Weiterleitungen; zum Testen `HUGIN_ALLOW_HTTP=1`. Der
|
||
PowerShell-Client warnt bei `http://`.
|
||
- **Kein Browser-Cache:** Antworten außer `/static/` mit `Cache-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:
|
||
|
||
```html
|
||
<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 `sync` geht 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 check` meldet ein CLI älter als
|
||
`HUGIN_CLI_MIN_VERSION` als Befund und weist auf neuere Versionen hin.
|
||
|
||
---
|
||
|
||
## 19. Zusammenspiel mit Munin
|
||
|
||
- **Erstes Paket:** `pub-munin-nutzen` (Skill), dazu `pub-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-nutzen` zeigen, 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.
|
||
|
||
1. Ignoriert OpenCode unbekannte Felder in der Frontmatter einer `SKILL.md`
|
||
(z. B. `allowed-tools`), oder warnt es bzw. lehnt es ab?
|
||
2. Was tut OpenCode, wenn derselbe Skill-Name in `.claude/skills/` und
|
||
`.opencode/skills/` liegt?
|
||
3. Liest OpenCode `.claude/commands/` oder `.claude/agents/` mit?
|
||
4. Löst OpenCode `@`-Verweise in einer `CLAUDE.md` auf, wenn es keine `AGENTS.md` gibt?
|
||
5. Claude Code liest `AGENTS.md` ab 2.1.277, wenn es keine `CLAUDE.md` gibt
|
||
(bestätigt). Offen: Verhalten, wenn beide da sind.
|
||
6. Erlaubt `opencode.json` Kommentare (JSONC)?
|
||
7. Genaue Werkzeugnamen (Claude) und Rechtenamen (OpenCode) für die Fähigkeitsklassen.
|
||
8. Untergrenzen sind ermittelt (→ 5.5). Offen: ab wann Claude Code benannte
|
||
Argumente (`arguments`, `$name`) kennt und ab wann OpenCode die `metadata`-Schlüssel
|
||
`opencode/slash` und `opencode/autoinvoke` versteht.
|
||
9. Unterstützt OpenCode MCP-Prompts und -Ressourcen? (Nur zur Information; Hugin baut nicht darauf.)
|
||
10. **OpenCode 2:** Liest es MCP-Einträge in der alten Form (`mcp.<name>`, `enabled`)?
|
||
Kennt es `opencode/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 `skill` und `rules`, 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 `text` und `tools`.
|
||
- Tabelle `people` schon 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-anbindung` als 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.ps1` fü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 check` mit 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 `scripts` und `permissions`.
|
||
- Audit mit Namen.
|
||
- Kanäle `stable` und `test`, 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 findet `hugin` über den `PATH` und löst `{env:NAME}` in `environment` auf.
|
||
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.json` und `node_modules` an.
|
||
|
||
#### 5a Windows als Grundlage
|
||
|
||
- Alle Pfade im Repo mit `/` im Bundle; das CLI wandelt beim Schreiben. Prüfen unter
|
||
Windows: `instructions` in `opencode.json`, Zeilenenden, Pfade über 260 Zeichen
|
||
(venv unter dem Benutzerordner).
|
||
- `scripts/windows-test.ps1` und eine Checkliste in `TESTEN.md` für die Prüfung von Hand:
|
||
Go-Tests, `hugin sync` mit allen Arten, echtes OpenCode (`debug skill`, `agent list`,
|
||
`mcp list`), lokaler Server startet.
|
||
- `hugin-install.ps1` lernt alles, was `hugin sync` neu lernt.
|
||
|
||
#### 5b Lokale MCP-Server mit Python
|
||
|
||
- Manifest: `server.local` mit `runtime: python`, `entry` (Modul oder Skript) und
|
||
`env` (Namen der Umgebungsvariablen). Im Paket `requirements.txt` mit Hashes und der
|
||
Code. Risiko mindestens `scripts`.
|
||
- Ablage außerhalb des Repos: `~/.local/share/mcp/<id>/<version>/` (Windows:
|
||
`%USERPROFILE%\.local\share\mcp\…`), änderbar mit `HUGIN_MCP_HOME`. Jede Version in
|
||
einem eigenen Ordner; mehrere Repos teilen sie.
|
||
- `hugin sync` (und `hugin try`) legt dort eine venv an (`python -m venv`; Python aus
|
||
`HUGIN_PYTHON`, unter Windows `py -3`, sonst das neueste `python3.x`, mindestens
|
||
`local.python`) und
|
||
installiert mit `pip install --require-hashes -r requirements.txt`. pip liest die
|
||
`pip.conf` (Windows `pip.ini`) mit dem Artifactory selbst; ohne Anmeldung.
|
||
- In `opencode.json` bzw. `.mcp.json` nur `hugin mcp run <id>`, nie ein Pfad: gleich auf
|
||
jedem Rechner, einzuchecken. `hugin mcp run` sucht das Repo nach oben, liest die
|
||
Version aus `hugin.lock` und startet den Server aus der venv; Ein- und Ausgabe gehen
|
||
durch (Windows: Kindprozess statt `exec`).
|
||
- `hugin check` meldet eine fehlende Installation; `hugin mcp prune` räumt Versionen
|
||
auf, die kein Repo mehr nutzt (Repos merkt sich `hugin` in `.hugin-repos` der Ablage;
|
||
Ordner ohne `.hugin-install.json` bleiben 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: binary` mit `artifacts` je Plattform (`windows-amd64`,
|
||
`linux-amd64`, optional `darwin-arm64` zum Ausprobieren): `url` und `sha256`. Die
|
||
Dateien bleiben im Artifactory, nicht in Hugin.
|
||
- `hugin sync` lä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). Inhaltsdatei
|
||
`index.ts`, dazu weitere `.ts`/`.js`-Dateien.
|
||
- Im Repo: das Paket unter `.opencode/plugins/<id>/`, dazu die Startdatei
|
||
`.opencode/plugins/<id>.ts` mit `export * 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:…`, `bun` und `@opencode-ai/plugin` (H039); das
|
||
letzte trägt OpenCode selbst in `.opencode/package.json` ein.
|
||
- 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
|
||
über `export *`; importierte Hilfsdateien nicht.
|
||
- `opencode debug config` lädt die Plugins und listet sie unter `plugin`; `--pure`
|
||
lädt keine. `ctx.worktree` ist der Wurzelordner des Repos, auch aus einem Unterordner.
|
||
- `.opencode/package.json`, `node_modules` und `.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/.gitignore` von OpenCode
|
||
schließt `package.json` aus; 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.md` von selbst liest, solange keine `CLAUDE.md` da 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).
|