# Fabric Administrator & Developer Guide

This guide is for the people who run, deploy, secure, integrate, and extend Fabric: administrators standing up the REST API, DevOps engineers packaging it, security reviewers auditing it, and developers contributing to or building on the code base. It documents Fabric's architecture, configuration surface, security posture, operational procedures, and internals in enough depth to operate it confidently or reconstruct how it works.

For end-user installation and CLI usage, see the companion **User Guide**. This document assumes that baseline and goes deeper.

---

## 1. Executive Summary

**Fabric** is an open-source Go framework that turns AI prompts into reusable, version-controlled **Patterns** and applies them to arbitrary input through any of ~30 AI providers. It is delivered as a set of standalone binaries plus a SvelteKit web application.

| Aspect | Detail |
|---|---|
| Language | Go (single-module repo, `github.com/danielmiessler/fabric`) |
| Primary binary | `cmd/fabric` — the CLI and REST/Ollama server |
| Helper binaries | `cmd/to_pdf`, `cmd/code2context`, `cmd/generate_changelog` |
| Web app | `web/` — SvelteKit 5 + Tailwind/Skeleton UI |
| CLI parser | `jessevdk/go-flags` (struct-tag flags) |
| REST framework | `gin-gonic/gin` with Swagger (`swaggo`) |
| Persistence | Filesystem under `~/.config/fabric/` (patterns, sessions, contexts, `.env`); SQLite only in `generate_changelog` |
| State model | No database server; patterns/sessions/contexts are files |
| AI providers | OpenAI, Anthropic, Gemini, Ollama, Azure (OpenAI/Entra/Gateway), Bedrock, Vertex AI, LM Studio, Perplexity, Codex, Copilot, DigitalOcean, Exolab, plus many OpenAI-compatible vendors |
| Auth (server) | Optional shared API key via `X-API-Key` header |
| License | MIT |

**Architectural thesis:** Fabric is a thin, well-factored orchestration layer. A CLI front end parses flags, a **PluginRegistry** wires together vendors and tools, a **Chatter** assembles a system prompt (strategy + context + pattern + input) and streams it to a vendor, and a filesystem-backed DB stores reusable artifacts. The same core powers a REST API and an Ollama-compatible shim.

```text
         ┌──────────── cmd/fabric (CLI) ────────────┐
         │  internal/cli: flags + command handlers   │
         └───────────────────┬───────────────────────┘
                             │
                ┌────────────▼─────────────┐
                │  internal/core            │
                │  PluginRegistry + Chatter │
                └───┬───────────┬───────────┘
         ┌──────────┘           └───────────┐
 ┌───────▼────────┐               ┌─────────▼─────────┐
 │ plugins/ai/*   │               │ plugins/db/fsdb   │
 │ (vendor clients)│              │ patterns/sessions │
 └────────────────┘               │ contexts / .env   │
 ┌────────────────┐               └───────────────────┘
 │ plugins/template│  ← variables, plugins, extensions
 └────────────────┘
         ▲
         │ same core
 ┌───────┴────────────────┐
 │ internal/server (gin)  │  ← REST API + Ollama shim + Swagger
 └────────────────────────┘
```

---

## 2. Architecture

### 2.1 Request lifecycle (CLI)

The entry point `cmd/fabric/main.go` calls `cli.Cli(version)`. That function (`internal/cli/cli.go`) runs a fixed pipeline:

1. **`Init()`** parses flags (`internal/cli/flags.go`), then merges YAML config for any flag with a `yaml:` tag (CLI wins), and reads stdin if piped.
2. **i18n init** with the requested/detected language.
3. **`initializeFabric()`** opens the filesystem DB at `~/.config/fabric` and builds the `PluginRegistry`.
4. A chain of handlers runs, each returning `(handled, error)` and short-circuiting if it handled the request:
   - `handleSetupAndServerCommands` — `--setup`, `--serve`, `--serveOllama`
   - `handleConfigurationCommands` — `--updatepatterns`, `--changeDefaultModel`
   - `handleListingCommands` — `--listpatterns/-models/-contexts/-sessions/-strategies/-vendors`, voices, transcription models
   - `handleManagementCommands` — `--wipecontext/-session`, `--printcontext/-session`
   - `handleExtensionCommands` — `--listextensions/--addextension/--rmextension`
   - tool processing (`--youtube`, `--scrape_url/-question`, `--spotify`), transcription, HTML readability
5. If none short-circuit, **`handleChatProcessing`** builds a `ChatRequest`/`ChatOptions`, obtains a `Chatter`, and streams the response.

### 2.2 The PluginRegistry (`internal/core/plugin_registry.go`)

`PluginRegistry` is the composition root. On construction it instantiates every vendor client and every tool, then configures them from the environment.

