hearthworks: merge homelab stacks + move flake to nix/ + packages scaffold #2

Merged
trent-maetzold merged 388 commits from monorepo into main 2026-08-14 07:25:04 -05:00

Imports homelab history (agents branch, deployed on Unraid) at the repo root, moves the NixOS/Home-Manager flake under nix/, scaffolds the packages/ sub-monorepo. Merge with a MERGE COMMIT (not squash/rebase — squash would drop homelab's 354 commits from main's history).

Imports homelab history (agents branch, deployed on Unraid) at the repo root, moves the NixOS/Home-Manager flake under nix/, scaffolds the packages/ sub-monorepo. Merge with a MERGE COMMIT (not squash/rebase — squash would drop homelab's 354 commits from main's history).
Docker Compose stacks and configs extracted from the infra repo.
Flattened services/ nesting — stacks/ and config/ now at root.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from gurucomputing/headscale-ui to tale/headplane for a
feature-complete management UI with Docker integration, healthchecks,
and direct headscale config access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace headscale-ui with headplane

See merge request trkm/homelab!1
- Set server_url, listen_addr, base_domain (ts.trkm.io), and ACL policy path
- Change admin user from trent@ to trkm@ in ACL
- Add Unraid labels (icon, webui, shell) to headscale and headplane services
- Document Unraid label conventions in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Configure headscale stack and Unraid labels

See merge request trkm/homelab!2
Portainer/Compose requires healthcheck test to start with CMD, CMD-SHELL,
or NONE when using list syntax.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tini as entrypoint for proper PID 1 signal handling
- Bake HOME, TERM, EXPOSE into Dockerfile
- Split ENTRYPOINT/CMD for easy bind mode override
- Remove host port mappings (Traefik handles routing)
Fix headplane healthcheck CMD prefix

See merge request trkm/homelab!3
- Fix admin user from trkm@ to tm@ in ACL config
- Add SSH access policy for admin group to all nodes
- Pin headplane image to 0.6.2-beta.5

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: update headscale ACL with SSH rules and pin headplane version

See merge request trkm/homelab!4
- Remove Python, uv, vim, wget, build-essential, openssh-client (MCPs handle own deps)
- Remove protonmail-bridge sidecar (email via mcp-email)
- Remove YNAB, IMAP/SMTP, Protonmail env vars from compose
- Add MCP_TOKEN env var for MCP server auth
- Keep Docker CLI + dind for container-based tooling
git, curl, ca-certificates, gnupg, procps already in node:22-bookworm.
Only jq and docker-ce-cli actually need installing.
- Python3 + venv + uv for Python dev
- Rust via rustup (system-wide at /usr/local/cargo)
- Go 1.24.0 at /usr/local/go
- build-essential for native compilation
- All tools accessible to node user at runtime
- Base image: node:lts-trixie (Node 24, Debian 13)
- Go 1.24 and Rust 1.85 from Trixie repos (current stable)
- Remove curl-pipe-sh installers for Rust and Go
- Docker apt repo uses dynamic VERSION_CODENAME
- uv still from installer (not in Debian repos)
- Add back /mnt/user/appdata/openclaw/appdata mount to both openclaw and dind
- Restore -ro suffix on host reference mounts to avoid shadowing writable appdata
- openclaw: bash shell, openclaw icon
- dind: sh shell (Alpine, no bash), docker icon
- Order: icon → webui → shell (consistent with other stacks)
- Use /bin/bash and /bin/sh (not bare bash/sh)
- Use refs/heads/main in homarr icon URLs
refactor: slim openclaw container for MCP model

See merge request trkm/homelab!5
- New stack: mcp-servers with mcp-container and mcp-git services
- Both use SSE transport on port 8000
- External 'mcp' network for inter-container communication
- mcp-container talks to Portainer via internal network
- mcp-git configured for gitlab.com
- Added openclaw to mcp network for MCP client access
- Note: mcp-git image not yet built (needs Dockerfile + CI)
- Add MCP_TOKEN to both mcp-container and mcp-git for server auth
- Add GIT_USERNAME and GIT_TOKEN_REPO_READ to mcp-container for
  git-based stack deployments via Portainer
- Rename GITLAB_PAT to GITLAB_API_TOKEN on mcp-git
- Add MCP_PORTAINER_URL and MCP_GIT_URL to openclaw service so it
  knows where to find the MCP servers on the mcp network
