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.
┌──────────── 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:
Init()parses flags (internal/cli/flags.go), then merges YAML config for any flag with ayaml:tag (CLI wins), and reads stdin if piped.- i18n init with the requested/detected language.
initializeFabric()opens the filesystem DB at~/.config/fabricand builds thePluginRegistry.- A chain of handlers runs, each returning
(handled, error)and short-circuiting if it handled the request:handleSetupAndServerCommands—--setup,--serve,--serveOllamahandleConfigurationCommands—--updatepatterns,--changeDefaultModelhandleListingCommands—--listpatterns/-models/-contexts/-sessions/-strategies/-vendors, voices, transcription modelshandleManagementCommands—--wipecontext/-session,--printcontext/-sessionhandleExtensionCommands—--listextensions/--addextension/--rmextension- tool processing (
--youtube,--scrape_url/-question,--spotify), transcription, HTML readability
- If none short-circuit,
handleChatProcessingbuilds aChatRequest/ChatOptions, obtains aChatter, 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.
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
}
VendorsAllholds all registered vendors (sorted case-insensitively by name).VendorManagerholds only those that successfullyConfigure()(i.e., have valid env settings).ConfigureVendors()rebuildsVendorManagerfromVendorsAll.GetChatter(model, ctxLen, vendor, stream, dryRun)resolves which vendor serves a model: case-insensitive model normalization,vendor/modelprefix parsing (e.g.ollama/llama3), ambiguity warnings when multiple vendors offer a model, and a Codex pass-through for subscription-hidden models. IfdryRun, it swaps in thedryrunclient.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.
BuildSession()assembles the conversation. The system prompt is built byjoinPromptSections(strategy, context, pattern)— strategy first, then context, then pattern — and the user input is appended (or merged, in--rawmode).- Pattern variables are resolved via
template.ApplyTemplatewhenInputHasVarsis set; the pattern itself is loaded with variables applied unlessNoVariableReplacement. - Language enforcement: if a non-English language is requested, an instruction to translate the response is wrapped around the system prompt.
- The vendor is called via
Send()(blocking) orSendStream()(streaming through a channel). Streaming aggregates content, surfacesusagemetadata when--show-metadatais set, and records the first stream error. --suppress-thinkstrips<think>…</think>blocks (configurable tags) after generation.- 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). - If a session name was given, the turn is persisted.
Note
Chatternormalizes the model name to the vendor's canonical casing before sending, and forcesRaw = truewhen the vendor reportsNeedsRawMode(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:
type Plugin interface {
GetName() string
GetSetupDescription() string
IsConfigured() bool
Configure() error
Setup() error
SetupFillEnvFileContent(*bytes.Buffer)
}
PluginBase provides the machinery: Settings 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_templates 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 thegenerate_changeloghelper, 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 |
FABRIC_ALIAS_PREFIX |
Prefix for generated pattern aliases (shell) |
GITHUB_TOKEN |
Used by generate_changelog |
Important
The
.envfile contains secrets. Restrict its permissions (the parent dir is created0755, the file0644by default — tighten to0600on 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):
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.
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-keyis empty, the server logsStarting REST API server without API key authenticationand 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)
fileplugin: rejects paths containing.., expands~/safely, caps reads at 1 MB.fetchplugin: 1 MB cap, UTF-8 + null-byte validation, only text-like content types; documented as making outbound requests — consider an allowlist in hardened deployments.sysplugin: only whitelisted operations;envreads 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
- [ ]
.envpermissions 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_featureused only in disposable/reviewed working trees - [ ] No secrets embedded in patterns (the
sys:envplugin can read them)
6. Deployment & Operations
6.1 Running the REST API
# 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:
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
/chatendpoint streams Server-Sent Events. Disable proxy buffering (proxy_buffering off, and Fabric already setsX-Accel-Buffering: no) or clients will see responses only after completion.
6.2 Containerized deployment
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
# 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/):
cd web
pnpm install
pnpm build # vite build; outputs the SPA
pnpm preview # serve the built app
Regenerate Swagger docs after changing server annotations:
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:
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
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"}}'
{ "Name": "summarize", "Pattern": "...resolved system prompt..." }
8. Repository Structure
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.texfile, shells out topdflatex -interaction=nonstopmodein a temp dir, and copies the resulting PDF to the destination. Validatespdflatexis onPATH.code2context(cmd/code2context): scans a directory (depth/ignore options) or a stdin file list and emits JSON.-depthdefault 3,-ignoredefault.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, Ollamanum_ctxparsing, file-change parsing, notification escaping). Vendor calls are mocked via theai.Vendorinterface. - Security-by-construction: path-traversal guards, size caps, content-type checks, hash-verified extensions, positional-argument shell invocation for notifications.
- Separation of concerns:
domainholds pure types/logic;coreorchestrates;pluginsintegrate;server/cliare thin front ends over the same core. - Defaults discipline: generation defaults are constants in
domainthat must mirror the CLI struct-tag defaults.
10.1 Testing strategy
Run the Go test suite:
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
fetchtemplate 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:
/chathardcodesAccess-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) whilegenerate_changeloguses 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-keyis omitted (logged as a warning). Consider requiring an explicit--insecureflag to make the unauthenticated mode opt-in.
12. Troubleshooting (operational)
Error: Starting REST API server without API key authentication
- Cause:
--servewithout--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-Keyheader. - 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 setsX-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/updateorfabric --setup); confirm withGET /models/namesorfabric --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 placedocs/swagger.yamlbeside the executable.
Error: Ollama shim num_ctx must be ...
- Cause: Invalid
num_ctxin an Ollama/api/chatrequest (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
--addextensionafter 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(ordisableResponsesAPI: trueinconfig.yaml).
Diagnostics
fabric --debug 4 -p summarize "test" # wire-level: logs exact messages to/from the model
13. Contributing
- Fork and clone; ensure a recent Go toolchain.
- Build:
go build ./...; test:go test ./.... - Follow conventions: route user-facing strings through
i18n.T(); add table-driven tests beside new code; keepdomainpure and orchestration incore. - New AI provider → implement the
ai.Vendorinterface and register it inNewPluginRegistry(or add toopenai_compatible.ProviderMapif OpenAI-compatible). - New built-in template plugin → add a plugin type and wire it into the
plugin:switch intemplate.go. - Server changes → update Swagger annotations and regenerate with
swag init. - Web changes →
cd web && pnpm lint && pnpm check && pnpm test. - Patterns → add a
system.mdunderdata/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 |
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