```go
type PluginRegistry struct {
    Db                 *fsdb.Db
    VendorManager      *ai.VendorsManager   // configured (usable) vendors
    VendorsAll         *ai.VendorsManager   // every known vendor
    Defaults           *tools.Defaults      // default vendor/model/context-length
    PatternsLoader     *tools.PatternsLoader
    CustomPatterns     *custom_patterns.CustomPatterns
    YouTube            *youtube.YouTube
    Language           *lang.Language
    Jina               *jina.Client
    Spotify            *spotify.Spotify
    TemplateExtensions *template.ExtensionManager
    Strategies         *strategy.StrategiesManager
}
```

- **`VendorsAll`** holds all registered vendors (sorted case-insensitively by name). **`VendorManager`** holds only those that successfully `Configure()` (i.e., have valid env settings). `ConfigureVendors()` rebuilds `VendorManager` from `VendorsAll`.
- **`GetChatter(model, ctxLen, vendor, stream, dryRun)`** resolves which vendor serves a model: case-insensitive model normalization, `vendor/model` prefix parsing (e.g. `ollama/llama3`), ambiguity warnings when multiple vendors offer a model, and a Codex pass-through for subscription-hidden models. If `dryRun`, it swaps in the `dryrun` client.
- **`SaveEnvFile()`** serializes every plugin's settings back into `~/.config/fabric/.env`.

Vendors registered (see `NewPluginRegistry`): OpenAI, DigitalOcean, Ollama, Azure, Azure AI Gateway, Azure Entra, Gemini, Anthropic, Vertex AI, LM Studio, Exolab, Perplexity, Codex, Copilot (M365), Bedrock — plus all entries in `openai_compatible.ProviderMap`.

### 2.3 The Chatter (`internal/core/chatter.go`)

`Chatter.Send()` is where a request becomes a model call.

1. `BuildSession()` assembles the conversation. The **system prompt** is built by `joinPromptSections(strategy, context, pattern)` — strategy first, then context, then pattern — and the user input is appended (or merged, in `--raw` mode).
2. Pattern variables are resolved via `template.ApplyTemplate` when `InputHasVars` is set; the pattern itself is loaded with variables applied unless `NoVariableReplacement`.
3. Language enforcement: if a non-English language is requested, an instruction to translate the response is wrapped around the system prompt.
4. The vendor is called via `Send()` (blocking) or `SendStream()` (streaming through a channel). Streaming aggregates content, surfaces `usage` metadata when `--show-metadata` is set, and records the first stream error.
5. `--suppress-think` strips `<think>…</think>` blocks (configurable tags) after generation.
6. Special case: when the pattern is **`create_coding_feature`**, the response is parsed for a file-changes block and applied to disk (`domain.ParseFileChanges` / `ApplyFileChanges`).
7. If a session name was given, the turn is persisted.

> [!NOTE]
> `Chatter` normalizes the model name to the vendor's canonical casing before sending, and forces `Raw = true` when the vendor reports `NeedsRawMode(model)` (e.g. certain Perplexity/Ollama/OpenAI models).

### 2.4 Domain model (`internal/domain`)

| Type | Role |
|---|---|
| `ChatRequest` | Pattern/context/session/strategy names, variables, message, language |
| `ChatOptions` | Model + all generation params (temperature, top-p, penalties, seed, thinking, image/audio, search, notification) |
| `Attachment` | File/URL/bytes attachment with mimetype detection and base64 encoding |
| `StreamUpdate` / `UsageMetadata` | Unified streaming payload + normalized token counts |
| `ThinkingLevel` | `off`/`low`/`medium`/`high` with token budgets (1024/2048/4096) |
| `FileChange` | `create`/`update` operation for `create_coding_feature` |

Default generation values are pinned as constants (`DefaultTemperature=0.7`, `DefaultTopP=0.9`, penalties `0.0`) and must match the CLI flag defaults.

### 2.5 The plugin/settings framework (`internal/plugins`)

Every vendor and tool implements the `Plugin` interface:

```go
type Plugin interface {
    GetName() string
    GetSetupDescription() string
    IsConfigured() bool
    Configure() error
    Setup() error
    SetupFillEnvFileContent(*bytes.Buffer)
}
```

`PluginBase` provides the machinery: `Setting`s map to environment variables built from the plugin name (`BuildEnvVariablePrefix` uppercases the name and replaces spaces/hyphens with `_`, e.g. `LM Studio` → `LM_STUDIO_`). `Configure()` reads env vars into settings and validates required ones; `Setup()` interactively asks questions; `SetupFillEnvFileContent()` serializes back to `.env`. Booleans accept `1/true/yes/on` and `0/false/no/off`.

### 2.6 Filesystem database (`internal/plugins/db/fsdb`)

There is no database server. The "DB" is the directory tree under `~/.config/fabric/`:

| Entity | Storage |
|---|---|
| Patterns | `patterns/<name>/system.md` (+ custom patterns directory) |
| Contexts | `contexts/<name>` (plain text) |
| Sessions | `sessions/<name>` (serialized chat history) |
| Config | `.env` (keys + defaults), `config.yaml` (optional) |
| Strategies | `strategies/<name>.json` |
| Extensions | `extensions/extensions.yaml` (registry) |

