Skip to content

hq.kevinryan.io

The HQ site at hq.kevinryan.io is an internal AI chat workspace built on upstream, unmodified LibreChat. It is the deliberate exception to every static-site constraint in this repository: there is no build step, no Dockerfile, and no committed application source. All branding is applied as an overlay layer at deploy time, on top of the digest-pinned upstream image. The architecture and its trade-offs are decided in ADR-023.

TechnologyVersionRole
LibreChatupstream image, digest-pinned (sha256:c5db3331…)Chat application (pre-built upstream image, pulled direct — never built here)
MongoDB8.0Chat/data store (internal StatefulSet, not Azure PostgreSQL)
Kubernetes—Deployment, StatefulSet, IngressRoute, PVCs, ConfigMaps
Flux CD—GitOps reconciliation from k8s/hq-kevinryan-io/
External Secrets Operator—Syncs librechat-secrets from Azure Key Vault

There is no build toolchain for this site — no Next.js, no Astro, no pnpm scripts. The repo deliberately contains manifests and overlay sources only.

graph TD
    subgraph overlay["Overlay layer (deploy-time branding)"]
        patch["patch-index initContainer<br/>(sed-patched index.html + JS assets,<br/>fail-loud post-patch guards)"]
        theme["librechat-custom ConfigMap<br/>(custom-theme.css, logo.svg, favicons)"]
    end

    subgraph image["Upstream image (unmodified, digest-pinned)"]
        lc["LibreChat app<br/>registry.librechat.ai/danny-avila/librechat@sha256:…"]
    end

    subgraph cluster["hq-kevinryan-io namespace"]
        deploy["librechat Deployment<br/>(theme-patch emptyDir mounts<br/>over /app/client/dist/)"]
        mongo["librechat-mongodb StatefulSet<br/>(mongo:8.0 + PVC)"]
        pvc["PVCs: data, uploads, images"]
    end

    patch -->|patched index.html + assets| deploy
    theme -->|mounted over /app/client/dist/| deploy
    deploy --> lc
    deploy -->|MongoDB URI| mongo
    deploy --> pvc
    eso["External Secrets Operator"] -.->|ANTHROPIC_API_KEY<br/>from Azure Key Vault| deploy

Everything runs in the hq-kevinryan-io namespace, managed by Flux from k8s/hq-kevinryan-io/:

ResourceNotes
librechat Deployment1 replica; initContainer + container use the same digest-pinned image
librechat-mongodb StatefulSet + Servicemongo:8.0, own PVC — internal to the namespace, not Azure PostgreSQL
PVCslibrechat-data, librechat-uploads, librechat-images (logs/skill are emptyDirs)
librechat-config ConfigMapApp config incl. the Claude model list
librechat-custom ConfigMapTheme overlay assets — generated by scripts/sync-hq-theme.sh
librechat-secrets ExternalSecretSynced from Azure Key Vault via the ClusterSecretStore
IngressRouteTraefik TLS for hq.kevinryan.io

Health probes are httpGet /health (verified on the pinned digest — re-verify whenever the digest is bumped). The shared CI deploy workflow auto-skips this site because it has no Dockerfile; changes deploy via Flux on push to main only.

All branding — Tokyo Night Moon dark-only theme, “HQ - Kevin Ryan & Associates” title, kevinryan.io favicons, transparent login logo, patched login heading — is applied at deploy time, never baked into an image:

  1. patch-index initContainer copies the image’s index.html and assets/ into an emptyDir, then seds in the theme stylesheet link, the branded title, Tokyo Night Moon loading-screen colors, dark-mode forcing, and the en-locale login heading in the compiled JS bundle.
  2. Fail-loud guards follow every patch: if any post-condition is missing (upstream changed the index.html shape), the initContainer exits 1 and the rollout blocks — the site never silently deploys unthemed.
  3. librechat-custom ConfigMap mounts custom-theme.css, logo.svg, and favicons over /app/client/dist/.
  4. Cache busting is content-derived: the CSS ?v= is the first 8 hex of sha256(custom-theme.css); the JS asset ?v= is the hash of the patched bundle — so the service worker precache busts exactly when content changes. Precompressed .br/.gz siblings of the patched JS are deleted so the patched file is what gets served.

The overlay sources live in sites/hq-kevinryan-io/ (custom-theme.css, logo.svg, favicons); scripts/sync-hq-theme.sh regenerates the ConfigMap from them, and validate.yml runs it in --check mode on every push to block drift between sources and the deployed ConfigMap.

Any change to this layer — theming, branding, or a LibreChat image digest bump — must follow the patch-librechat-theme skill (.pi/skills/), including the mandatory throwaway-pod guard test before any image bump. Never edit the generated ConfigMap by hand.

  • Auth: LibreChat native email/password. The Auth0/GitHub OAuth layer from ADR-021 was dropped in the re-platform to vanilla LibreChat — ADR-021 is superseded by ADR-023.
  • Claude endpoint: enabled via ANTHROPIC_API_KEY, synced from Azure Key Vault by External Secrets Operator. The model list (Opus/Sonnet/Haiku 4–5 families) is set in librechat-config.

hq.kevinryan.io is the one site excluded from the repo’s static-export and zero-runtime constraints:

  • No static export (server-side Node app + MongoDB)
  • No build step, no Dockerfile, no committed app source — CI deploy auto-skips it
  • No TypeScript/React/Tailwind conventions apply (there is no first-party frontend code)
  • Upstream updates are deliberate, gated manual changes (digest bump + guard test), not CI-pushed builds

The trade-off is explicit in ADR-023: zero build maintenance in exchange for an overlay that is brittle to upstream UI changes — with the brittleness converted into loud, blocking failures (rollout guards, CI drift checks, pre-bump guard tests).