feat(honcho): add role + day1 playbook + traefik route for lincoln
Deploys Honcho (plastic-labs/honcho) as a rootful Podman + Quadlet service on the lincoln VM (10.1.71.132). Three containers on a user-defined network: - honcho-postgres pgvector/pgvector:pg16 - honcho-api FastAPI on :8000 - honcho-deriver background worker for theory-of-mind derivations LLM provider: Anthropic Claude (claude-sonnet-4-5). Switching providers is two env-var changes — see README. Traefik route hall-of-presidents.local.mk-labs.cloud -> lincoln:8000 added under boilerplates/traefik/dynamic/. JARVIS itself talks to Honcho directly at lincoln:8000 (east-west); the Traefik alias exists only for browser access to the Swagger /docs UI. Requires three new vault entries before first run: - vault_honcho_database_password - vault_honcho_jwt_secret - vault_honcho_anthropic_api_key
This commit is contained in:
@@ -72,6 +72,14 @@ hermes_server:
|
|||||||
ansible_host: 10.1.71.131
|
ansible_host: 10.1.71.131
|
||||||
ansible_user: wed
|
ansible_user: wed
|
||||||
ansible_become: true
|
ansible_become: true
|
||||||
|
|
||||||
|
honcho_server:
|
||||||
|
hosts:
|
||||||
|
lincoln:
|
||||||
|
ansible_host: 10.1.71.132
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
papermc_server:
|
papermc_server:
|
||||||
# ansible-galaxy role install engonzal.papermc
|
# ansible-galaxy role install engonzal.papermc
|
||||||
hosts:
|
hosts:
|
||||||
|
|||||||
17
ansible/playbooks/day1_deploy_honcho.yml
Normal file
17
ansible/playbooks/day1_deploy_honcho.yml
Normal file
@@ -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
|
||||||
85
ansible/roles/honcho/README.md
Normal file
85
ansible/roles/honcho/README.md
Normal file
@@ -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: "<strong random>"
|
||||||
|
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.
|
||||||
109
ansible/roles/honcho/defaults/main.yml
Normal file
109
ansible/roles/honcho/defaults/main.yml
Normal file
@@ -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
|
||||||
27
ansible/roles/honcho/meta/main.yml
Normal file
27
ansible/roles/honcho/meta/main.yml
Normal file
@@ -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: []
|
||||||
34
ansible/roles/honcho/tasks/api.yml
Normal file
34
ansible/roles/honcho/tasks/api.yml
Normal file
@@ -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
|
||||||
34
ansible/roles/honcho/tasks/deriver.yml
Normal file
34
ansible/roles/honcho/tasks/deriver.yml
Normal file
@@ -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
|
||||||
42
ansible/roles/honcho/tasks/main.yml
Normal file
42
ansible/roles/honcho/tasks/main.yml
Normal file
@@ -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]
|
||||||
17
ansible/roles/honcho/tasks/network.yml
Normal file
17
ansible/roles/honcho/tasks/network.yml
Normal file
@@ -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
|
||||||
24
ansible/roles/honcho/tasks/podman.yml
Normal file
24
ansible/roles/honcho/tasks/podman.yml
Normal file
@@ -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
|
||||||
48
ansible/roles/honcho/tasks/postgres.yml
Normal file
48
ansible/roles/honcho/tasks/postgres.yml
Normal file
@@ -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
|
||||||
27
ansible/roles/honcho/tasks/verify.yml
Normal file
27
ansible/roles/honcho/tasks/verify.yml
Normal file
@@ -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 }}.
|
||||||
17
ansible/roles/honcho/tasks/volumes.yml
Normal file
17
ansible/roles/honcho/tasks/volumes.yml
Normal file
@@ -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 }}"
|
||||||
55
ansible/roles/honcho/templates/honcho-api.container.j2
Normal file
55
ansible/roles/honcho/templates/honcho-api.container.j2
Normal file
@@ -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
|
||||||
58
ansible/roles/honcho/templates/honcho-deriver.container.j2
Normal file
58
ansible/roles/honcho/templates/honcho-deriver.container.j2
Normal file
@@ -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
|
||||||
35
ansible/roles/honcho/templates/honcho-postgres.container.j2
Normal file
35
ansible/roles/honcho/templates/honcho-postgres.container.j2
Normal file
@@ -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
|
||||||
30
boilerplates/traefik/dynamic/honcho.yml
Normal file
30
boilerplates/traefik/dynamic/honcho.yml
Normal file
@@ -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"
|
||||||
Reference in New Issue
Block a user