Custom patterns live in a separate directory (env `CUSTOM_PATTERNS_DIRECTORY`) and take precedence over built-ins, so `--updatepatterns` never clobbers them.

### 2.7 The template engine (`internal/plugins/template`)

`ApplyTemplate(content, variables, input)` resolves `{{…}}` tokens iteratively until none remain (detecting stuck/unresolvable templates). Token kinds:

- `{{input}}` — the user input (internally protected by `__FABRIC_INPUT_SENTINEL_TOKEN__`).
- `{{variable}}` — looked up in the variables map (errors if missing).
- `{{plugin:NS:OP:VAL}}` — built-in plugins: `text` (upper/lower/title/trim), `datetime`, `file` (read/tail/exists/size/modified), `fetch` (get), `sys` (hostname/user/os/arch/env/pwd/home).
- `{{ext:NAME:OP:VAL}}` — registered external extension.

Each built-in plugin enforces guardrails: `file` blocks `..` traversal and caps reads at 1 MB; `fetch` caps at 1 MB and only accepts text-like content types; `sys` exposes only whitelisted operations.

### 2.8 Extensions subsystem (`internal/plugins/template`)

Extensions run external programs from inside patterns. The **registry** records a SHA-256 hash of both the YAML config and the executable; before execution, the executor re-hashes and refuses to run if either changed (tamper detection). An extension declares `operations` with `cmd_template`s and an output method (`stdout` or `file`, with optional `path_from_stdout`, `work_dir`, `cleanup`, and a per-extension `timeout`). Pipe-separated values become numbered template variables (`{{1}}`, `{{2}}`).

### 2.9 REST API server (`internal/server`)

`Serve(registry, address, apiKey)` builds a Gin engine (`gin.New()` + `Logger` + `Recovery`), optionally adds `APIKeyMiddleware`, mounts Swagger, and registers handlers. `ServeOllama` additionally mounts Ollama-compatible routes. Storage entities (patterns/contexts/sessions) share a generic `StorageHandler[T]` exposing CRUD routes.

### 2.10 Web application (`web/`)

A SvelteKit 5 SPA (Tailwind + Skeleton UI, Vite 8) that talks to the REST API. Notable build wiring: a `prebuild`/`predev` step copies `scripts/pattern_descriptions/pattern_descriptions.json` into `static/data/`. The base API URL is read from `FABRIC_BASE_URL` (`web/src/lib/config/environment.ts`).

### 2.11 Internationalization (`internal/i18n`)

All user-facing strings flow through `i18n.T(key)`. Locale files live in `internal/i18n/locales/` for ~10 languages. Language is detected from `--language`/`-g`, then `LC_ALL`/`LC_MESSAGES`/`LANG`.

---

## 3. System Requirements

| Component | Requirement |
|---|---|
| OS | Linux, macOS, Windows (amd64 and arm64 binaries published) |
| Runtime | None for prebuilt binaries (statically linked Go) |
| Build toolchain | Go (recent toolchain per `go.mod`) for `go install`/from-source |
| Web app build | Node.js ≥ 20, a package manager (pnpm/npm), for `web/` |
| Optional: PDF | `pdflatex` (TeX Live/MiKTeX) for `to_pdf` |
| Optional: media | `yt-dlp` and `ffmpeg` for YouTube visual extraction and media splitting |
| Optional: Swagger regen | `swag` CLI to regenerate `docs/swagger.yaml` |
| Network | Outbound HTTPS to your chosen AI provider(s); none if using purely local Ollama/LM Studio |
| Disk | Minimal; patterns + sessions are small text files |

> [!NOTE]
> SQLite (`mattn/go-sqlite3`, which uses cgo) is only a dependency of the `generate_changelog` helper, not the core CLI or server.

---

## 4. Configuration Mastery

### 4.1 Configuration precedence

For any flag carrying a `yaml:` tag, effective value = **CLI flag > YAML config > built-in default**. Environment variables drive **plugin/provider** configuration (API keys, URLs) and a few behaviors (per-pattern model, custom patterns directory). The `.env` file is loaded into the process environment.

### 4.2 `.env` — provider keys and defaults

Stored at `~/.config/fabric/.env`, written by setup and `SaveEnvFile()`. Provider variables follow the `<VENDOR>_<SETTING>` convention. Common keys:

| Variable | Used for |
|---|---|
| `OPENAI_API_KEY` | OpenAI |
| `ANTHROPIC_API_KEY` | Anthropic (Claude) |
| `GEMINI_API_KEY` | Google Gemini |
| `GROQ_API_KEY` | Groq |
| `MISTRAL_API_KEY` | Mistral |
| `OPENROUTER_API_KEY` | OpenRouter |
| `SILICON_API_KEY` | SiliconCloud |
| `DEEPSEEK_API_KEY` | DeepSeek |
| `GROKAI_API_KEY` | GrokAI |
| `OLLAMA_URL` | Ollama base URL |
| `LM_STUDIO_API_BASE_URL` | LM Studio base URL |
| `CUSTOM_PATTERNS_DIRECTORY` | Path to private custom patterns (supports `~/`) |
| `DEFAULT_VENDOR` / `DEFAULT_MODEL` / `DEFAULT_MODEL_CONTEXT_LENGTH` | Default selection |
| `FABRIC_MODEL_<PATTERN>` | Per-pattern model override, value `vendor|model` |
| `FABRIC_ALIAS_PREFIX` | Prefix for generated pattern aliases (shell) |
| `GITHUB_TOKEN` | Used by `generate_changelog` |

> [!IMPORTANT]
> The `.env` file contains secrets. Restrict its permissions (the parent dir is created `0755`, the file `0644` by default — tighten to `0600` on shared hosts) and never commit it.

### 4.3 `config.yaml` — declarative defaults

Located at `~/.config/fabric/config.yaml` (or `--config <path>`). Only flags with a `yaml:` tag are honored. Example (`internal/cli/example.yaml`):

```yaml
pattern: ai
model: phi3:latest
modelContextLength: 2048
frequencypenalty: 0.5
presencepenalty: 0.5
topp: 0.67
temperature: 0.88
seed: 42
stream: true
raw: false
suppressThink: false
thinkStartTag: "<think>"
thinkEndTag: "</think>"
disableResponsesAPI: true   # for llama-server / OpenAI-compatible local servers
```

YAML-eligible flags include: `pattern`, `model`, `vendor`, `modelContextLength`, `temperature`, `topp`, `presencepenalty`, `frequencypenalty`, `stream`, `raw`, `seed`, `suppressThink`, `thinkStartTag`, `thinkEndTag`, `disableResponsesAPI`, `transcribeFile`, `transcribeModel`, `splitMediaFile`, `voice`, `notification`, `notificationCommand`, `thinking`, `ytDlpArgs`.

### 4.4 Per-pattern model mapping

`FABRIC_MODEL_<PATTERN_NAME>=vendor|model` (uppercase, hyphens → underscores) pins a model to a pattern when `-m` is not passed. Resolved in `handleChatProcessing`.

```bash
export FABRIC_MODEL_SUMMARIZE="Anthropic|claude-opus-4-5"
export FABRIC_MODEL_ANALYZE_CODE="OpenAI|gpt-4o"
```

### 4.5 Strategies as config

Strategies are JSON files (`{"description": "...", "prompt": "..."}`) in `~/.config/fabric/strategies/`. The bundled set (`data/strategies/`): `aot`, `cod`, `cot`, `ltm`, `reflexion`, `self-consistent`, `self-refine`, `standard`, `tot`. Add your own by dropping a JSON file in the directory; the `--strategy` flag and the `/strategies` endpoint pick it up.

---

## 5. Security

Fabric's threat surface spans local file/command execution (template plugins, extensions, `create_coding_feature`), the REST server, and secret handling. The code base contains deliberate hardening; administrators should understand and, where applicable, augment it.

### 5.1 REST API authentication

`APIKeyMiddleware` (`internal/server/auth.go`) requires the `X-API-Key` header to match the `--api-key` value, returning `401 Missing API Key` / `401 Wrong API Key`. Swagger paths (`/swagger/*`) are intentionally exempt so docs are browsable.

> [!CAUTION]
> If `--api-key` is empty, the server logs `Starting REST API server without API key authentication` and serves **all routes unauthenticated**. Never expose an unauthenticated server beyond localhost. Always run behind TLS (a reverse proxy) and set a strong key in production.

### 5.2 Secret masking

`GET /config` masks API keys to their last 4 characters (`maskAPIKey`, CWE-200) and returns URLs in the clear. `POST /config/update` ignores values that still contain `*` (a redacted placeholder) so a round-trip through the UI never corrupts a stored key.

### 5.3 File and fetch guardrails (template plugins)

- **`file` plugin**: rejects paths containing `..`, expands `~/` safely, caps reads at 1 MB.
- **`fetch` plugin**: 1 MB cap, UTF-8 + null-byte validation, only text-like content types; documented as making outbound requests — consider an allowlist in hardened deployments.
- **`sys` plugin**: only whitelisted operations; `env` reads arbitrary environment variables, so avoid putting secrets in patterns' reach.

### 5.4 Extension integrity

The extension registry stores SHA-256 hashes of the config and the executable. `GetExtension`/`Verify` re-hash before use and refuse to run on mismatch, detecting post-registration tampering. Extensions execute via `sh -c`, honor a per-extension timeout, and can clean up file output.

> [!WARNING]
> Extensions are arbitrary local command execution by design. Treat registering an extension as granting code-execution trust. Review the executable and `cmd_template`. Do not register extensions from untrusted pattern bundles.