- Rename GIT_TOKEN_REPO_READ to GITLAB_TOKEN_REPO_READ for specificity
Per review: use provider-specific container names instead of generic ones.
- mcp-container → mcp-portainer
- mcp-git → mcp-gitlab
- MCP_GIT_URL → MCP_GITLAB_URL
- Update openclaw URLs to match new container names
feat: add mcp-servers stack, join openclaw to mcp network

See merge request trkm/homelab!6
- Mount /builds to /mnt/user/appdata/gitlab-runner/builds
- Prevents large build artifacts from filling docker.img
- Tar artifacts now written to Unraid array instead of loop-mounted image

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add builds directory bind mount for GitLab runner

See merge request trkm/homelab!7
- Add gitlab-runner-dind container with docker:dind image
- Runner connects to dind via TCP with TLS
- Isolated Docker environment for CI jobs
- Organize dind data under /mnt/user/appdata/gitlab-runner/dind/
- Keeps builds mount for artifact storage on array
- Docker icon for dind container

Benefits:
- Clean isolation from host Docker
- No containers left on host after jobs
- Dedicated Docker daemon for CI
- Better resource management

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add dedicated dind service for GitLab runner

See merge request trkm/homelab!8
- Service name must match TLS certificate hostname
- Changed service name from 'dind' to 'docker'
- Added explicit hostname: docker
- DOCKER_HOST now uses tcp://docker:2376
- Fixes x509 certificate validation error

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Changed service name back to 'dind' (more descriptive)
- Set explicit hostname: dind (TLS cert will be valid for 'dind')
- DOCKER_HOST uses tcp://dind:2376
- Clearer intent - name matches what it is

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
fix: rename dind service to docker for TLS cert validation

See merge request trkm/homelab!9
- Mount /mnt/user/appdata/gitlab-runner/cache:/cache
- Allows jobs to cache artifacts locally instead of uploading to GitLab
- Speeds up builds with local dependency caching

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add cache bind mount for local artifact storage

See merge request trkm/homelab!10
- Changed from /mnt/user to /mnt/cache for dind data
- Avoids nested overlay filesystem error
- Cache drive has native filesystem (ext4/xfs/btrfs)
- Keeps data on local storage (not Docker volumes)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
fix: use cache drive for dind data to avoid overlay-on-overlay

See merge request trkm/homelab!11
Remove dind service and use host Docker socket directly:
- Simpler architecture, no TLS complexity
- Runner config handles Docker, not CI configs
- All containers benefit from daemon-level DNS fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: simplify GitLab runner to use host Docker socket

See merge request trkm/homelab!12
Replace local build with registry.gitlab.com/hardmcp/openclaw:latest
- No more local builds needed
- Automatically get latest updates from CI/CD
- Dockerfile kept for reference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from ANTHROPIC_API_KEY to ANTHROPIC_OAUTH_TOKEN for
authentication. The entrypoint auto-detects auth method and
configures accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add yaml-language-server comment and blank line to all stacks
- Add blank lines between services and before top-level keys
- Enforce service key ordering throughout
- Service names: kebab-case; container/volume/network names: snake_case
- Add TZ to all services missing it
- Fix networks/environment key ordering in authelia and gitea
- Fix command/healthcheck ordering in immich database service
- Remove empty net.unraid.docker.webui labels
- Add missing Unraid labels to gitea, mcp-servers, authelia-redis
- Add postgres stack (standalone postgres 18 alpine)
- Update CLAUDE.md with whitespace rules, naming conventions, appdata-db path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add proxy network + Traefik labels to sonarr, radarr, lidarr, prowlarr
- Add path-based routing for *-4k and *-anime arr instances
- Add gluetun to proxy network; Traefik labels on sabnzbd and qbittorrent
- Add Tailscale sidecar to traefik stack (hostname: proxy, Headscale auth)
- Update all webui labels from http://[IP]:[PORT] to *.trkm.io URLs
- Add new litellm stack with postgres/llm/proxy network support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Feat/gitlab runner builds bind mount
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Feat/gitlab runner builds bind mount
fix: update litellm url and icon
Add hostname (pos 2) to service key order in CLAUDE.md. Fix authelia
redis (command before labels) and gluetun (cap_add before ports) to
match the updated ordering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add hostname, TS_ACCEPT_DNS, TS_AUTH_ONCE, TS_USERSPACE to traefik
stack; remove TS_HOSTNAME (superseded by container hostname). Add
Traefik file provider configs for openclaw and unraid routing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add clickhouse as its own stack
- Fix key ordering, quoted numerics, TZ/labels on health service
- Update CLAUDE.md icon rule: always include, homarr → find → generate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Enable gateway auth, factor Anthropic provider defaults, and improve config readability. Also keep the TensorZero health sidecar alive long enough for healthy dependency gating and document compose exec-form guidance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move all config files into their stack directories (stacks/<stack>/config/)
  and mount tracked files directly via bind mounts on top of appdata
