diff --git a/ansible/inventory.yml b/ansible/inventory.yml index 55c8733..86748c9 100755 --- a/ansible/inventory.yml +++ b/ansible/inventory.yml @@ -72,6 +72,14 @@ hermes_server: ansible_host: 10.1.71.131 ansible_user: wed ansible_become: true + +honcho_server: + hosts: + lincoln: + ansible_host: 10.1.71.132 + ansible_user: wed + ansible_become: true + papermc_server: # ansible-galaxy role install engonzal.papermc hosts: diff --git a/ansible/playbooks/day1_deploy_honcho.yml b/ansible/playbooks/day1_deploy_honcho.yml new file mode 100644 index 0000000..059c608 --- /dev/null +++ b/ansible/playbooks/day1_deploy_honcho.yml @@ -0,0 +1,17 @@ +--- +# ============================================================================ +# day1_deploy_honcho.yml +# ---------------------------------------------------------------------------- +# Deploys Honcho + pgvector PostgreSQL on the `lincoln` host. Run AFTER +# day0_linux_baseline.yml has been applied to the target. +# +# Usage: +# ansible-playbook -i inventory.yml playbooks/day1_deploy_honcho.yml +# ============================================================================ + +- name: Deploy Honcho on lincoln + hosts: honcho_server + become: true + gather_facts: true + roles: + - honcho diff --git a/ansible/roles/honcho/README.md b/ansible/roles/honcho/README.md new file mode 100644 index 0000000..6b9d567 --- /dev/null +++ b/ansible/roles/honcho/README.md @@ -0,0 +1,85 @@ +# Honcho role + +Deploys [Honcho](https://honcho.dev) — Plastic Labs' memory + theory-of-mind +layer for stateful agents — on a single mk-labs VM (`lincoln`) using +rootful Podman + Quadlet. + +## Topology + +``` + ┌──────────────────────────────┐ + │ lightning-lane (Traefik) │ + │ hall-of-presidents.l... │ + └─────────────┬────────────────┘ + │ + │ HTTP :8000 + ▼ + ┌──────────────────────────────── lincoln ───────────────────────────────┐ + │ │ + │ ┌───────────────┐ ┌───────────────┐ ┌──────────────────────┐ │ + │ │ honcho-api │ │ honcho-deriver│ │ honcho-postgres │ │ + │ │ (FastAPI:8000)│◄──►│ (worker loop) │◄──►│ pgvector/pgvector:pg16│ │ + │ └──────┬────────┘ └──────┬────────┘ └──────────────────────┘ │ + │ │ │ │ + │ └────────┬───────────┘ │ + │ ▼ │ + │ Anthropic Claude API (outbound, east-west to Internet) │ + │ │ + └────────────────────────────────────────────────────────────────────────┘ +``` + +## What this role does + +1. Installs Podman + ensures the rootful Quadlet drop-in dir exists. +2. Creates a user-defined Podman network (`honcho-net`). +3. Creates a named volume for Postgres data (`honcho_postgres_data`). +4. Deploys a pgvector-enabled Postgres 16 container and waits for it to be + ready. +5. Deploys the Honcho API container (`honcho-api`) — FastAPI on :8000. +6. Deploys the Honcho deriver worker container (`honcho-deriver`). +7. Verifies the API answers on `/docs`. + +## Required vault entries + +Add to `group_vars/all/vault` (ansible-vault encrypted): + +```yaml +vault_honcho_database_password: "" +vault_honcho_jwt_secret: "<32+ byte random>" +vault_honcho_anthropic_api_key: "sk-ant-..." +``` + +## LLM provider switching + +This role starts with Anthropic Claude. To swap to a local +OpenAI-compatible endpoint later (e.g. astro-orbiter once we have the +GPU running llama.cpp/Ollama in OpenAI-compatible mode), override these +in `group_vars/honcho_server` or via a playbook variable: + +```yaml +honcho_llm_transport: "openai" +honcho_deriver_model: "qwen3:8b" +honcho_summary_model: "qwen3:8b" +honcho_dialectic_model: "qwen3:8b" +# and add LLM_OPENAI_API_KEY (or set the base URL override in the template) +``` + +## Traefik route + +The Traefik file-provider YAML for `hall-of-presidents.local.mk-labs.cloud` +lives at `boilerplates/traefik/dynamic/honcho.yml`. It points at +`http://lincoln.local.mk-labs.cloud:8000`. + +## Known Quadlet pitfall (carried from the semaphore role) + +Quadlet regenerates the systemd unit on `.container` file change but does +NOT restart the running container. This role explicitly handles that by +gating a `state: restarted` task on the template's `changed` status — +the same pattern documented in `homelab-application-deployment`'s +quadlet-patterns reference. + +## Honcho version pinning + +The `honcho_image` default is `ghcr.io/plastic-labs/honcho:latest`. +Move to a digest-pinned tag once we've validated the deployment works +against a known-good build. diff --git a/ansible/roles/honcho/defaults/main.yml b/ansible/roles/honcho/defaults/main.yml new file mode 100644 index 0000000..0b02e53 --- /dev/null +++ b/ansible/roles/honcho/defaults/main.yml @@ -0,0 +1,109 @@ +--- +# ============================================================================ +# honcho role defaults +# ============================================================================ +# Deploys Honcho (https://honcho.dev — plastic-labs/honcho) as a rootful +# Podman + Quadlet service on a single VM. Three containers: +# +# honcho-postgres pgvector-enabled PostgreSQL backing store +# honcho-api FastAPI server on :8000 (theory-of-mind read/write) +# honcho-deriver background worker that consumes the queue and calls +# out to the configured LLM provider for derivations +# +# All three share a user-defined podman network. State persists to named +# volumes. Traefik on lightning-lane terminates TLS and routes the +# `hall-of-presidents.local.mk-labs.cloud` host to honcho-api:8000. +# +# LLM provider for the first deployment is Anthropic Claude. Switching +# providers is a single env-var change (see DERIVER_MODEL_CONFIG__TRANSPORT +# / SUMMARY_MODEL_CONFIG__TRANSPORT and the LLM_*_API_KEY variables). +# ============================================================================ + +# --------------------------------------------------------------------------- +# Image pinning +# --------------------------------------------------------------------------- +# Honcho does not yet publish a Docker Hub image we trust; we use the +# GitHub Container Registry build. Pin to a digest-stable tag. +honcho_image: "ghcr.io/plastic-labs/honcho:latest" +# pgvector/pgvector image follows the upstream postgres version channel. +honcho_postgres_image: "docker.io/pgvector/pgvector:pg16" + +# --------------------------------------------------------------------------- +# Container & network identifiers +# --------------------------------------------------------------------------- +honcho_container_name: honcho-api +honcho_deriver_container_name: honcho-deriver +honcho_postgres_container_name: honcho-postgres +honcho_network_name: honcho-net + +# Named podman volumes +honcho_postgres_volume: honcho_postgres_data + +# --------------------------------------------------------------------------- +# Networking +# --------------------------------------------------------------------------- +# Honcho's FastAPI default port is 8000. Bind to 0.0.0.0 so Traefik on +# lightning-lane can reach it; auth is enabled (JWT) so the open port is +# not an open door. +honcho_listen_address: "0.0.0.0" +honcho_listen_port: 8000 + +# Public-facing URL used for absolute links / OIDC callbacks. +# Leave EMPTY to make Honcho host-agnostic — FastAPI emits relative URLs. +# Set to a fully-qualified URL only if a specific feature requires it. +honcho_web_url: "" + +# --------------------------------------------------------------------------- +# PostgreSQL configuration +# --------------------------------------------------------------------------- +honcho_db_user: honcho +honcho_db_name: honcho +# honcho_db_password sourced from vault below + +# pgvector-enabled Postgres uses the same env vars as the stock image +honcho_postgres_initdb_args: "--encoding=UTF8 --locale=C" + +# --------------------------------------------------------------------------- +# LLM provider configuration +# --------------------------------------------------------------------------- +# Supported transports: openai, anthropic, gemini. We start with anthropic +# and can flip to a local OpenAI-compatible endpoint later by changing +# these two values plus the API-key env-var name. +honcho_llm_transport: "anthropic" +honcho_deriver_model: "claude-sonnet-4-5" +honcho_summary_model: "claude-sonnet-4-5" +honcho_dialectic_model: "claude-sonnet-4-5" + +# Deriver tuning — number of workers, polling cadence, etc. Conservative +# defaults appropriate for a small homelab deployment. +honcho_deriver_enabled: true +honcho_deriver_workers: 1 +honcho_deriver_polling_seconds: "1.0" + +# --------------------------------------------------------------------------- +# Auth +# --------------------------------------------------------------------------- +# AUTH_USE_AUTH=true means clients must present a JWT signed with +# AUTH_JWT_SECRET. The secret lives in vault. +honcho_auth_enabled: true + +# --------------------------------------------------------------------------- +# Vault inputs (defined in group_vars/all/vault, encrypted with ansible-vault) +# --------------------------------------------------------------------------- +# vault_honcho_database_password - postgres role password +# vault_honcho_jwt_secret - 32+ byte random string for JWT signing +# vault_honcho_anthropic_api_key - Anthropic API key for Claude calls +honcho_db_password: "{{ vault_honcho_database_password }}" +honcho_jwt_secret: "{{ vault_honcho_jwt_secret }}" +honcho_anthropic_api_key: "{{ vault_honcho_anthropic_api_key }}" + +# --------------------------------------------------------------------------- +# Health check +# --------------------------------------------------------------------------- +honcho_health_check_retries: 30 +honcho_health_check_delay: 2 + +# --------------------------------------------------------------------------- +# Quadlet location (rootful) +# --------------------------------------------------------------------------- +honcho_quadlet_dir: /etc/containers/systemd diff --git a/ansible/roles/honcho/meta/main.yml b/ansible/roles/honcho/meta/main.yml new file mode 100644 index 0000000..961bef1 --- /dev/null +++ b/ansible/roles/honcho/meta/main.yml @@ -0,0 +1,27 @@ +--- +galaxy_info: + role_name: honcho + author: JARVIS + description: >- + Deploys Honcho (plastic-labs/honcho) — a memory + theory-of-mind layer + for stateful agents — with a pgvector-enabled PostgreSQL backing store, + via rootful Podman Quadlet on Ubuntu. Designed for the mk-labs + `lincoln` host. Fronted by Traefik at hall-of-presidents.local.mk-labs.cloud. + license: MIT + min_ansible_version: "2.14" + platforms: + - name: Ubuntu + versions: + - noble + - jammy + galaxy_tags: + - honcho + - memory + - llm + - podman + - quadlet + - homelab + +# linux-baseline is applied separately as a day0 playbook. We don't depend +# on it here so this role stays composable. +dependencies: [] diff --git a/ansible/roles/honcho/tasks/api.yml b/ansible/roles/honcho/tasks/api.yml new file mode 100644 index 0000000..ae48a1d --- /dev/null +++ b/ansible/roles/honcho/tasks/api.yml @@ -0,0 +1,34 @@ +--- +# ============================================================================ +# Honcho API container (Quadlet) +# ---------------------------------------------------------------------------- +# Runs `fastapi run src/main.py` via the image's default CMD. The +# entrypoint.sh in the image runs the DB migration first; safe to do on +# every restart (idempotent). +# ============================================================================ + +- name: Deploy Honcho API Quadlet + ansible.builtin.template: + src: honcho-api.container.j2 + dest: "{{ honcho_quadlet_dir }}/{{ honcho_container_name }}.container" + owner: root + group: root + mode: "0644" + register: honcho_api_quadlet + +- name: Reload systemd to pick up Quadlet changes + ansible.builtin.systemd: + daemon_reload: true + when: honcho_api_quadlet.changed + +- name: Ensure Honcho API container is started and enabled + ansible.builtin.systemd: + name: "{{ honcho_container_name }}.service" + state: started + enabled: true + +- name: Restart Honcho API on Quadlet change + ansible.builtin.systemd: + name: "{{ honcho_container_name }}.service" + state: restarted + when: honcho_api_quadlet.changed diff --git a/ansible/roles/honcho/tasks/deriver.yml b/ansible/roles/honcho/tasks/deriver.yml new file mode 100644 index 0000000..89749c4 --- /dev/null +++ b/ansible/roles/honcho/tasks/deriver.yml @@ -0,0 +1,34 @@ +--- +# ============================================================================ +# Honcho deriver worker container (Quadlet) +# ---------------------------------------------------------------------------- +# Same image as the API container, but runs the deriver script instead of +# the FastAPI server. Pulls jobs off the in-database queue and calls the +# configured LLM provider for theory-of-mind derivations. +# ============================================================================ + +- name: Deploy Honcho deriver Quadlet + ansible.builtin.template: + src: honcho-deriver.container.j2 + dest: "{{ honcho_quadlet_dir }}/{{ honcho_deriver_container_name }}.container" + owner: root + group: root + mode: "0644" + register: honcho_deriver_quadlet + +- name: Reload systemd to pick up Quadlet changes + ansible.builtin.systemd: + daemon_reload: true + when: honcho_deriver_quadlet.changed + +- name: Ensure Honcho deriver container is started and enabled + ansible.builtin.systemd: + name: "{{ honcho_deriver_container_name }}.service" + state: started + enabled: true + +- name: Restart Honcho deriver on Quadlet change + ansible.builtin.systemd: + name: "{{ honcho_deriver_container_name }}.service" + state: restarted + when: honcho_deriver_quadlet.changed diff --git a/ansible/roles/honcho/tasks/main.yml b/ansible/roles/honcho/tasks/main.yml new file mode 100644 index 0000000..7e15b43 --- /dev/null +++ b/ansible/roles/honcho/tasks/main.yml @@ -0,0 +1,42 @@ +--- +# ============================================================================ +# honcho / main entrypoint +# ---------------------------------------------------------------------------- +# Order matters: +# 1. Podman + Quadlet support installed and ready +# 2. Network created (containers reference it by name) +# 3. Volumes created (postgres data) +# 4. Postgres started (Honcho API + deriver depend on it being ready) +# 5. Honcho API started +# 6. Honcho deriver worker started +# 7. Verify reachable on listen port +# ============================================================================ + +- name: Install Podman and dependencies + ansible.builtin.import_tasks: podman.yml + tags: [honcho, podman] + +- name: Ensure podman network exists + ansible.builtin.import_tasks: network.yml + tags: [honcho, network] + +- name: Ensure podman volumes exist + ansible.builtin.import_tasks: volumes.yml + tags: [honcho, volumes] + +- name: Deploy PostgreSQL (pgvector) container + ansible.builtin.import_tasks: postgres.yml + tags: [honcho, postgres] + +- name: Deploy Honcho API container + ansible.builtin.import_tasks: api.yml + tags: [honcho, api] + +- name: Deploy Honcho deriver worker + ansible.builtin.import_tasks: deriver.yml + when: honcho_deriver_enabled | bool + tags: [honcho, deriver] + +- name: Verify Honcho is reachable + ansible.builtin.import_tasks: verify.yml + tags: [honcho, verify] diff --git a/ansible/roles/honcho/tasks/network.yml b/ansible/roles/honcho/tasks/network.yml new file mode 100644 index 0000000..9cb9b55 --- /dev/null +++ b/ansible/roles/honcho/tasks/network.yml @@ -0,0 +1,17 @@ +--- +# ============================================================================ +# Podman network — single user-defined network shared by api/deriver/postgres +# ============================================================================ + +- name: Check if honcho network exists + ansible.builtin.command: + cmd: "podman network exists {{ honcho_network_name }}" + register: honcho_network_check + failed_when: false + changed_when: false + +- name: Create honcho podman network + ansible.builtin.command: + cmd: "podman network create {{ honcho_network_name }}" + when: honcho_network_check.rc != 0 + changed_when: true diff --git a/ansible/roles/honcho/tasks/podman.yml b/ansible/roles/honcho/tasks/podman.yml new file mode 100644 index 0000000..b77fdfe --- /dev/null +++ b/ansible/roles/honcho/tasks/podman.yml @@ -0,0 +1,24 @@ +--- +# ============================================================================ +# Podman + Quadlet prerequisites +# ---------------------------------------------------------------------------- +# Mirrors the pattern from the semaphore role. Ubuntu 24.04's podman is new +# enough that Quadlet ships out of the box (>= 4.4). +# ============================================================================ + +- name: Ensure Podman is installed + ansible.builtin.package: + name: + - podman + - podman-compose + state: present + become: true + +- name: Ensure Quadlet drop-in directory exists + ansible.builtin.file: + path: "{{ honcho_quadlet_dir }}" + state: directory + owner: root + group: root + mode: "0755" + become: true diff --git a/ansible/roles/honcho/tasks/postgres.yml b/ansible/roles/honcho/tasks/postgres.yml new file mode 100644 index 0000000..26f9643 --- /dev/null +++ b/ansible/roles/honcho/tasks/postgres.yml @@ -0,0 +1,48 @@ +--- +# ============================================================================ +# pgvector PostgreSQL container (Quadlet) +# ---------------------------------------------------------------------------- +# Honcho stores embeddings in pgvector, so we use the pgvector-enabled +# image rather than stock postgres. Same env-var surface as the upstream +# image; the pgvector extension is created by Honcho's migration script +# on first start (scripts/provision_db.py). +# ============================================================================ + +- name: Deploy Honcho PostgreSQL Quadlet + ansible.builtin.template: + src: honcho-postgres.container.j2 + dest: "{{ honcho_quadlet_dir }}/{{ honcho_postgres_container_name }}.container" + owner: root + group: root + mode: "0644" + register: postgres_quadlet + +- name: Reload systemd to pick up Quadlet changes + ansible.builtin.systemd: + daemon_reload: true + when: postgres_quadlet.changed + +- name: Ensure Honcho PostgreSQL container is started and enabled + ansible.builtin.systemd: + name: "{{ honcho_postgres_container_name }}.service" + state: started + enabled: true + +- name: Wait for PostgreSQL to accept connections + ansible.builtin.command: + cmd: >- + podman exec {{ honcho_postgres_container_name }} + pg_isready -U {{ honcho_db_user }} -d {{ honcho_db_name }} + register: pg_ready + until: pg_ready.rc == 0 + retries: 30 + delay: 2 + changed_when: false + +# Quadlet does not auto-restart on .container changes — same pitfall as +# semaphore role. Force restart only when the template was modified. +- name: Restart Honcho PostgreSQL on Quadlet change + ansible.builtin.systemd: + name: "{{ honcho_postgres_container_name }}.service" + state: restarted + when: postgres_quadlet.changed diff --git a/ansible/roles/honcho/tasks/verify.yml b/ansible/roles/honcho/tasks/verify.yml new file mode 100644 index 0000000..6948aea --- /dev/null +++ b/ansible/roles/honcho/tasks/verify.yml @@ -0,0 +1,27 @@ +--- +# ============================================================================ +# Post-deploy verification +# ---------------------------------------------------------------------------- +# Polls the local Honcho API port until it responds. Fails loudly if the +# service doesn't come up — the operator should not get an "all green" +# playbook result while Honcho is silently broken. +# ============================================================================ + +- name: Wait for Honcho HTTP endpoint + ansible.builtin.uri: + url: "http://127.0.0.1:{{ honcho_listen_port }}/docs" + status_code: [200, 307] + return_content: false + register: honcho_ping + until: honcho_ping.status in [200, 307] + retries: "{{ honcho_health_check_retries }}" + delay: "{{ honcho_health_check_delay }}" + +- name: Report Honcho status + ansible.builtin.debug: + msg: >- + Honcho API reachable on http://127.0.0.1:{{ honcho_listen_port }}/docs. + Traefik should now route hall-of-presidents.local.mk-labs.cloud to + this backend. Deriver enabled: {{ honcho_deriver_enabled }}; + LLM transport: {{ honcho_llm_transport }}; + deriver model: {{ honcho_deriver_model }}. diff --git a/ansible/roles/honcho/tasks/volumes.yml b/ansible/roles/honcho/tasks/volumes.yml new file mode 100644 index 0000000..014b8ff --- /dev/null +++ b/ansible/roles/honcho/tasks/volumes.yml @@ -0,0 +1,17 @@ +--- +# ============================================================================ +# Named podman volumes +# ---------------------------------------------------------------------------- +# Honcho itself is stateless; only postgres needs durable storage. +# ============================================================================ + +- name: Ensure named volumes exist + ansible.builtin.command: + cmd: "podman volume create {{ item }}" + register: volume_create + changed_when: "'already exists' not in (volume_create.stderr | default(''))" + failed_when: + - volume_create.rc != 0 + - "'already exists' not in (volume_create.stderr | default(''))" + loop: + - "{{ honcho_postgres_volume }}" diff --git a/ansible/roles/honcho/templates/honcho-api.container.j2 b/ansible/roles/honcho/templates/honcho-api.container.j2 new file mode 100644 index 0000000..2f8bedb --- /dev/null +++ b/ansible/roles/honcho/templates/honcho-api.container.j2 @@ -0,0 +1,55 @@ +# {{ ansible_managed }} +# mk-labs Honcho API service (FastAPI on :8000) +# Managed by Ansible - honcho role + +[Unit] +Description=Honcho API +After=network-online.target {{ honcho_postgres_container_name }}.service +Wants=network-online.target +Requires={{ honcho_postgres_container_name }}.service + +[Container] +Image={{ honcho_image }} +ContainerName={{ honcho_container_name }} +Network={{ honcho_network_name }} +PublishPort={{ honcho_listen_address }}:{{ honcho_listen_port }}:8000 + +# Run the entrypoint that performs DB migration before starting FastAPI. +Exec=/app/docker/entrypoint.sh + +# -- Database --------------------------------------------------------------- +Environment=DB_CONNECTION_URI=postgresql+psycopg://{{ honcho_db_user }}:{{ honcho_db_password }}@{{ honcho_postgres_container_name }}:5432/{{ honcho_db_name }} + +# -- Auth ------------------------------------------------------------------- +Environment=AUTH_USE_AUTH={{ honcho_auth_enabled | string | lower }} +Environment=AUTH_JWT_SECRET={{ honcho_jwt_secret }} + +# -- LLM provider (deriver / summary / dialectic) --------------------------- +{% if honcho_llm_transport == 'anthropic' %} +Environment=LLM_ANTHROPIC_API_KEY={{ honcho_anthropic_api_key }} +{% endif %} +Environment=DERIVER_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }} +Environment=DERIVER_MODEL_CONFIG__MODEL={{ honcho_deriver_model }} +Environment=SUMMARY_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }} +Environment=SUMMARY_MODEL_CONFIG__MODEL={{ honcho_summary_model }} +Environment=DIALECTIC_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }} +Environment=DIALECTIC_MODEL_CONFIG__MODEL={{ honcho_dialectic_model }} + +# -- Vector store ----------------------------------------------------------- +Environment=VECTOR_STORE_TYPE=pgvector + +# -- Public-facing URL (empty -> relative URLs, host-agnostic) -------------- +{% if honcho_web_url | length > 0 %} +Environment=HONCHO_PUBLIC_URL={{ honcho_web_url }} +{% endif %} + +# Timezone matches host baseline +Environment=TZ=America/Chicago +Environment=LOG_LEVEL=INFO + +[Service] +Restart=always +TimeoutStartSec=180 + +[Install] +WantedBy=multi-user.target default.target diff --git a/ansible/roles/honcho/templates/honcho-deriver.container.j2 b/ansible/roles/honcho/templates/honcho-deriver.container.j2 new file mode 100644 index 0000000..c3259a4 --- /dev/null +++ b/ansible/roles/honcho/templates/honcho-deriver.container.j2 @@ -0,0 +1,58 @@ +# {{ ansible_managed }} +# mk-labs Honcho deriver worker (background queue consumer) +# Managed by Ansible - honcho role +# +# Same image as the API; different command. The deriver pulls jobs off +# the DB-backed queue and calls the LLM provider to perform theory-of-mind +# derivations against user sessions. + +[Unit] +Description=Honcho deriver worker +After=network-online.target {{ honcho_postgres_container_name }}.service {{ honcho_container_name }}.service +Wants=network-online.target +Requires={{ honcho_postgres_container_name }}.service + +[Container] +Image={{ honcho_image }} +ContainerName={{ honcho_deriver_container_name }} +Network={{ honcho_network_name }} + +# Run the deriver script instead of the FastAPI server. The deriver +# polls the queue and processes work in a loop. Number of concurrent +# workers controlled via DERIVER_WORKERS. +Exec=/app/.venv/bin/python /app/src/deriver/deriver.py + +# -- Database --------------------------------------------------------------- +Environment=DB_CONNECTION_URI=postgresql+psycopg://{{ honcho_db_user }}:{{ honcho_db_password }}@{{ honcho_postgres_container_name }}:5432/{{ honcho_db_name }} + +# -- Auth ------------------------------------------------------------------- +# Deriver does not serve HTTP, but it reads the same config object — keep +# secrets aligned with the API container. +Environment=AUTH_USE_AUTH={{ honcho_auth_enabled | string | lower }} +Environment=AUTH_JWT_SECRET={{ honcho_jwt_secret }} + +# -- LLM provider ----------------------------------------------------------- +{% if honcho_llm_transport == 'anthropic' %} +Environment=LLM_ANTHROPIC_API_KEY={{ honcho_anthropic_api_key }} +{% endif %} +Environment=DERIVER_ENABLED={{ honcho_deriver_enabled | string | lower }} +Environment=DERIVER_WORKERS={{ honcho_deriver_workers }} +Environment=DERIVER_POLLING_SLEEP_INTERVAL_SECONDS={{ honcho_deriver_polling_seconds }} +Environment=DERIVER_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }} +Environment=DERIVER_MODEL_CONFIG__MODEL={{ honcho_deriver_model }} +Environment=SUMMARY_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }} +Environment=SUMMARY_MODEL_CONFIG__MODEL={{ honcho_summary_model }} + +# -- Vector store ----------------------------------------------------------- +Environment=VECTOR_STORE_TYPE=pgvector + +# Timezone matches host baseline +Environment=TZ=America/Chicago +Environment=LOG_LEVEL=INFO + +[Service] +Restart=always +TimeoutStartSec=180 + +[Install] +WantedBy=multi-user.target default.target diff --git a/ansible/roles/honcho/templates/honcho-postgres.container.j2 b/ansible/roles/honcho/templates/honcho-postgres.container.j2 new file mode 100644 index 0000000..4625a3d --- /dev/null +++ b/ansible/roles/honcho/templates/honcho-postgres.container.j2 @@ -0,0 +1,35 @@ +# {{ ansible_managed }} +# mk-labs pgvector-enabled PostgreSQL backing store for Honcho +# Managed by Ansible - honcho role + +[Unit] +Description=PostgreSQL (pgvector) for Honcho +After=network-online.target +Wants=network-online.target + +[Container] +Image={{ honcho_postgres_image }} +ContainerName={{ honcho_postgres_container_name }} +Network={{ honcho_network_name }} + +# Persistent data on a named volume. :Z relabels for SELinux; harmless on +# Ubuntu's ext4/apparmor stack and portable to any future host. +Volume={{ honcho_postgres_volume }}:/var/lib/postgresql/data:Z + +Environment=POSTGRES_USER={{ honcho_db_user }} +Environment=POSTGRES_DB={{ honcho_db_name }} +Environment=POSTGRES_PASSWORD={{ honcho_db_password }} +Environment=POSTGRES_INITDB_ARGS={{ honcho_postgres_initdb_args }} + +# Healthcheck — systemd doesn't act on this, but it's useful diagnostically +HealthCmd=pg_isready -U {{ honcho_db_user }} -d {{ honcho_db_name }} +HealthInterval=10s +HealthTimeout=5s +HealthRetries=3 + +[Service] +Restart=always +TimeoutStartSec=120 + +[Install] +WantedBy=multi-user.target default.target diff --git a/boilerplates/traefik/dynamic/honcho.yml b/boilerplates/traefik/dynamic/honcho.yml new file mode 100644 index 0000000..bbb8b61 --- /dev/null +++ b/boilerplates/traefik/dynamic/honcho.yml @@ -0,0 +1,30 @@ +# {{ ansible_managed }} +# Traefik dynamic config for Honcho on lincoln. +# Routes hall-of-presidents.local.mk-labs.cloud -> lincoln:8000. +# +# Honcho is normally an east-west API (JARVIS talks to it directly), but +# the operator wants browser access to the Swagger /docs UI, so the API +# is exposed via Traefik. There is no separate dashboard endpoint — +# /docs is the dashboard. +# +# No service-name alias: per the mk-labs east-west / north-south rule, +# only the human-facing Traefik route gets a thematic name. JARVIS +# accesses Honcho directly at http://lincoln:8000. + +http: + routers: + honcho: + rule: "Host(`hall-of-presidents.local.mk-labs.cloud`)" + entryPoints: + - websecure + service: honcho + tls: + certResolver: cloudflare + middlewares: + - security-headers + + services: + honcho: + loadBalancer: + servers: + - url: "http://lincoln.local.mk-labs.cloud:8000"