### 5.5 `create_coding_feature` file writes

When this pattern runs, model output is parsed for a file-changes block and written to disk. `domain.ParseFileChanges` validates: operation must be `create`/`update`, path must be non-empty, paths containing `..` are rejected (traversal guard), and content > 10 MB is rejected. Still, run it only inside a git repo and review `git diff`.

### 5.6 Notification command injection hardening

Custom notification commands receive the title and message as **positional shell arguments** (`sh -c '<cmd> "$1" "$2"' -- title message`), not via string interpolation — preventing backtick/`$()`/`;`/quote injection (covered by `TestSendNotification_SecurityEscaping`).

### 5.7 Network and CORS

The streaming chat handler sets `Access-Control-Allow-Origin: http://localhost:5173` (the dev web app origin) and HSTS on error responses. Adjust CORS for your deployment if the web app is served elsewhere; place the API behind a reverse proxy that terminates TLS and sets appropriate headers.

### 5.8 Secret hygiene checklist

- [ ] `.env` permissions tightened (`chmod 600`) on shared hosts
- [ ] Server started with a strong `--api-key`
- [ ] API reachable only via TLS reverse proxy, not directly on `0.0.0.0:8080`
- [ ] Extensions reviewed and limited to trusted executables
- [ ] `create_coding_feature` used only in disposable/reviewed working trees
- [ ] No secrets embedded in patterns (the `sys:env` plugin can read them)

---

## 6. Deployment & Operations

### 6.1 Running the REST API

```bash
# Local, key-protected
fabric --serve --api-key "$FABRIC_API_KEY" --address ":8080"

# Ollama-compatible shim (adds /api/tags, /api/chat, /api/version)
fabric --serve --serveOllama --api-key "$FABRIC_API_KEY"
```

Bind to localhost and front it with a reverse proxy for TLS:

```nginx
server {
    listen 443 ssl;
    server_name fabric.example.com;
    # ssl_certificate / ssl_certificate_key ...
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header X-API-Key $http_x_api_key;
        proxy_buffering off;            # required for SSE streaming
        proxy_set_header Connection '';
        proxy_http_version 1.1;
    }
}
```

> [!IMPORTANT]
> The `/chat` endpoint streams Server-Sent Events. Disable proxy buffering (`proxy_buffering off`, and Fabric already sets `X-Accel-Buffering: no`) or clients will see responses only after completion.

### 6.2 Containerized deployment

```bash
docker run -d --restart unless-stopped \
  -p 127.0.0.1:8080:8080 \
  -v $HOME/.fabric-config:/home/appuser/.config/fabric \
  kayvan/fabric:latest \
  --serve --api-key "$FABRIC_API_KEY"
```

The container runs as a non-root user; mount the config directory for persistent keys/patterns. See `scripts/docker/` for building custom images.

### 6.3 Building from source

```bash
# CLI + server
go install github.com/danielmiessler/fabric/cmd/fabric@latest
# Helpers
go install github.com/danielmiessler/fabric/cmd/to_pdf@latest
go install github.com/danielmiessler/fabric/cmd/code2context@latest
go install github.com/danielmiessler/fabric/cmd/generate_changelog@latest
```

Build the web app (from `web/`):

```bash
cd web
pnpm install
pnpm build        # vite build; outputs the SPA
pnpm preview      # serve the built app
```

Regenerate Swagger docs after changing server annotations:

```bash
swag init -g internal/server/serve.go -o docs
```

### 6.4 Nix

The repo ships a `flake.nix` / `flake.lock` and `nix/pkgs/fabric` derivation for reproducible builds (`nix build`).

### 6.5 Day-2 operations

| Task | Command |
|---|---|
| Update built-in patterns | `fabric --updatepatterns` |
| Change default model | `fabric --changeDefaultModel` |
| Re-run setup / add providers | `fabric --setup` |
| Inspect a session | `fabric --printsession <name>` |
| Clean a session/context | `fabric --wipesession <name>` / `--wipecontext <name>` |
| Check version | `fabric --version` |
| Verbose diagnostics | `fabric --debug 4 ...` (wire-level) |

### 6.6 Observability

Logging is leveled via `--debug` (0 off → 4 wire). The server uses Gin's logger and recovery middleware (panics return 500). Token usage is available per request with `--show-metadata` (CLI) and via the `usage` SSE event (API). There is no built-in metrics endpoint; scrape the reverse proxy or wrap the binary for production telemetry.

### 6.7 Backup & restore

Everything stateful is under `~/.config/fabric/`. Back it up as a unit:

```bash
tar czf fabric-config-backup.tgz -C ~/.config fabric
```

Restore by extracting to the same path. Custom patterns in `CUSTOM_PATTERNS_DIRECTORY` must be backed up separately if outside this tree.

---

## 7. REST API Reference

Base URL defaults to `http://<host>:8080`. Authenticated endpoints require `X-API-Key` when the server was started with `--api-key`.