- Rename stack dirs to snake_case (cloudflare-ddns, home-assistant)
- Add komodo stack (core + periphery + mongo)
- Update CLAUDE.md: double-quote all env/label/command values, snake_case
  naming for everything except service names, command entry quoting
- Apply quoting conventions across all stacks (env values, label values,
  ports, command entries)
- Bump headplane to latest, traefik to v3, mariadb to 10.11
- Fix spec violations: missing labels on authelia/gitea-db, key ordering
  in recyclarr/tuwunel, unquoted ports across multiple stacks
- Remove stale stacks: gitlab-runner, litellm, mcp-servers, ollama,
  openclaw, postgres

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move dynamic configs into config/dynamic/ dir
- Parameterize CF_API_EMAIL and ACME email
- Remove hardcoded port 8080 exposure
- Consolidate tailscale appdata path under traefik/
- Remove standalone clickhouse stack

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move all quality/CF config into per-instance include files
- Update all trash IDs and scores to current trash guide recommendations
- Rename profiles to match trash guide names (HD Bluray + WEB, UHD Bluray + WEB, WEB-1080p, WEB-2160p, Remux-1080p)
- Fix UHD Bluray Tier IDs (were incorrectly using Remux Tier IDs)
- Add Repack3, Generated Dynamic HDR, Sing-Along Versions to unwanted
- Add Dutch Groups (hardcoded NL subs) to all Radarr profiles
- Use new 4-CF HDR system (HDR, DV Boost, HDR10+ Boost, DV w/o fallback)
- Enable audio codec scoring for UHD Radarr
- Enable movie version scoring (IMAX 800, Special Edition 125, etc.)
- Enable SDR/x265 unwanted for UHD profiles
- Enable misc filters (Obfuscated, Scene, No-RlsGroup, etc.) for all profiles
- Boost Anime Dual Audio to 2000 on both radarr/sonarr anime
- Set DV (w/o HDR fallback) to 0 for LG C1 + Apple TV 4K setup
- Mount includes dir in compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Convert include files from standalone config format to recyclarr include
format and configure optional CF groups: Miscellaneous penalty pack,
Movie Versions, HDR boosts, SDR filtering, Season Packs, and Anime Dual
Audio at score 2000. Remove unused profile variants (remux-web-alt,
remux-web-comb, sonarr web-alt/comb).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace invalid 'plex' and 'default' naming format keys with valid
TRaSH Guides format identifiers: plex-tmdb for Radarr folders/files,
plex-anime-tmdb for the anime Radarr instance, and plex-tvdb for
all Sonarr series.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removing explicit select: on default CFs in Golden Rule HD/UHD groups
eliminates recyclarr warnings about redundant CF selection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch to PORT_FORWARD_ONLY Chicago servers, enable VPN_PORT_FORWARDING,
and add up/down commands to automatically update qBittorrent's listen port
via its API whenever ProtonVPN assigns a new forwarded port. Remove the
now-unused static 6881 port binding and TORRENTING_PORT env var.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update CLAUDE.md: config path, dbdata path, shared data on FUSE,
  service/container naming rules, healthcheck format, shared DB docs,
  required variable syntax, inline examples
- Add openclaw stack
- Convert contextforge to postgres, add init script
- Rename immich services (server/ml/cache), add IMMICH_MACHINE_LEARNING_URL
- Strip CMD prefix from all healthcheck arrays
- Apply ${VAR:?required} to all secrets across all stacks
- openclaw: add user 1000:1000, fix healthcheck host to localhost,
  extend start_period to 60s, reduce retries to 3, remove dead
  commented-out command block
- contextforge + openclaw: replace --quiet with --tries 1 to prevent
  wget internal retries (Docker handles retry logic)
- Add init sidecar service that runs on every stack start after db is
  healthy, replacing first-boot-only initdb.d mechanism
