diff --git a/apps/hermes-obsidian-bridge/config.json b/apps/hermes-obsidian-bridge/config.json new file mode 100644 index 0000000..b060217 --- /dev/null +++ b/apps/hermes-obsidian-bridge/config.json @@ -0,0 +1,92 @@ +{ + "$schema": "../app-info-schema.json", + "name": "Hermes Obsidian Bridge", + "id": "hermes-obsidian-bridge", + "available": true, + "short_desc": "OpenAI-compatible proxy in front of Hermes api_server with session continuity and vault RAG", + "author": "cori", + "port": 9120, + "categories": [ + "ai", + "utilities" + ], + "description": "Thin OpenAI-compatible proxy in front of the Hermes api_server. Adds session continuity (X-Session-Id), vault RAG via the Obsidian Local REST API (X-Obsidian-Note-Path), and automatic cost routing between a cheap and strong model based on prompt length. Wire it into any OpenAI-compatible Obsidian plugin (Copilot for Obsidian, Text Generator, Local GPT) for in-note chat with the full Hermes agent. Image: forge.407.lol/cori/hermes-obsidian-bridge:sha-7139662d. Source: https://github.com/cori/hermes-obsidian-bridge.", + "tipi_version": 1, + "version": "sha-7139662d", + "source": "https://github.com/cori/hermes-obsidian-bridge", + "website": "https://github.com/cori/hermes-obsidian-bridge", + "exposable": true, + "supported_architectures": [ + "arm64", + "amd64" + ], + "created_at": 1757610000000, + "updated_at": 1757610000000, + "dynamic_config": true, + "form_fields": [ + { + "type": "password", + "label": "Hermes API Server Key", + "hint": "Bearer token for the Hermes api_server. Must match the API_SERVER_KEY on the gateway host. Required.", + "required": true, + "env_variable": "API_SERVER_KEY", + "default": "" + }, + { + "type": "text", + "label": "Hermes api_server URL", + "hint": "Base URL of the Hermes api_server (e.g. http://192.168.1.10:8642). Used by the bridge for chat and model listing. host.docker.internal works for Docker Desktop; on Linux Runtipi, use the host IP.", + "required": false, + "env_variable": "HERMES_BASE_URL", + "default": "http://host.docker.internal:8642/v1" + }, + { + "type": "text", + "label": "Obsidian Local REST API URL", + "hint": "Base URL of the Obsidian Local REST API plugin. Default 27123. On Tailscale setups, use the Mac's Tailscale IP.", + "required": false, + "env_variable": "OBSIDIAN_REST_URL", + "default": "http://host.docker.internal:27123" + }, + { + "type": "password", + "label": "Obsidian Local REST API Key", + "hint": "API key from the Obsidian Local REST API plugin (Settings → Local REST API → API Key). Leave empty if the plugin is configured for no auth on a trusted network.", + "required": false, + "env_variable": "OBSIDIAN_REST_KEY", + "default": "" + }, + { + "type": "text", + "label": "Cheap Model", + "hint": "Model id to route short prompts to. Default uses umans-flash to burn expiring credits.", + "required": false, + "env_variable": "CHEAP_MODEL", + "default": "umans-flash" + }, + { + "type": "text", + "label": "Strong Model", + "hint": "Model id to route longer prompts to.", + "required": false, + "env_variable": "STRONG_MODEL", + "default": "minimax-m3" + }, + { + "type": "number", + "label": "Cheap Model Char Threshold", + "hint": "Prompts shorter than this (in characters) go to CHEAP_MODEL. Set to a large number to disable cheap-routing entirely.", + "required": false, + "env_variable": "CHEAP_MODEL_CHAR_THRESHOLD", + "default": 500 + }, + { + "type": "text", + "label": "Enable Vault RAG", + "hint": "Set to 'true' to pre-fetch the current note + today's daily + this week's review from Obsidian and prepend as context. Set to 'false' to disable.", + "required": false, + "env_variable": "RAG_ENABLED", + "default": "true" + } + ] +} diff --git a/apps/hermes-obsidian-bridge/docker-compose.json b/apps/hermes-obsidian-bridge/docker-compose.json new file mode 100644 index 0000000..ef287a8 --- /dev/null +++ b/apps/hermes-obsidian-bridge/docker-compose.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 2, + "services": [ + { + "name": "hermes-obsidian-bridge", + "image": "forge.407.lol/cori/hermes-obsidian-bridge:sha-7139662d", + "isMain": true, + "internalPort": 9120, + "command": "python -m bridge", + "environment": [ + { "key": "PORT", "value": "9120" }, + { "key": "API_SERVER_KEY", "value": "${API_SERVER_KEY}" }, + { "key": "HERMES_BASE_URL", "value": "${HERMES_BASE_URL}" }, + { "key": "OBSIDIAN_REST_URL", "value": "${OBSIDIAN_REST_URL}" }, + { "key": "OBSIDIAN_REST_KEY", "value": "${OBSIDIAN_REST_KEY}" }, + { "key": "CHEAP_MODEL", "value": "${CHEAP_MODEL}" }, + { "key": "STRONG_MODEL", "value": "${STRONG_MODEL}" }, + { "key": "CHEAP_MODEL_CHAR_THRESHOLD", "value": "${CHEAP_MODEL_CHAR_THRESHOLD}" }, + { "key": "RAG_ENABLED", "value": "${RAG_ENABLED}" }, + { "key": "LOG_LEVEL", "value": "info" } + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/data", "containerPath": "/data" } + ], + "healthCheck": { + "test": "python -c \"import urllib.request,sys; r=urllib.request.urlopen('http://127.0.0.1:9120/health', timeout=3); sys.exit(0 if r.status==200 else 1)\"", + "interval": "30s", + "timeout": "5s", + "retries": 3 + } + } + ] +} diff --git a/apps/hermes-obsidian-bridge/metadata/description.md b/apps/hermes-obsidian-bridge/metadata/description.md new file mode 100644 index 0000000..7bdd1d0 --- /dev/null +++ b/apps/hermes-obsidian-bridge/metadata/description.md @@ -0,0 +1,29 @@ +# Hermes Obsidian Bridge + +A thin OpenAI-compatible proxy in front of the Hermes `api_server`. Designed to be pointed +at from any client that speaks OpenAI chat-completions (Copilot for Obsidian, Text Generator, +Local GPT, Open WebUI, curl scripts). + +## What it adds + +- **Session continuity.** Pass `X-Session-Id` and the bridge forwards it; conversations resume + across separate HTTP calls instead of starting a fresh session every time. +- **Vault RAG.** Send `X-Obsidian-Note-Path` and the bridge reads the current note + today's + daily + this week's review from the Obsidian Local REST API and prepends them as context. +- **Cost routing.** Prompts shorter than `CHEAP_MODEL_CHAR_THRESHOLD` go to `CHEAP_MODEL`; + longer ones go to `STRONG_MODEL`. Explicit `model` in the request body is always respected. + +## Install + +Install via the Runtipi dashboard. Required field: `API_SERVER_KEY` (the bearer token for +the Hermes `api_server` — must match the value on the gateway host's `~/.hermes/.env`). + +## Wire to Obsidian + +Any OpenAI-compatible community plugin works. Recommended: **Copilot for Obsidian**, with +base URL `http://hermes-obsidian-bridge.407.lol/v1` and any string as the API key. + +## Image source + +Image is built and hosted at `forge.407.lol/cori/hermes-obsidian-bridge` (local Forgejo). +Source repo: https://github.com/cori/hermes-obsidian-bridge. diff --git a/apps/hermes-obsidian-bridge/metadata/logo.jpg b/apps/hermes-obsidian-bridge/metadata/logo.jpg new file mode 100644 index 0000000..911455a Binary files /dev/null and b/apps/hermes-obsidian-bridge/metadata/logo.jpg differ diff --git a/apps/inventree/config.json b/apps/inventree/config.json new file mode 100644 index 0000000..1f60b09 --- /dev/null +++ b/apps/inventree/config.json @@ -0,0 +1,69 @@ +{ + "$schema": "../app-info-schema.json", + "name": "InvenTree", + "id": "inventree", + "available": true, + "short_desc": "Open-source inventory management for parts, components, BOMs, and stock control", + "author": "InvenTree", + "port": 8000, + "categories": ["data", "utilities"], + "description": "InvenTree is an open-source inventory management system providing intuitive parts management, stock control, BOM management, and supplier tracking. Designed for makers, hobbyists, and small businesses. Features hierarchical categories, custom fields, build orders, REST API, mobile app with barcode scanning, and a plugin ecosystem.", + "tipi_version": 1, + "version": "1.5.5", + "source": "https://github.com/inventree/InvenTree", + "website": "https://inventree.org", + "exposable": true, + "supported_architectures": ["arm64", "amd64"], + "created_at": 1726700000000, + "updated_at": 1726700000000, + "dynamic_config": true, + "form_fields": [ + { + "type": "random", + "label": "Database Password", + "hint": "Password for the PostgreSQL database (auto-generated)", + "required": true, + "env_variable": "INVENTREE_DB_PASSWORD", + "encoding": "hex" + }, + { + "type": "random", + "label": "Secret Key", + "hint": "Django secret key for cryptographic signing (auto-generated)", + "required": true, + "env_variable": "INVENTREE_SECRET_KEY", + "encoding": "hex" + }, + { + "type": "text", + "label": "Admin User", + "hint": "Username for the InvenTree superuser account", + "required": true, + "env_variable": "INVENTREE_ADMIN_USER", + "default": "admin" + }, + { + "type": "password", + "label": "Admin Password", + "hint": "Password for the InvenTree superuser account", + "required": true, + "env_variable": "INVENTREE_ADMIN_PASSWORD" + }, + { + "type": "text", + "label": "Admin Email", + "hint": "Email for the InvenTree superuser account", + "required": false, + "env_variable": "INVENTREE_ADMIN_EMAIL", + "default": "admin@localhost" + }, + { + "type": "text", + "label": "Site URL", + "hint": "The external URL for this InvenTree instance (used for links, CSRF, etc.)", + "required": true, + "env_variable": "INVENTREE_SITE_URL", + "default": "https://inventree.407.lol" + } + ] +} \ No newline at end of file diff --git a/apps/inventree/docker-compose.json b/apps/inventree/docker-compose.json new file mode 100644 index 0000000..074c239 --- /dev/null +++ b/apps/inventree/docker-compose.json @@ -0,0 +1,130 @@ +{ + "$schema": "../dynamic-compose-schema.json", + "schemaVersion": 2, + "services": [ + { + "name": "inventree-db", + "image": "postgres:17", + "environment": [ + { "key": "PGDATA", "value": "/var/lib/postgresql/data/pgdb" }, + { "key": "POSTGRES_USER", "value": "inventree" }, + { "key": "POSTGRES_PASSWORD", "value": "${INVENTREE_DB_PASSWORD}" }, + { "key": "POSTGRES_DB", "value": "inventree" } + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/pgdb", "containerPath": "/var/lib/postgresql/data" } + ], + "healthCheck": { + "test": "pg_isready -U inventree -d inventree", + "interval": "15s", + "timeout": "5s", + "retries": 10 + } + }, + { + "name": "inventree-cache", + "image": "redis:7-alpine", + "command": [ + "redis-server", + "--save", + "", + "--appendonly", + "no" + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/redis", "containerPath": "/data" } + ], + "healthCheck": { + "test": "redis-cli ping", + "interval": "15s", + "timeout": "5s", + "retries": 10 + } + }, + { + "name": "inventree-server", + "image": "inventree/inventree:1.5.5", + "isMain": true, + "internalPort": "8000", + "dependsOn": [ + "inventree-db", + "inventree-cache" + ], + "environment": [ + { "key": "INVENTREE_DB_ENGINE", "value": "postgresql" }, + { "key": "INVENTREE_DB_NAME", "value": "inventree" }, + { "key": "INVENTREE_DB_HOST", "value": "inventree-db" }, + { "key": "INVENTREE_DB_PORT", "value": "5432" }, + { "key": "INVENTREE_DB_USER", "value": "inventree" }, + { "key": "INVENTREE_DB_PASSWORD", "value": "${INVENTREE_DB_PASSWORD}" }, + { "key": "INVENTREE_CACHE_ENABLED", "value": "True" }, + { "key": "INVENTREE_CACHE_HOST", "value": "inventree-cache" }, + { "key": "INVENTREE_CACHE_PORT", "value": "6379" }, + { "key": "INVENTREE_SECRET_KEY", "value": "${INVENTREE_SECRET_KEY}" }, + { "key": "INVENTREE_SITE_URL", "value": "${INVENTREE_SITE_URL}" }, + { "key": "INVENTREE_WEB_PORT", "value": "8000" }, + { "key": "INVENTREE_LOG_LEVEL", "value": "WARNING" }, + { "key": "INVENTREE_PLUGINS_ENABLED", "value": "True" }, + { "key": "INVENTREE_AUTO_UPDATE", "value": "True" }, + { "key": "INVENTREE_ADMIN_USER", "value": "${INVENTREE_ADMIN_USER}" }, + { "key": "INVENTREE_ADMIN_PASSWORD", "value": "${INVENTREE_ADMIN_PASSWORD}" }, + { "key": "INVENTREE_ADMIN_EMAIL", "value": "${INVENTREE_ADMIN_EMAIL}" }, + { "key": "INVENTREE_USE_X_FORWARDED_HOST", "value": "True" }, + { "key": "INVENTREE_USE_X_FORWARDED_PORT", "value": "True" }, + { "key": "INVENTREE_USE_X_FORWARDED_PROTO", "value": "True" }, + { "key": "INVENTREE_GUNICORN_TIMEOUT", "value": "90" }, + { "key": "TZ", "value": "${TZ}" } + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/data", "containerPath": "/home/inventree/data" } + ], + "healthCheck": { + "test": "invoke server-health --address http://localhost:8000", + "interval": "20s", + "timeout": "5s", + "retries": 10 + } + }, + { + "name": "inventree-worker", + "image": "inventree/inventree:1.5.5", + "command": [ + "invoke", + "worker" + ], + "dependsOn": [ + "inventree-server" + ], + "environment": [ + { "key": "INVENTREE_DB_ENGINE", "value": "postgresql" }, + { "key": "INVENTREE_DB_NAME", "value": "inventree" }, + { "key": "INVENTREE_DB_HOST", "value": "inventree-db" }, + { "key": "INVENTREE_DB_PORT", "value": "5432" }, + { "key": "INVENTREE_DB_USER", "value": "inventree" }, + { "key": "INVENTREE_DB_PASSWORD", "value": "${INVENTREE_DB_PASSWORD}" }, + { "key": "INVENTREE_CACHE_ENABLED", "value": "True" }, + { "key": "INVENTREE_CACHE_HOST", "value": "inventree-cache" }, + { "key": "INVENTREE_CACHE_PORT", "value": "6379" }, + { "key": "INVENTREE_SECRET_KEY", "value": "${INVENTREE_SECRET_KEY}" }, + { "key": "INVENTREE_SITE_URL", "value": "${INVENTREE_SITE_URL}" }, + { "key": "INVENTREE_WEB_PORT", "value": "8000" }, + { "key": "INVENTREE_LOG_LEVEL", "value": "WARNING" }, + { "key": "INVENTREE_PLUGINS_ENABLED", "value": "True" }, + { "key": "INVENTREE_AUTO_UPDATE", "value": "True" }, + { "key": "INVENTREE_USE_X_FORWARDED_HOST", "value": "True" }, + { "key": "INVENTREE_USE_X_FORWARDED_PORT", "value": "True" }, + { "key": "INVENTREE_USE_X_FORWARDED_PROTO", "value": "True" }, + { "key": "TZ", "value": "${TZ}" } + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/data", "containerPath": "/home/inventree/data" } + ], + "healthCheck": { + "test": "invoke worker-health", + "interval": "60s", + "timeout": "10s", + "retries": 3 + } + } + ] +} \ No newline at end of file diff --git a/apps/inventree/metadata/description.md b/apps/inventree/metadata/description.md new file mode 100644 index 0000000..3d6acc0 --- /dev/null +++ b/apps/inventree/metadata/description.md @@ -0,0 +1,22 @@ +# InvenTree + +InvenTree is an open-source inventory management system for parts, components, and stock control. It provides hierarchical categories, BOM management, supplier tracking, build orders, custom fields, a REST API, and a mobile app with barcode scanning. + +## Features + +- **Parts Management**: Hierarchical categories, custom fields, part images, datasheet attachments +- **Stock Control**: Multi-location stock tracking, serialized items, stock history +- **BOM Management**: Multi-level BOMs with substitutions, BOM import/export +- **Build Orders**: Consume stock to build assemblies, track build progress +- **Supplier Management**: Multi-vendor pricing, supplier part URLs, purchase orders +- **REST API**: Full CRUD API for integration with external tools +- **Mobile App**: iOS/Android with barcode scanning +- **Plugin System**: 50+ community plugins for integrations and custom workflows + +## Setup + +On first launch, InvenTree runs database migrations and creates the admin account from the form fields. This can take 1-2 minutes. The server is ready when the health check passes. + +## Usage + +Ideal for tracking electronic components, CNC tooling, consumables, PCB materials, and any physical inventory that needs structured organization with supplier links and BOM support. \ No newline at end of file diff --git a/apps/inventree/metadata/logo.jpg b/apps/inventree/metadata/logo.jpg new file mode 100644 index 0000000..e07a790 Binary files /dev/null and b/apps/inventree/metadata/logo.jpg differ diff --git a/apps/karakeep/config.json b/apps/karakeep/config.json new file mode 100644 index 0000000..8a288a1 --- /dev/null +++ b/apps/karakeep/config.json @@ -0,0 +1,62 @@ +{ + "$schema": "../app-info-schema.json", + "name": "Karakeep", + "id": "karakeep", + "available": true, + "short_desc": "Self-hostable bookmark/notes/images app with AI tagging and iOS + Android apps", + "author": "karakeep-app", + "port": 8643, + "categories": [ + "utilities", + "ai" + ], + "description": "Karakeep (formerly Hoarder) is a self-hostable bookmark-everything app: save links, notes, images, and PDFs, with optional AI auto-tagging. Includes native iOS and Android apps that connect to your server, plus Chrome/Firefox/Safari extensions. Capture from the share sheet on iOS, tag and search later, keep historical notes on products you liked, restaurants worth returning to, etc.", + "tipi_version": 1, + "min_tipi_version": "4.5.0", + "version": "0.33.2", + "source": "https://github.com/karakeep-app/karakeep", + "website": "https://karakeep.app", + "exposable": true, + "dynamic_config": true, + "supported_architectures": [ + "arm64", + "amd64" + ], + "created_at": 1726064280917, + "updated_at": 1726064280917, + "form_fields": [ + { + "type": "random", + "label": "NextAuth Secret", + "min": 32, + "env_variable": "NEXTAUTH_SECRET" + }, + { + "type": "random", + "label": "Meilisearch Master Key", + "min": 32, + "env_variable": "MEILI_MASTER_KEY" + }, + { + "type": "text", + "label": "OpenAI API Key", + "required": false, + "env_variable": "OPENAI_API_KEY" + }, + { + "type": "text", + "label": "Ollama Base URL", + "hint": "Optional: point at your local Ollama (e.g. http://100.126.40.64:11434) for local AI tagging. Leave blank to disable AI tagging or use OpenAI only.", + "required": false, + "env_variable": "OLLAMA_BASE_URL" + }, + { + "type": "text", + "label": "Disable Signups", + "hint": "Set to 'true' after creating your account to block new registrations.", + "required": false, + "env_variable": "DISABLE_SIGNUPS", + "default": "false" + } + ] +} diff --git a/apps/karakeep/docker-compose.json b/apps/karakeep/docker-compose.json new file mode 100644 index 0000000..0204712 --- /dev/null +++ b/apps/karakeep/docker-compose.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://schemas.runtipi.io/v2/dynamic-compose.json", + "schemaVersion": 2, + "services": [ + { + "name": "karakeep", + "image": "ghcr.io/karakeep-app/karakeep:v0.33.2", + "isMain": true, + "internalPort": 3000, + "environment": [ + { "key": "MEILI_ADDR", "value": "http://karakeep-meilisearch:7700" }, + { "key": "BROWSER_WEB_URL", "value": "http://karakeep-chrome:9222" }, + { "key": "DATA_DIR", "value": "/data" }, + { "key": "NEXTAUTH_SECRET", "value": "${NEXTAUTH_SECRET}" }, + { "key": "MEILI_MASTER_KEY", "value": "${MEILI_MASTER_KEY}" }, + { "key": "NEXTAUTH_URL", "value": "${APP_PROTOCOL:-http}://${APP_DOMAIN}" }, + { "key": "OPENAI_API_KEY", "value": "${OPENAI_API_KEY}" }, + { "key": "OLLAMA_BASE_URL", "value": "${OLLAMA_BASE_URL}" }, + { "key": "DISABLE_SIGNUPS", "value": "${DISABLE_SIGNUPS}" } + ], + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data/app", + "containerPath": "/data" + } + ], + "healthCheck": { + "test": "wget -q --spider http://localhost:3000/api/health || exit 1", + "interval": "30s", + "timeout": "10s", + "retries": 10 + }, + "dependsOn": [ + "karakeep-meilisearch", + "karakeep-chrome" + ] + }, + { + "name": "karakeep-chrome", + "image": "ghcr.io/karakeep-app/karakeep-chrome:v0.33.2", + "command": [ + "--disable-gpu", + "--disable-dev-shm-usage", + "--hide-scrollbars", + "--disable-blink-features=AutomationControlled", + "--window-size=1440,900" + ], + "healthCheck": { + "test": "curl -fsS http://localhost:9222/json/version || exit 1", + "interval": "30s", + "timeout": "10s", + "retries": 5 + } + }, + { + "name": "karakeep-meilisearch", + "image": "getmeili/meilisearch:v1.21.0", + "environment": [ + { "key": "MEILI_NO_ANALYTICS", "value": "true" }, + { "key": "MEILI_MASTER_KEY", "value": "${MEILI_MASTER_KEY}" } + ], + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data/meili_data", + "containerPath": "/meili_data" + } + ], + "healthCheck": { + "test": "curl -fsS http://localhost:7700/health || exit 1", + "interval": "30s", + "timeout": "10s", + "retries": 5 + } + } + ] +} diff --git a/apps/karakeep/metadata/description.md b/apps/karakeep/metadata/description.md new file mode 100644 index 0000000..77590d8 --- /dev/null +++ b/apps/karakeep/metadata/description.md @@ -0,0 +1,60 @@ +# Karakeep + +A self-hostable bookmark-everything app: links, notes, images, PDFs. With optional AI-based automatic tagging (OpenAI or local Ollama) and full-text search via Meilisearch. Native iOS and Android apps talk directly to your server; share-sheet capture on iOS is the killer feature. + +Use cases: keep historical notes on brands you like (e.g. a particular blue cheese), restaurants worth returning to (the killer cubano at X), products, recipes, anything that benefits from a quick tag and search later. Karakeep replaces your brain's "I should remember that" stack. + +## Features + +- Bookmark links, take simple notes, store images and PDFs +- Automatic fetching for link titles, descriptions, images +- Sort bookmarks into lists, full-text search across everything +- Optional AI auto-tagging (OpenAI cloud or local Ollama) +- Chrome, Firefox, and Safari extensions for quick bookmarking +- Native iOS app (https://apps.apple.com/us/app/karakeep-app/id6479258022) and Android app +- Full page archival (via monolith) to protect against link rot +- Bulk actions, dark mode, multi-user +- Self-hosting first + +## Components + +- `karakeep` (main web app, Next.js) — port 3000 internally +- `karakeep-chrome` (headless Chrome for page archival) — separate container +- `karakeep-meilisearch` (search index) — separate container + +## Setup + +After install: + +1. Open the app at the assigned domain +2. Sign up for the first account (signups are enabled by default) +3. Flip the `DISABLE_SIGNUPS` form field to `true` so no one else can register +4. Optional: configure AI tagging by setting `OPENAI_API_KEY` (cloud) or `OLLAMA_BASE_URL` (local) +5. Install the iOS app, point it at your server URL +6. Install the browser extension if you want quick captures from your desktop browser + +## AI Tagging + +Two options, both optional: + +- **OpenAI**: paste an API key into the `OpenAI API Key` form field +- **Local Ollama**: point `Ollama Base URL` at any reachable Ollama instance (e.g. `http://100.126.40.64:11434`). No data leaves your network. + +Either way, Karakeep auto-tags new bookmarks so you don't have to maintain a taxonomy manually. Tags are suggestions — edit at will. + +## Versioning Note + +Pinned to upstream `v0.33.2` (2026-08-11 release). Renovate does not currently auto-bump Karakeep because the image registry pattern doesn't match the regex in `renovate.json` — bump manually by editing `apps/karakeep/{config.json,docker-compose.json}` and opening a PR. + +## Volumes + +All persistent data lives under `${APP_DATA_DIR}/data/`: + +- `app/` — Karakeep's data dir (bookmarks DB, uploads, etc.) +- `meili_data/` — Meilisearch index data + +Back these up together to fully restore. + +## Security + +`NEXTAUTH_SECRET` and `MEILI_MASTER_KEY` are auto-generated as random hex strings on first install. Save them somewhere safe (1Password) — losing them invalidates all sessions and the search index. diff --git a/apps/karakeep/metadata/logo.jpg b/apps/karakeep/metadata/logo.jpg new file mode 100644 index 0000000..0477833 Binary files /dev/null and b/apps/karakeep/metadata/logo.jpg differ