### 7.1 Core endpoints (`--serve`)

| Method | Path | Purpose |
|---|---|---|
| POST | `/chat` | Streaming chat completions (SSE). Body: `{prompts:[{userInput,vendor,model,contextName,patternName,strategyName,sessionName,variables}], language, modelContextLength, ...ChatOptions}` |
| GET | `/config` | Get masked provider config |
| POST | `/config/update` | Update provider keys/URLs |
| GET | `/models/names` | All models + per-vendor grouping |
| GET | `/patterns/names` | List pattern names |
| GET | `/patterns/:name` | Get raw pattern |
| POST | `/patterns/:name` | Save pattern |
| POST | `/patterns/:name/apply` | Apply pattern with `{input, variables}` |
| DELETE | `/patterns/:name` | Delete pattern |
| GET | `/patterns/exists/:name` | Existence check |
| PUT | `/patterns/rename/:oldName/:newName` | Rename pattern |
| GET/POST/DELETE/PUT | `/contexts/...` | Context CRUD (StorageHandler) |
| GET/POST/DELETE/PUT | `/sessions/...` | Session CRUD (StorageHandler) |
| GET | `/strategies` | List strategies (name/description/prompt) |
| POST | `/youtube/transcript` | `{url, language?, timestamps?}` → transcript + metadata |
| GET | `/swagger/*any` | Swagger UI + `swagger.yaml` (no auth) |

### 7.2 Ollama-compatible endpoints (`--serveOllama`)

| Method | Path | Purpose |
|---|---|---|
| GET | `/api/tags` | Patterns presented as Ollama "models" (`<pattern>:latest`) |
| POST | `/api/chat` | Ollama-format chat (maps model→pattern, supports `num_ctx`, `variables`) |
| GET | `/api/version` | Server version |

The Ollama shim translates an Ollama request into a Fabric `/chat` request (the pattern name is the part of the model before `:`), proxies it, and reformats the streamed response into Ollama's NDJSON.

### 7.3 Example: apply a pattern over HTTP

```bash
curl -s http://localhost:8080/patterns/summarize/apply \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $FABRIC_API_KEY" \
  -d '{"input":"long text...","variables":{"role":"editor"}}'
```

```json
{ "Name": "summarize", "Pattern": "...resolved system prompt..." }
```

---

## 8. Repository Structure

```text
fabric/
├── cmd/
│   ├── fabric/             # main CLI + server entry point
│   ├── to_pdf/             # LaTeX → PDF helper
│   ├── code2context/       # code dir → JSON for create_coding_feature
│   └── generate_changelog/ # git/PR changelog generator (cobra + SQLite)
├── internal/
│   ├── cli/                # flag parsing + command handlers
│   ├── core/               # PluginRegistry + Chatter (composition + orchestration)
│   ├── domain/             # request/options/attachment/stream/thinking types
│   ├── chat/               # chat message primitives
│   ├── plugins/
│   │   ├── plugin.go       # Plugin interface, Settings, SetupQuestions
│   │   ├── ai/             # vendor clients (openai, anthropic, gemini, ollama, ...)
│   │   ├── db/fsdb/        # filesystem-backed patterns/contexts/sessions
│   │   ├── template/       # template engine, plugins, extensions
│   │   └── strategy/       # strategy loader/manager
│   ├── server/             # gin REST API + Ollama shim + Swagger
│   ├── tools/              # youtube, jina, spotify, converter, custom_patterns, ...
│   ├── i18n/               # translations + locale detection
│   └── log/                # leveled debug logging
├── data/
│   ├── patterns/           # built-in patterns (system.md per pattern)
│   └── strategies/         # built-in prompt strategies (JSON)
├── web/                    # SvelteKit web app
├── completions/            # zsh/bash/fish completion scripts + installer
├── scripts/                # docker, installer, pattern_descriptions, readme_updates, python_ui
├── docs/                   # docs + generated swagger
├── nix/                    # Nix packaging
├── go.mod / go.sum         # Go module
└── flake.nix / flake.lock  # Nix flake
```

---

## 9. Module Reference (developer internals)