- Extract shared ensure_user/ensure_db helpers into _lib.sh
- Make all init scripts idempotent (CREATE IF NOT EXISTS, ALTER USER,
  SELECT/\gexec for databases)
- Remove init mount and tenant password vars from db service
- Move dedicated clickhouse instance from stacks/clickhouse/ into tensorzero stack
- Rename postgres service from 'db' to 'postgres' (container: tensorzero_postgres)
- Add clickhouse service with container name tensorzero_clickhouse
- Update all connection URLs and depends_on references
- Remove external clickhouse network (now uses internal 'net')
- Update volume paths to follow convention:
  - postgres: /mnt/cache/dbdata/postgres/tensorzero/data
  - clickhouse: /mnt/cache/dbdata/clickhouse/tensorzero/{data,log}
- Update compose.override.yaml with labels for new services

BREAKING CHANGE: External clickhouse stack removed. Services now internal
to tensorzero stack with scoped container names.
Add OPENCODE_API_KEY and OPENROUTER_API_KEY environment variables
to the gateway service for additional LLM provider support.
- Add 8 OpenCode Go models (GLM, Kimi, MiMo, MiniMax) with OpenCode primary and OpenRouter fallback
- Configure environment variable substitution for API base and keys:
  - OPENCODE_GO_API_BASE
  - OPENCODE_API_KEY
  - OPENROUTER_API_KEY
- Create config/models/ directory with model definitions
- Update compose.yaml to load config via glob pattern
- Mount entire config directory instead of individual files

Excludes Qwen models (awaiting TensorZero Alibaba provider support)
Add clear visual separation between model sections using box drawing
characters to improve readability for ADHD accessibility:

- Double-line header for file category
- Single-line separators for each model
- Blank lines for visual breathing room
Use cleaner single-line separator pattern with consistent indentation:

  ───── Model Name ─────

Easier to scan and less visual noise for ADHD accessibility.
- Fix kimi.toml: moonshot/ → moonshotai/ for OpenRouter
- Standardize all model configs to use same visual separator style
- Shorter separator lines for better readability

Verified correct OpenRouter IDs:
- z-ai/glm-5, z-ai/glm-5.1
- moonshotai/kimi-k2.5, moonshotai/kimi-k2.6
- xiaomi/mimo-v2-pro, xiaomi/mimo-v2-omni
- minimax/minimax-m2.5, minimax/minimax-m2.7
- Update Traefik router rule to use PathPrefix() for api.trkm.io/t0
- Rename provider 'opencode' to 'opencode-go' in all model configs
  (kimi.toml, glm.toml, mimo.toml, minimax.toml)
- Add ProtonMail SMTP config (STARTTLS on 587)
- Use YAML anchor to share env vars between server and worker
- Add steady-state roadmap plans
- Add authelia stack with postgres 18, valkey cache, and smtp notifier
- Add traefik-dashboard.yaml file provider for api@internal routing
- Protect traefik dashboard with authelia@docker middleware
- Add .prettierrc.yaml with relaxed print width for yaml files
- Configure bypass rules for service APIs (radarr, sonarr, lidarr, etc.)
- Add webhook endpoint for future agent integration at api.trkm.io/webhook/github
- Update traefik and sabnzbd configs
- authelia: add cache and db overrides

- postgres: add init override

- seerr: add db override

- normalize icon URLs to cdn.jsdelivr.net
- archlinux:base-devel with uv, npm, rustup, go, gh, khal, vdirsyncer

- grouped pacman packages by category

- override tool dirs to $HOME/.local/share/{toolchain}

- add OPENCODE_API_KEY env var

- switch from upstream image to local build
- Add hermes stack with gateway service (nousresearch/hermes-agent)
- Add honcho stack (api, deriver, db, cache)
- Update AGENTS.md shell spec: prefer bash, fallback to sh, blank if neither
- Fix shell labels across 12 stacks to match actual image capabilities
- Change traefik tailscale icon to tailscale-light
- Update openclaw Dockerfile WORKDIR and CMD
- Ignore plans/ directory in git
- Mount config.toml into api service
- Remove model config env vars (now in toml)
- Add upstream example toml with source URL for agent reference
- Add empty config.toml for runtime use
feat: add gitea with dedicated postgres
- dind service runs docker:dind with privileged mode on the 'net' network
- TLS certs at /mnt/cache/appdata/hermes/shared/certs (shared with host for remote access)
- DOCKER_HOST=tcp://dind:2375 tells the hermes container to use dind
- No port exposure needed — both on the same internal network
Gateway (the primary service) leads, with LLM API keys and matrix config at the top of environment — logical hierarchy for an agent-first stack. DOCKER_HOST moved below those. Dind sidecar follows as a supporting service.
- dind: set DOCKER_TLS_CERTDIR=/certs to enable TLS on port 2376
- gateway: mount /certs and set DOCKER_HOST/DOCKER_CERT_PATH to use TLS
- key ordering fixed per AGENTS.md: container_name, image, restart, then
  volumes/networking before environment; dind env TZ at top
