Doku-Vollausbau: 62 Seiten (Projekt/Admin/Anwender/Referenz), Hook-Referenz mit Signaturen, Corporate-Screenshots, Coverage-Inventar
Deploy tibi-docs / build-and-deploy (push) Failing after 38s
Deploy tibi-docs / build-and-deploy (push) Failing after 38s
- Einstieg: Schnellstart + Neues Projekt aus Starter-Skill, Was-ist-Tibi mit Backend/Website-Trennung - Projekt: Sammlungen, Felder, Abfragen, Rechte, Hooks, Dateien, i18n, Suche, SSR, Pagebuilder, Jobs, Audit, Deploy, LLM, MCP - Hook-Referenz: 66 Symbole mit Signatur + Beispiel + Fehlerfall (5 Kapitel) - Admin: Ansichten, Editor, Medien, Exporte, Aktionen, Rechte, KI, Setup, Customizing - Anwender: 11 Anleitungen im professionellen Du-Ton, 30 Corporate-Screenshots - Referenz: Schemas, Token-Köpfe, Fehlerbilder, Shortcuts, OpenAPI-Platzhalter - ADR-0001 bis 0003, Coverage-Skript (282 Items) + Map, Plan-Dateien
This commit is contained in:
1 parent
acb1d32355
commit
1897bb50d9
101 files changed
+8980
-68
No files matched your search
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: 'Betrieb und Beobachtung'
|
||||
description: 'Logs, Metriken, Suche neu aufbauen, Jobs und Audit aus Betreiber-Sicht.'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
Du betreibst den **tibi-server** als Container. Diese Seite zeigt Startprofile, Gesundheit, Suche, Jobs und typische Störungen. Details zur Datei stehen unter [Server-Konfiguration](/server/konfiguration/).
|
||||
|
||||
## Startprofile
|
||||
|
||||
Der Starter (`docker-compose-local.yml`) kennt zwei Profile:
|
||||
|
||||
<Badge text="tibi" variant="tip" /> <Badge text="tibi-dev" variant="note" />
|
||||
|
||||
| Profil | Service | Wofür |
|
||||
|---|---|---|
|
||||
| `tibi` | `tibiserver` (Image `tibi-server:nova`) | Normalbetrieb: fertiges Image, Projekt unter `./` als `/data` gemountet |
|
||||
| `tibi-dev` | `tibiserver-dev` (Build aus `tibi-server/Dockerfile.air`) | Entwicklung am Server: Live-Reload, Server-Code gemountet |
|
||||
|
||||
Dazu laufen `mongo` (MongoDB 7) und `maildev` (Mails auf `maildev:25`, Web-UI zum Lesen).
|
||||
|
||||
```bash title="Starten und Logs ansehen"
|
||||
docker compose --profile tibi up -d
|
||||
docker compose logs -f tibiserver
|
||||
```
|
||||
|
||||
Beim Start loggt der Server die wirksame Konfiguration (`using following server config:`). Prüfe dort zuerst `DB.Dial`, `DB.Prefix` und `API.Port`.
|
||||
|
||||
## Gesundheit und Metriken
|
||||
|
||||
| Signal | Adresse | Was du siehst |
|
||||
|---|---|---|
|
||||
| Version | `GET /api/v1/version` | `{"version":"…"}` — der Server lebt und die Config lädt |
|
||||
| Prometheus | `GET /metrics` | u. a. `tibi_request_duration_seconds` je Methode, Status, Namespace, Collection |
|
||||
| Sentry | nur bei gesetzter `sentry.dsn` | Fehler + Traces mit Client-IP und Transaktionsname |
|
||||
|
||||
<Steps>
|
||||
1. `curl http://localhost:8080/api/v1/version` — kommt JSON, läuft der Router.
|
||||
2. `curl http://localhost:8080/metrics` — kommen `tibi_*`-Zeilen, läuft die Instrumentierung.
|
||||
3. Antwortzeiten steigen? In `/metrics` nach `status`- und `col`-Labels filtern.
|
||||
</Steps>
|
||||
|
||||
<Aside type="note" title="Audit">
|
||||
`audit.enabled: true` schreibt System-Änderungen (User, Projekt, Login, Shutdown) mit TTL weg. Vorgabe sind 30 Tage (`720h` über `audit.defaultTTL`). Lies sie über `GET /api/v1/audit` (`audit.read`-Recht nötig).
|
||||
</Aside>
|
||||
|
||||
## Suche und Reload
|
||||
|
||||
Die Volltextsuche läuft pro Projekt in einem Worker. Nach Config-Änderungen musst du neu laden:
|
||||
|
||||
| Endpunkt | Wirkung |
|
||||
|---|---|
|
||||
| `POST /api/v1/_/:project/_/admin/reload` | Lädt Projekt-Config neu (Recht `project.reload`) |
|
||||
| `POST /api/v1/_/:project/_/admin/regenerate-search` | Baut Suchindex neu auf (`project.regenerate-search`) |
|
||||
| `GET /api/v1/_/:project/_/admin/search-worker/status` | Status des Such-Workers |
|
||||
| `GET /api/v1/_/:project/_/admin/embedding/status` | Status der Embedding-Provider |
|
||||
|
||||
```bash title="Nach Config-Änderung: Reload plus Suchindex neu aufbauen"
|
||||
curl -X POST http://localhost:8080/api/v1/_/meinprojekt/_/admin/reload \
|
||||
-H "X-Admin-Token: $TOKEN"
|
||||
curl -X POST http://localhost:8080/api/v1/_/meinprojekt/_/admin/regenerate-search \
|
||||
-H "X-Admin-Token: $TOKEN"
|
||||
```
|
||||
|
||||
Backups und Snapshots meldet der Server per Fortschritts-Stream (SSE, `text/event-stream`). Bricht ein Download ab, hole zuerst die Analyse (`backup/analyze-download`), dann die Datei neu.
|
||||
|
||||
## Jobs
|
||||
|
||||
Jobs sind geplante Skripte je Projekt (Cron in der Projekt-`config.yml`, gleiche goja-Laufzeit wie Hooks):
|
||||
|
||||
- Sie laufen im `JOB_CONTEXT` — ohne HTTP-Request, mit fast allen Hook-Paketen.
|
||||
- Sie laufen unter der Projekt-UID (suexec-Isolation, siehe [Sicherheit](/server/sicherheit/)).
|
||||
- Schlägt ein Job fehl, steht die Ursache im Server-Log; Quotas und Nutzung landen in der LLM-Nutzungsablage.
|
||||
|
||||
## Störungen
|
||||
|
||||
| Bild | Ursache (häufig) | Maßnahme |
|
||||
|---|---|---|
|
||||
| Start-Panic `invalid number in env API_PORT` | Port fehlt oder `<= 0` | `API_PORT` setzen, siehe [Server-Konfiguration](/server/konfiguration/) |
|
||||
| `version` antwortet nicht | Container down, falscher Port | `docker compose ps`, Logs prüfen |
|
||||
| Login wird immer langsamer | Ratelimit-Backoff nach Fehlversuchen | Warten (Retry-After), Zugangsdaten prüfen |
|
||||
| Suche findet nichts Neues | Worker hängt oder Index alt | Worker-Status abfragen, `regenerate-search` |
|
||||
| 413 bei Upload | Body-Limit erreicht (Server → Projekt → Collection) | `API_UPLOAD_MAX_BODY_SIZE` bzw. Collection-Limit erhöhen |
|
||||
| Leere Seite trotz Daten | CORS blockt den Browser | Origins in der Collection-Config prüfen |
|
||||
|
||||
Begriffe wie Collection, Hook oder Namespace klärt [Begriffe](/einstieg/begriffe/). Zum Einrichten siehe [Schnellstart](/einstieg/schnellstart/).
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server); Compose-Profile am `e3f92ebf` 2026-10-14 (tibi-svelte-starter).
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: 'Server-Konfiguration'
|
||||
description: 'config.yml und Env-Overrides des tibi-servers selbst (Betreiber-Sicht).'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
Du konfigurierst den **tibi-server** über eine `config.yml` plus Umgebungsvariablen. Env schlägt Datei. Die Wahrheit steht in `internal/config/server.go` (Struct-Tags).
|
||||
|
||||
```text title="Config-Vorrang"
|
||||
config.yml (Datei) < Umgebungsvariable (gewinnt immer)
|
||||
\ /
|
||||
v v
|
||||
Server.Server (Speicher)
|
||||
```
|
||||
|
||||
## Datei und Env-Regeln
|
||||
|
||||
- Fehlt die Datei, startet der Server mit Notwerten: Port `8080`, Prefix `tibi`, Stacktraces an.
|
||||
- `API_PORT` oder `PORT` sind Pflicht: Port `<= 0` beendet den Start per Panic.
|
||||
- Namen mit Bindestrich werden in Env zu Unterstrich + Großschrift: `bge-m3` → `EMBEDDING_BGE-M3_*`.
|
||||
- Listen in Env sind Komma-getrennt: `LLM_PROVIDERS=openai,lokal`.
|
||||
|
||||
## Die Bereiche im Überblick
|
||||
|
||||
| Bereich (`config.yml`) | Wichtigste Schlüssel | Env-Override |
|
||||
|---|---|---|
|
||||
| `db` | `dial`, `prefix`, `logging` | `DB_DIAL` (Fallback `MGO_DIAL`), `DB_PREFIX`, `DB_LOGGING` |
|
||||
| `api` | `port`, `jwtSecret`, `upload.maxBodySize`, Token-Lifetimes | `API_PORT`/`PORT`, `API_JWTSECRET`, `API_UPLOAD_MAX_BODY_SIZE`, `API_ACCESS_TOKEN_LIFETIME`, `API_REFRESH_TOKEN_LIFETIME`, `API_SECURE_COOKIES` |
|
||||
| `mail` | `host`, `username`, `password` | `MAIL_HOST` |
|
||||
| `security` | `allowAbsolutePaths`, `allowUpperPaths` | `SECURITY_ALLOWABSOLUTEPATHS`, `SECURITY_ALLOWUPPERPATHS` |
|
||||
| `response.error` | `stack`, `message` | `RESPONSE_ERROR_STACK`, `RESPONSE_ERROR_MESSAGE` |
|
||||
| `audit` | `enabled`, `defaultTTL`, `defaultLimit`, `maxLimit` | `AUDIT_ENABLED`, `AUDIT_DEFAULT_TTL`, `AUDIT_DEFAULT_LIMIT`, `AUDIT_MAX_LIMIT` |
|
||||
| `ratelimit` | `enabled`, `loginInitialDelay`, `loginMaxDelay`, `loginResetAfter` | `RATELIMIT_*` (gleiche Namen) |
|
||||
| `hook` | `cache.*`, `ratelimit.*` | `HOOK_CACHE_*`, `HOOK_RATELIMIT_*` |
|
||||
| `mcp` | `enabled`, `route`, `maxResultItems`, `allowAdminActions` | `MCP_ENABLED`, `MCP_ROUTE`, `MCP_MAX_RESULT_ITEMS`, `MCP_ALLOW_ADMIN_ACTIONS` |
|
||||
| `sentry` | `dsn`, `environment`, `serverName`, `enableTracing`, `debug` | keine — nur Datei |
|
||||
| `cors` | `allowOrigins`, `allowCredentials`, … | keine — wirksam ist die Projekt-/Collection-Ebene (siehe [Sicherheit](/server/sicherheit/)) |
|
||||
| `llm` / `embedding` | Provider-Listen | dynamisch, siehe unten |
|
||||
|
||||
<Aside type="caution" title="README lügt an zwei Stellen">
|
||||
Das README-Beispiel ist unvollständig und nennt <del>Go 1.23</del> <ins>go 1.25.0</ins> (Toolchain `go1.25.7`) laut `go.mod`. Verlasse dich nur auf die Struct-Tags in `server.go`.
|
||||
</Aside>
|
||||
|
||||
## Datenbank, API, Mail
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Minimal-Beispiel">
|
||||
|
||||
```yaml title="config.yml"
|
||||
db:
|
||||
dial: mongodb://mongo
|
||||
prefix: meinprojekt
|
||||
api:
|
||||
port: 8080
|
||||
jwtSecret: mindestens-32-zufallszeichen
|
||||
mail:
|
||||
host: maildev:25
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Lokaler Dial (Starter)">
|
||||
|
||||
```yaml title="docker-compose-local.yml (Service tibiserver)"
|
||||
environment:
|
||||
DB_DIAL: mongodb://mongo
|
||||
DB_PREFIX: ${TIBI_PREFIX}
|
||||
MAIL_HOST: maildev:25
|
||||
SECURITY_ALLOWABSOLUTEPATHS: "true"
|
||||
SECURITY_ALLOWUPPERPATHS: "true"
|
||||
RESPONSE_ERROR_STACK: "true"
|
||||
```
|
||||
|
||||
Der Starter nutzt `mongodb://mongo` (Compose-Netz). Lokal ohne Compose heißt der Dial `mongodb://localhost:27017`. Der Prefix trennt Projekte in einer MongoDB.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Defaults, die du kennen musst:
|
||||
|
||||
- `secureCookies`: Standard `true`. Nur für reines HTTP-Lokalsetup auf `false` setzen.
|
||||
- `accessTokenLifetime`: Standard `60m`. `refreshTokenLifetime`: Standard `10h`. Format ist Go-Duration (`30m`, `24h`).
|
||||
- Ungültige Duration fällt still auf den Default zurück (Log-Warnung).
|
||||
|
||||
## LLM und Embedding per Env
|
||||
|
||||
Provider musst du nicht in die Datei schreiben. Diese Muster gelten:
|
||||
|
||||
```bash title="LLM-Provider anmelden"
|
||||
LLM_PROVIDERS=openai,lokal-llm
|
||||
LLM_OPENAI_APIKEY=sk-...
|
||||
LLM_OPENAI_TYPE=openai # Vorgabe, wenn leer
|
||||
LLM_OPENAI_BASEURL=https://api.openai.com/v1 # Vorgabe für Typ openai
|
||||
LLM_OPENAI_DEFAULTMODEL=gpt-4o
|
||||
LLM_OPENAI_MODELS=gpt-4o,gpt-4o-mini
|
||||
```
|
||||
|
||||
```bash title="Embedding-Provider anmelden (Starter-Beispiel)"
|
||||
EMBEDDING_PROVIDERS=bge-m3
|
||||
EMBEDDING_BGE-M3_TYPE=native
|
||||
EMBEDDING_BGE-M3_MODELPATH=/models/bge-m3
|
||||
EMBEDDING_BGE-M3_DIMENSIONS=1024
|
||||
EMBEDDING_BGE-M3_DOCUMENTPREFIX="search_document: "
|
||||
EMBEDDING_BGE-M3_QUERYPREFIX="search_query: "
|
||||
```
|
||||
|
||||
Gruppen gehen über `EMBEDDING_GROUPS=main` plus `EMBEDDING_GROUP_MAIN_MEMBERS=...`, `..._POLICY=...`, `..._TIMEOUT_TOTAL=...`.
|
||||
|
||||
## Admin-Tokens
|
||||
|
||||
Server-Admin-Tokens stehen unter `api.adminTokens`. Jedes Token hat `token`, `label` und `permissions`:
|
||||
|
||||
```yaml title="config.yml"
|
||||
api:
|
||||
adminTokens:
|
||||
- token: ersetze-mich-durch-zufall
|
||||
label: backup-job
|
||||
permissions: ["project.backup", "project.reload"]
|
||||
```
|
||||
|
||||
- Syntax: `domain.action` (`project.read`, `user.create`, `server.shutdown`).
|
||||
- Kurzform: `project` meint `project.*`. `*` matcht genau ein Segment (`namespace.*.articles.read`).
|
||||
- Gültige Domänen: `user`, `project` (plus `reload`, `backup`, `regenerate-search`, `snapshot`), `team`, `org`, `audit.read`, `llm.chat`, `namespace…`.
|
||||
- Header: `X-Admin-Token: …` oder `Authorization: Bearer …`.
|
||||
|
||||
## Abnahme über den Version-Endpunkt
|
||||
|
||||
<Steps>
|
||||
1. Server starten und warten, bis das Config-Dump im Log steht.
|
||||
2. Version abfragen — antwortet sie, stimmen Port und Prefix:
|
||||
```bash title="Version prüfen"
|
||||
curl http://localhost:8080/api/v1/version
|
||||
# {"version":"0.0.0"}
|
||||
```
|
||||
3. Bei Fehlern zuerst `DB_DIAL`, `DB_PREFIX` und `API_PORT` prüfen (Tabelle oben).
|
||||
</Steps>
|
||||
|
||||
Weiter: [Betrieb und Beobachtung](/server/betrieb/) zeigt Profile und Metriken. [Sicherheit](/server/sicherheit/) erklärt Token-Arten und Ratelimit.
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server); Starter-Compose am `e3f92ebf` 2026-10-14 (tibi-svelte-starter); Go-Version aus `go.mod`.
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: 'Schnittstellen im Überblick'
|
||||
description: 'REST, SSE, MCP, OpenAPI: was wofür gilt und wo die Referenz liegt.'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
Der **tibi-server** spricht vier Sprachen: REST, SSE-Streams, MCP und OpenAPI. Alle hängen unter `/api/v1` (außer `/metrics`). Quelle: `internal/controller/setup.go`.
|
||||
|
||||
```text title="API-Baum im Überblick"
|
||||
/api/v1
|
||||
├── System: /login /token/_scopes /llm/* /audit /admin/* /version /mcp
|
||||
├── Projekt: /_/:project/:collection … /_/:project/_actions/:action
|
||||
│ /_/:project/_/assets/… /_/:project/_/admin/…
|
||||
└── Schema: /_openapi/:project
|
||||
/metrics (Prometheus, außerhalb)
|
||||
```
|
||||
|
||||
## REST: System gegen Collection
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="System-Endpunkte">
|
||||
|
||||
| Methode | Pfad | Wofür |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/version` | Lebenszeichen (`{"version":"…"}`) |
|
||||
| `POST` | `/api/v1/login`, `/api/v1/login/impersonate` | JWT holen / als Admin schlüpfen |
|
||||
| `GET` | `/api/v1/token/_scopes` | Gültige PAT-Scopes auflisten |
|
||||
| `POST` / `GET` | `/api/v1/llm/chat`, `/api/v1/llm/providers` | Chat (ohne Secrets) / Provider-Namen |
|
||||
| `GET` | `/api/v1/audit` | Audit-Logs (`audit.read`) |
|
||||
| `GET` / `POST` | `/api/v1/admin/embedding/status`, `/api/v1/admin/shutdown` | Embedding-Status / Stop (`server.shutdown`) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Collection-Endpunkte">
|
||||
|
||||
| Methode | Pfad | Wofür |
|
||||
|---|---|---|
|
||||
| `GET` / `POST` | `/_/:project/:collection` | Suchen / anlegen |
|
||||
| `GET` | `/_/:project/:collection/_count`, `/_facet`, `/_queue` | Zählen / Facetten / Warteschlange |
|
||||
| `GET` / `PUT` / `DELETE` | `/_/:project/:collection/:id` | Lesen / ersetzen / löschen |
|
||||
| `PUT` / `DELETE` | `/_/:project/:collection` (ohne ID) | Bulk-Update / Bulk-Delete |
|
||||
| `GET` | `/_/:project/:collection/:id/:field`, `…/:field/*path` | Einzelnes Feld / Datei |
|
||||
| `GET` / `POST` | `/_/:project/_actions/:action` | Hook-Aktion ohne Collection |
|
||||
| `GET` | `/_/:project/_/assets/:name/*path` | Statische Assets |
|
||||
|
||||
Trick für lange Filter: `POST` mit Header `X-HTTP-Method-Override: GET` — der Body wird zur Query.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Projekt-Admin">
|
||||
|
||||
| Methode | Pfad | Recht |
|
||||
|---|---|---|
|
||||
| `POST` | `/_/:project/_/admin/reload` | `project.reload` |
|
||||
| `POST` / `GET` | `/_/:project/_/admin/regenerate-search`, `…/search-worker/status` | `project.regenerate-search` |
|
||||
| `GET` | `/_/:project/_/admin/embedding/status` | Projekt-Admin |
|
||||
| `GET` / `POST` / `DELETE` | `/_/:project/_/admin/backup…` | `project.backup` |
|
||||
| `GET` / `POST` / `DELETE` | `/_/:project/_/admin/snapshots…` | `project.snapshot` |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Alle Antworten sind gzip-komprimiert, Body-Limits kaskadieren (Server → Projekt → Collection). Erstellen meldet `201`, Löschen `204` — außer das Projekt will Legacy-Codes.
|
||||
|
||||
## SSE (Echtzeit und Fortschritt)
|
||||
|
||||
- Der Realtime-Hub ist ein In-Memory-Pub/Sub **je Projekt**: benannte Kanäle, fester Ringpuffer (Standard 256), Nachhol-Optionen (`LastN`, `MaxAge`, TTL).
|
||||
- Volle Kanäle werfen wahlweise Alt oder Neu weg — langsame Leser blockieren nie den Server.
|
||||
- **Kein fertiger Abo-Endpunkt:** Der Server liefert keine HTTP-Route für Collection-Abos. Du erreichst den Hub nur über Hooks (`channel.send` / `channel.subscribe`) — typisch als eigene Action (`/_/:project/_actions/:action`), die du selbst als SSE auslieferst.
|
||||
- `text/event-stream` liefert der Server fertig nur an zwei Stellen: `POST /api/v1/llm/chat` (Chat-Streaming) und Backup-Analyse-Download (`.../admin/backup/analyze-download`). Snapshot-Streaming ist dagegen Datei-Download mit RAM-Begrenzung, kein SSE.
|
||||
- Backup- und Snapshot-Läufe melden Fortschritt als Event-Stream. Bricht der Stream ab, war es das Netzwerk — der Lauf selbst geht weiter.
|
||||
|
||||
## MCP (KI-Werkzeuge)
|
||||
|
||||
| Fakt | Wert |
|
||||
|---|---|
|
||||
| Transport | Streamable-HTTP, JSON-RPC 2.0 |
|
||||
| Route | fest `POST` + `GET /api/v1/mcp` — ein gesetztes `mcp.route`/`MCP_ROUTE` wird ignoriert (Routing hart in `setup.go`) |
|
||||
| Auth | nur PAT via `X-Auth-Token` — kein JWT, kein Admin-Token |
|
||||
| Rechte | jede Prüfung nutzt die gleichen Gates wie REST (nichts kopiert) |
|
||||
| Ergebnis-Deckel | `mcp.maxResultItems` (Standard 100) |
|
||||
| Admin-Werkzeuge | nur mit `mcp.allowAdminActions: true`, weiter pro Scope begrenzt |
|
||||
|
||||
```bash title="MCP-Tools auflisten (describe)"
|
||||
curl -X POST http://localhost:8080/api/v1/mcp \
|
||||
-H "X-Auth-Token: tibi_pat_..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
||||
```
|
||||
|
||||
## OpenAPI-Generator
|
||||
|
||||
`GET /api/v1/_openapi/:project` erzeugt die Spezifikation **deines** Projekts frisch aus Config und Collections. Nutze sie für Client-Generierung und als Klick-Referenz. Die Version oben (`/api/v1/version`) sagt dir, ob Server und Spec zusammenpassen.
|
||||
|
||||
## Welche Referenz wofür
|
||||
|
||||
| Frage | Antwort liegt in |
|
||||
|---|---|
|
||||
| Wie heißt Config-Schlüssel X? | [Server-Konfiguration](/server/konfiguration/) |
|
||||
| Wer darf Endpunkt Y? | [Sicherheit](/server/sicherheit/) (Tokens, Scopes) |
|
||||
| Läuft der Server gesund? | [Betrieb und Beobachtung](/server/betrieb/) (Version, Metriken) |
|
||||
| Wie frage ich Daten ab? | Projekt-Seiten ab [Begriffe](/einstieg/begriffe/) |
|
||||
| Was ist Tibi überhaupt? | [Was ist Tibi?](/einstieg/was-ist-tibi/) |
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: 'Sicherheit'
|
||||
description: 'Tokens, CORS-Kaskade, Ratelimit, Pfad-Regeln, Isolation als Betreiber-Überblick.'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
Du sicherst den **tibi-server** auf vier Ebenen: Tokens, CORS, Ratelimit und Dateipfade plus Prozess-Isolation. Quelle ist der Code (`internal/config`, `internal/ratelimit`, `internal/suexec`, `internal/controller/setup.go`).
|
||||
|
||||
## Token-Arten
|
||||
|
||||
<Badge text="X-Admin-Token" variant="danger" /> <Badge text="X-Auth-Token" variant="tip" /> <Badge text="Token" variant="note" /> <Badge text="Bearer" variant="caution" />
|
||||
|
||||
| Art | Header | Wofür | Scope-Syntax |
|
||||
|---|---|---|---|
|
||||
| Server-Admin-Token | `X-Admin-Token` oder `Authorization: Bearer` | Betrieb: Reload, Backup, Snapshots, Shutdown (`server.shutdown` nur für Stop) | `domain.action`, Kurzform (`project` = `project.*`), `*` je Segment |
|
||||
| Benutzer-JWT | `X-Auth-Token` | Anmeldung per `/api/v1/login`; Lebensdauer via `API_ACCESS_TOKEN_LIFETIME` (Standard `60m`) | Rollen + Collection-Rechte |
|
||||
| Personal Access Token (PAT) | `X-Auth-Token` (`tibi_pat_…`) | Dauerhafte Maschinen-Tokens, immer aktiv | gleiche Syntax wie Admin-Tokens; es gilt **Token-Recht UND Besitzer-Recht** |
|
||||
| MCP | `X-Auth-Token` (nur PAT) | KI-Werkzeugzugriff über `/api/v1/mcp` | PAT-Scopes, Admin-Aktionen nur mit `mcp.allowAdminActions: true` |
|
||||
|
||||
Gültige Domänen: `user`, `project` (`read/create/update/delete`, `reload`, `backup`, `regenerate-search`, `snapshot`), `team`, `org`, `audit.read`, `llm.chat`, `namespace.<ns>.<collection>.<aktion>`.
|
||||
|
||||
```text title="Scope-Abgleich (Beispiel)"
|
||||
Anfrage-Recht: namespace.shop.artikel.read
|
||||
Token-Scope: namespace.shop → passt (Kurzform)
|
||||
Token-Scope: namespace.*.artikel.read → passt (Wildcard)
|
||||
Besitzer darf es nicht → trotzdem verboten (UND-Regel)
|
||||
```
|
||||
|
||||
<Aside type="caution" title="Entfernt: TOKENS_ENABLED">
|
||||
Ein Flag `TOKENS_ENABLED` gibt es nicht mehr. PATs sind Kernfunktion und immer an. Wer alte Compose-Dateien mit diesem Flag findet, löscht es ersatzlos.
|
||||
</Aside>
|
||||
|
||||
## CORS-Kaskade
|
||||
|
||||
Jeder Request bekommt genau eine CORS-Config — vom Spezifischen zum Allgemeinen:
|
||||
|
||||
```text title="CORS-Kaskade"
|
||||
/_actions/<name> → /<collection> → / → "" → keine (Browser blockt)
|
||||
```
|
||||
|
||||
- Die Kaskade steht pro Projekt (`CORSMap`), nicht global. Der `cors:`-Abschnitt der Server-Config ist die **Basis**, Projekt- und Collection-Werte mergen feldweise dazu (`merge: true` hängt an, `merge: false` ersetzt). Globales CORS ist auskommentiert — ohne Projekt-Kontext greift gar kein CORS.
|
||||
- Action-Endpunkte (`/_actions/…`) haben eigene Einträge — prüfe sie separat.
|
||||
- Preflight ohne echte Route antwortet `404` (kein offenes `OPTIONS`-Loch).
|
||||
|
||||
## Ratelimit (Login)
|
||||
|
||||
Der Login-Limiter bremst Fehlversuche mit exponentiellem Backoff — je IP **und** je Benutzername:
|
||||
|
||||
| Schlüssel | Standard | Env |
|
||||
|---|---|---|
|
||||
| Erste Verzögerung | `1s` | `RATELIMIT_LOGIN_INITIAL_DELAY` |
|
||||
| Maximale Verzögerung | `60s` | `RATELIMIT_LOGIN_MAX_DELAY` |
|
||||
| Zähler-Reset nach Ruhe | `15m` | `RATELIMIT_LOGIN_RESET_AFTER` |
|
||||
| Ganz abschalten | an (`true`) | `RATELIMIT_ENABLED=false` (nur Notfall) |
|
||||
|
||||
Erfolg setzt beide Zähler zurück. Gebremste Versuche antworten mit **429** plus `Retry-After`-Header (Sekunden) — warte sie ab statt zu hämmern. Das Limit gilt **nur für Login**, nicht global.
|
||||
|
||||
## Pfade und suexec-Isolation
|
||||
|
||||
| Schalter | Wirkung |
|
||||
|---|---|
|
||||
| `security.allowAbsolutePaths` (`SECURITY_ALLOWABSOLUTEPATHS`) | Absolute Dateipfade erlauben — sonst verboten |
|
||||
| `security.allowUpperPaths` (`SECURITY_ALLOWUPPERPATHS`) | `..`-Pfade erlauben — sonst verboten |
|
||||
| suexec (kein Schalter) | Hooks und Jobs laufen unter der Projekt-UID/GID in eigenen Thread-Pools |
|
||||
|
||||
Der Starter setzt beide Pfad-Schalter lokal auf `"true"`. In Produktion lässt du sie aus, außer du weißt genau warum.
|
||||
|
||||
## Checkliste für den Go-Live
|
||||
|
||||
<Steps>
|
||||
1. `jwtSecret` gesetzt (lang, zufällig) und `secureCookies: true` (HTTPS).
|
||||
2. Admin-Tokens minimal vergeben: ein Token, ein Zweck, enge Scopes.
|
||||
3. `RESPONSE_ERROR_STACK` aus — keine Stacktraces an Clients.
|
||||
4. Pfad-Schalter aus, außer nötig. Ratelimit an.
|
||||
5. `sentry.dsn` gesetzt, `/metrics` nur intern erreichbar.
|
||||
6. CORS je Collection geprüft — keine `*`-Origins mit Credentials.
|
||||
</Steps>
|
||||
|
||||
Config-Schlüssel stehen unter [Server-Konfiguration](/server/konfiguration/). Laufende Kontrolle unter [Betrieb und Beobachtung](/server/betrieb/).
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server).
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Server-Übersicht
|
||||
description: Wie der Tibi-Server aufgebaut ist und wo welche Referenzseite hilft.
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
|
||||
> **Handseite.** Wird vom Sync nie überschrieben (`keep`). Stand wird am Code verifiziert, nicht aus alten Docs übernommen.
|
||||
|
||||
**Tibi-server** ist das Go-Backend (MongoDB, Gin, goja-Hooks, SSE, LLM, Jobs, Audit). Details stehen in den synchronisierten Referenzseiten daneben — diese Seite ordnet nur.
|
||||
|
||||
:::note title="Projekt-Entwickler nutzen, nicht weiterentwickeln"
|
||||
Als Projekt-Entwickler entwickelst du den Server nicht weiter. Du **nutzt** ihn: `api/config.yml` plus `api/collections/*.yml` und `api/hooks/*.js` in deinem geklonten Starter. Go-Code fasst du nicht an.
|
||||
:::
|
||||
|
||||
## Wo finde ich was?
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Server-Konfiguration">
|
||||
`config.yml`, Env-Overrides, Admin-Tokens (am Code verifiziert, inkl. Go-Version und Legacy-Default). [Lesen](/server/konfiguration/).
|
||||
</Card>
|
||||
<Card title="Betrieb und Beobachtung">
|
||||
Profile, Version/Metriken, Reload, Jobs, Störungen. [Lesen](/server/betrieb/).
|
||||
</Card>
|
||||
<Card title="Sicherheit">
|
||||
Token-Arten, CORS-Kaskade, Login-Ratelimit (429), Pfad-Schalter, suexec. [Lesen](/server/sicherheit/).
|
||||
</Card>
|
||||
<Card title="Schnittstellen">
|
||||
REST (System vs. Collection), SSE, MCP (`/api/v1/mcp` fest), OpenAPI-Generator. [Lesen](/server/schnittstellen/).
|
||||
</Card>
|
||||
<Card title="Projekt-Sicht">
|
||||
[Sammlungen](/projekt/sammlungen-anlegen/), [Hooks](/projekt/hooks-schreiben/), [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/).
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
Verifiziert am 68b42785 2026-10-15 (tibi-server).
|
||||
|
||||
## Nächster Schritt
|
||||
|
||||
- Neues Projekt? Siehe [Neues Projekt aufsetzen](/einstieg/neues-projekt/).
|
||||
Reference in new issue
Block a user