| Module | Key files | Responsibility |
|---|---|---|
| `internal/cli` | `cli.go`, `flags.go`, `help.go`, `chat.go`, `listing.go`, `management.go`, `setup_server.go`, `tools.go`, `transcribe.go`, `output.go` | Parse flags, route commands, build chat requests, format help (i18n), write output/clipboard/audio |
| `internal/core` | `chatter.go`, `plugin_registry.go` | Orchestrate a chat; compose vendors/tools; resolve model→vendor |
| `internal/domain` | `domain.go`, `attachment.go`, `stream.go`, `think.go`, `thinking.go`, `file_manager.go` | Core types and pure logic (message normalization, think-block stripping, file-change parsing) |
| `internal/plugins` | `plugin.go` | Plugin/Settings/SetupQuestion framework + env-var naming |
| `internal/plugins/ai/*` | per-vendor `*.go` | Vendor clients implementing the `ai.Vendor` interface |
| `internal/plugins/db/fsdb` | patterns/contexts/sessions entities | Filesystem CRUD + variable application |
| `internal/plugins/template` | `template.go`, `text.go`, `datetime.go`, `file.go`, `fetch.go`, `sys.go`, `extension_*.go`, `hash.go` | Template resolution, built-in plugins, extension registry/executor |
| `internal/plugins/strategy` | strategy loader/manager | Load JSON strategies, prepend prompt |
| `internal/server` | `serve.go`, `auth.go`, `chat.go`, `patterns.go`, `contexts.go`, `sessions.go`, `models.go`, `config*.go`, `strategies.go`, `youtube.go`, `ollama.go`, `storage.go` | Gin handlers, auth, SSE, Ollama shim, generic storage CRUD |
| `internal/tools` | `youtube`, `jina`, `spotify`, `converter`, `custom_patterns`, `githelper`, `lang`, `notifications` | External integrations and helpers |

### 9.1 The `ai.Vendor` interface

Vendor clients implement (see `internal/core/chatter_test.go` mock): `GetName`, `GetSetupDescription`, `IsConfigured`, `Configure`, `Setup`, `SetupFillEnvFileContent`, `ListModels`, `Send`, `SendStream`, `NeedsRawMode`. Adding a provider means implementing this interface (usually atop `plugins.NewVendorPluginBase`) and registering it in `NewPluginRegistry`. OpenAI-compatible providers are added en masse from `openai_compatible.ProviderMap`.

### 9.2 Helper binaries

- **`to_pdf`** (`cmd/to_pdf`): reads stdin or a `.tex` file, shells out to `pdflatex -interaction=nonstopmode` in a temp dir, and copies the resulting PDF to the destination. Validates `pdflatex` is on `PATH`.
- **`code2context`** (`cmd/code2context`): scans a directory (depth/ignore options) or a stdin file list and emits JSON. `-depth` default 3, `-ignore` default `.git,node_modules,vendor`.
- **`generate_changelog`** (`cmd/generate_changelog`): Cobra-based; walks git history and GitHub PRs (GraphQL), caches in SQLite (`changelog.db`), optional AI summaries via Fabric. Flags include `--repo`, `--output`, `--limit`, `--version`, `--ai-summarize`, `--sync-db`, `--release`, `--incoming-pr`, `--process-prs`.

---

## 10. Code Quality & Conventions

- **Error handling:** Go idiomatic `(value, error)` returns; named returns used heavily in CLI handlers; the handler chain uses a `(handled bool, err error)` pattern for clean short-circuiting.
- **Internationalization:** all user-facing strings go through `i18n.T()`; never hardcode English in new code.
- **Testing:** extensive table-driven unit tests colocated as `*_test.go` (flags, template engine, plugins, server URL building, Ollama `num_ctx` parsing, file-change parsing, notification escaping). Vendor calls are mocked via the `ai.Vendor` interface.
- **Security-by-construction:** path-traversal guards, size caps, content-type checks, hash-verified extensions, positional-argument shell invocation for notifications.
- **Separation of concerns:** `domain` holds pure types/logic; `core` orchestrates; `plugins` integrate; `server`/`cli` are thin front ends over the same core.
- **Defaults discipline:** generation defaults are constants in `domain` that must mirror the CLI struct-tag defaults.

### 10.1 Testing strategy

Run the Go test suite:

```bash
go test ./...
```

Notable suites: `internal/cli` (flag parsing, image validation, chat options), `internal/plugins/template` (template/plugin/extension behavior, sentinel handling), `internal/server` (`buildFabricChatURL`, `parseOllamaNumCtx`), `internal/domain` (`ParseFileChanges`, `StripThinkBlocks`, `NormalizeMessages`). Web app tests run under Vitest (`pnpm test`).

---

## 11. Potential Improvements & Tech Debt

Observations from the source, useful for maintainers and contributors:

- **Fetch allowlist:** the `fetch` template plugin documents (in code comments) that production deployments should consider URL allowlists; none is built in. A configurable allowlist would harden patterns that fetch URLs.
- **CORS is dev-pinned:** `/chat` hardcodes `Access-Control-Allow-Origin: http://localhost:5173`. A configurable allowed-origins list would ease non-default web deployments.
- **Two CLI flag systems:** the main binary uses `go-flags` (struct tags) while `generate_changelog` uses Cobra. Intentional (separate binaries) but worth noting for contributors.
- **No server-side rate limiting / metrics:** the API has no built-in throttling or metrics endpoint; rely on a reverse proxy.
- **Notification truncation:** desktop notification truncation counts Unicode code points, not grapheme clusters (documented limitation — complex emoji may truncate oddly).
- **Unauthenticated default:** the server runs without auth if `--api-key` is omitted (logged as a warning). Consider requiring an explicit `--insecure` flag to make the unauthenticated mode opt-in.