- Service/container/hostname: static → artifacts
- Host: static.trkm.io → artifacts.trkm.io
- Volume: /opt/data/home/www/html:/www:ro → /mnt/cache/appdata/artifacts/app/data:/srv/artifacts:ro
- Remove ports (traefik handles routing)
- Update Unraid labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move server, database, and oauth2_client settings from compose env vars
into app.ini; retain only PASSWD in compose for secret injection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Forgejo keeps GITEA_CUSTOM=/data/gitea from its Gitea lineage, so the
config lives at /data/gitea/conf/app.ini, not /data/app.ini.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Valkey cache service; wire cache/session/queue in app.ini
- Inject FORGEJO_SECRET_KEY and FORGEJO_INTERNAL_TOKEN via compose env vars
  (generated with: forgejo generate secret SECRET_KEY / INTERNAL_TOKEN)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aligns authelia, contextforge, openwebui, searxng, and forgejo with
immich and honcho which were already on valkey:9.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Let Forgejo manage its own ini in the bind mount. Non-secret config
moves to forgejo.env; generated secrets (SECRET_KEY, INTERNAL_TOKEN,
LFS_JWT_SECRET, JWT_SECRET) are auto-generated by Forgejo on first run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Negate *.env gitignore for config/ subdirectories so non-secret
env config files can be committed alongside their stacks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add X_AUTHELIA_CONFIG_FILTERS=template so the oidc.pem secret function
and env interpolation are processed. Switch client_secret to Go template
env syntax.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FastAPI sidecar that translates models.dev catalog data into Bifrost's
datasheet format. Provides two endpoints:

  GET /datasheet              — model pricing in Bifrost format
  GET /datasheet/model-parameters — model parameter definitions

Uses uv-managed project with hatchling build backend, built into a
multi-stage Docker image copying only the venv to runtime.
Switches from default rsgi to asgi interface since FastAPI/Starlette require
the standard ASGI protocol with (scope, receive, send) signature.

Restructures CMD as ENTRYPOINT + CMD so args like --host and --port can be
overridden at runtime. Keeps --mount=type=cache for BuildKit builds.
6 tests:
- 2 schema unit tests covering BifrostPricingModel (chat + image_generation)
- 2 schema unit tests covering BifrostParameterModel (with/without params)
- 2 endpoint registration tests verifying route paths, query params, and
  response model refs via the app's OpenAPI schema
Marks TestEndpointRegistration with @pytest.mark.integration and defaults
to 'not integration' so  runs only unit tests.

Run integration tests:  uv run pytest -m integration
Run all tests:         uv run pytest -m ''
Regenerate app schemas from getbifrost.ai using datamodel-code-generator with
composite JSON samples. Add models.dev client schemas from live API.
Structure:

  schemas/app.py                          — Bifrost output models
  schemas/clients/models_dev.py           — models.dev input models

Integration tests hit the live APIs and validate that every response entry
parses against the corresponding Pydantic model, ensuring schema fidelity.

Run:  uv run pytest -m integration
All:  uv run pytest -m ''
- Add docstrings to all models, clients, and config classes
- Remove unused  import from schemas/clients/models_dev.py
- Remove stale test_main.py (superseded by client tests and live schema tests)
- Clean up duplicate class definitions in app.py
Tests still referenced old ModelCatalog, BifrostPricingModel, BifrostParameterModel
names. Also fixed a corrupted test function (def line was lost during edit).

Changes:
- tests/test_live_schemas.py: import ModelsDevCatalog, PricingModel, ParameterModel
- schemas/app.py: fill descriptive docstrings on RootModel wrappers
- Move schemas/app.py → schemas/bifrost.py
- Rename BifrostPricingModels → BifrostPricingDatasheet
- Rename BifrostParameterModels → BifrostModelParametersDatasheet
- Update all imports across clients, main, and tests
Implement from_api(), to_datasheet(), to_datasheet_model_parameters() on
ModelsDevClient with conversion logic between models.dev and Bifrost formats.

Add unit tests under tests/clients/test_models_dev.py using a live-data
fixture (3 providers, 9 models) with httpx2 mocked.

Conversion notes:
- models.dev cost per million tokens → Bifrost per-token cost (/1_000_000)
- limit.context → max_input_tokens, limit.output → max_{output_}tokens
- Modalities infer mode: 'image' in input → image_generation, else chat
- Capability flags mapped directly where available
- Filter by provider string and BifrostMode in both to_pricing_datasheet
  and to_model_parameters_datasheet
- Add unit tests for provider filter, mode filter, and unknown-provider edge case
- Fix config.py Source.url to use module-level dict for StrEnum safety
- Rename stacks/bifrost/extensions/modelparams → model-params
- Keep Python package name as modelparams (src/modelparams/)
- Add full ext-model-params service definition to compose.yaml
- Add unraid composeman labels in compose.override.yaml
- Update all imports and references
- Add GET /health endpoint returning {"status":"ok"}
- Update Docker healthcheck to use /health
- Add framework.pricing block to bifrost config.json with pricing_url
  and model_parameters_url pointing to ext-model-params:8000
Ensures docker compose up always rebuilds the local image when source changes.
- Rename schemas/bifrost.py → schemas/getbifrost_ai.py
- Move schemas/clients/models_dev.py → schemas/models_dev.py
- Rename clients/__init__.py → clients/getbifrost_ai.py (stub)
- Update all imports across clients, main, and tests
- Add GETBIFROST_AI source to config with URL mapping
- Implement GetBifrostAiClient that fetches from getbifrost.ai endpoints,
  validates through Pydantic models, and filters on provider/mode
- Register in clients factory
fix(litellm): make master key optional, add service label to traefik router
The headroom opencode-go service runs at root path but is accessed via PathPrefix(/opencode-go). Added stripPrefix middleware to remove the prefix before forwarding to the container.

Also added Traefik labels, proxy network, and corrected middleware naming.
Adds the OpenHands canvas service with Docker-in-Docker support:

- canvas: agent-canvas with DOCKER_TLS connectivity to dind
- dind: docker:dind sidecar with TLS cert generation
- Internal net network for inter-service communication
- Unraid composeman labels for both services
- Read-only certs mount for canvas
- rename hosts/laptop to hosts/wsl, drop hosts/gamer
- add modules/llama-swap.nix with config/llama-swap/config.yaml
- set nixConfig (numtide cache, flakes) at flake level
- drop herdr and llm-agents overlays; pin llm-agents nixpkgs
- reorganize home-manager modules under home/modules
- move bifrost config to home/config, add omp agent models.yml
- split nvidia module into ctk.nix, remove unused llama.nix
- new hosts/{family,gamer,laptop}; wsl module moved to roles/wsl.nix
- agents/base: omp-bin plus llm-agents (bernstein, gnhf, oh-my-opencode, opencode, zaly)
- docker: bx distrobox container with self-healing wrapper
- flake: hoist nixConfig into attrs, numtide cache; omp-bin 17.2.12
- wsl: startMenuLaunchers; bump flake.lock
- rewrite modules/llama-swap.nix: per-model enable flags, generate config.yaml in Nix
- delete config/llama-swap/config.yaml (now Nix-generated)
- hosts/gamer: enable llama-swap + qwopus/refact/lfm-2.6b/lfm-8b models
- omp models.yml -> llama-swap provider at localhost:8081; drop bifrost service import
- format hosts/family with alejandra
- llama-swap: host port 8080, model name derived from attrset keys, preload refact at startup, --cache-reuse for FIM
- remove bifrost (orphaned) and the copilot LSP (extra + lspconfig + package)
- omp: point llama-swap provider at localhost:8080
- nvim: minuet-ai.nvim via openai_fim_compatible (Refact FIM), blink interaction config (preselect=false, ghost text off, lua fuzzy), A-y regenerate
- LazyVim: pin coding.blink, add 17 extras, bashls + shellcheck packages
- hosts gamer/laptop: enable llama-swap models (refact + liquids, qwopus on both)
- home: move default.nix to tm/; gitignore result
# Conflicts:
#	.gitignore
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
trent-maetzold/hearthworks!2
No description provided.