---

## 12. Troubleshooting (operational)

### Error: `Starting REST API server without API key authentication`

- **Cause:** `--serve` without `--api-key`.
- **Fix:** Restart with `--api-key`. Until then the API is open.

### Error: `401 Missing API Key` / `401 Wrong API Key`

- **Cause:** Missing or mismatched `X-API-Key` header.
- **Fix:** Send the correct header; if proxying, ensure the proxy forwards `X-API-Key` (see §6.1).

### Issue: streamed responses arrive only at the end / buffered

- **Cause:** Proxy buffering of SSE.
- **Fix:** `proxy_buffering off` (nginx); Fabric already sets `X-Accel-Buffering: no`.

### Error: `could not find vendor` (server or CLI)

- **Cause:** Requested model not mapped to a configured vendor, or no default set.
- **Fix:** Configure the provider (`/config/update` or `fabric --setup`); confirm with `GET /models/names` or `fabric --listmodels`; pass explicit vendor.

### Error: `swagger.yaml not found - generate it with: swag init -g internal/server/serve.go -o docs`

- **Cause:** Swagger spec not generated/shipped next to the binary.
- **Fix:** Run `swag init -g internal/server/serve.go -o docs`, or place `docs/swagger.yaml` beside the executable.

### Error: Ollama shim `num_ctx must be ...`

- **Cause:** Invalid `num_ctx` in an Ollama `/api/chat` request (non-integer, negative, > 1,000,000, NaN/Inf, wrong type).
- **Fix:** Send a positive integer ≤ 1,000,000, or omit it.

### Error: extension `config hash mismatch` / `executable hash mismatch`

- **Cause:** The extension's config or binary changed after registration (tamper detection).
- **Fix:** Re-register with `--addextension` after vetting the change.

### Issue: local OpenAI-compatible server returns empty/garbled responses

- **Cause:** The server doesn't support OpenAI's Responses API.
- **Fix:** `--disable-responses-api` (or `disableResponsesAPI: true` in `config.yaml`).

### Diagnostics

```bash
fabric --debug 4 -p summarize "test"   # wire-level: logs exact messages to/from the model
```

---

## 13. Contributing

1. Fork and clone; ensure a recent Go toolchain.
2. Build: `go build ./...`; test: `go test ./...`.
3. Follow conventions: route user-facing strings through `i18n.T()`; add table-driven tests beside new code; keep `domain` pure and orchestration in `core`.
4. New AI provider → implement the `ai.Vendor` interface and register it in `NewPluginRegistry` (or add to `openai_compatible.ProviderMap` if OpenAI-compatible).
5. New built-in template plugin → add a plugin type and wire it into the `plugin:` switch in `template.go`.
6. Server changes → update Swagger annotations and regenerate with `swag init`.
7. Web changes → `cd web && pnpm lint && pnpm check && pnpm test`.
8. Patterns → add a `system.md` under `data/patterns/<name>/`.

The project is MIT-licensed; the canonical repository is <https://github.com/danielmiessler/fabric>.

---

## 14. Quick Reference

### Key paths

| Path | Contents |
|---|---|
| `~/.config/fabric/.env` | API keys + defaults |
| `~/.config/fabric/config.yaml` | YAML defaults |
| `~/.config/fabric/patterns/` | Built-in patterns |
| `~/.config/fabric/strategies/` | Strategy JSON |
| `~/.config/fabric/contexts/` | Contexts |
| `~/.config/fabric/sessions/` | Sessions |
| `~/.config/fabric/extensions/extensions.yaml` | Extension registry |
| `$CUSTOM_PATTERNS_DIRECTORY` | Private custom patterns |

### Server commands

| Command | Effect |
|---|---|
| `fabric --serve --api-key K` | REST API on `:8080` |
| `fabric --serve --serveOllama --api-key K` | + Ollama endpoints |
| `fabric --serve --address :9000` | Custom bind address |

### Critical env vars

| Var | Purpose |
|---|---|
| `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, … | Provider auth |
| `OLLAMA_URL`, `LM_STUDIO_API_BASE_URL` | Local provider URLs |
| `CUSTOM_PATTERNS_DIRECTORY` | Private patterns |
| `DEFAULT_VENDOR` / `DEFAULT_MODEL` | Defaults |
| `FABRIC_MODEL_<PATTERN>` | Per-pattern model (`vendor|model`) |
| `GITHUB_TOKEN` | `generate_changelog` |

### REST endpoints at a glance

`POST /chat` · `GET/POST /config[/update]` · `GET /models/names` · `*/patterns/*` · `*/contexts/*` · `*/sessions/*` · `GET /strategies` · `POST /youtube/transcript` · `GET /swagger/*` · (Ollama) `GET /api/tags` · `POST /api/chat` · `GET /api/version`

---

_Generated June 2026 from danielmiessler/Fabric at 29b32f9. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
