Lifehouse — System Architecture
Version: 0.3.0
Last updated: 2026-02-20
Status: Prototype / Active Development
Overview
Lifehouse is a self-hosted community organising platform built around two distinct sub-projects: Fieldwork and Workbench. Both share a central PostgreSQL database on a dedicated server (CT 604) but are otherwise fully independent — separate services, separate networks, separate LLMs.
The guiding principle throughout is: own the infrastructure, own the data, keep it simple enough to explain to anyone.
Sub-Projects
Fieldwork
Door-knocking data capture pipeline. Volunteers use Telegram voice notes on doorsteps. The system silently handles transcription and structured data extraction. From the volunteer's point of view: talk, send, done.
Purpose: Build a live, searchable picture of community interest, skills, resources, and concerns — without forms, apps, or friction on the doorstep.
Data captured per conversation:
- Address (raw spoken → normalised)
- Contact outcome (interested, not home, wants info, etc.)
- Skills the person mentioned
- Resources they have (van, space, tools, etc.)
- Concerns and issues raised
- Volunteer who captured it
Future use: Sortition (Phase 2) — stratified random selection of assembly participants from the consented population pool.
Workbench
LLM-based curation of spreadsheet and structured data. Separate pipeline, separate LLM, separate network. Not covered in detail here.
Infrastructure
Hosts
| Host | Role | Access |
|---|---|---|
| Vera | Local Docker host. Runs all containerised services. | Local / WireGuard 10.200.0.2 |
| Helsinki 2 | Proxmox hypervisor. Contains all CTs. | SSH helsinki2 → 65.108.159.249 (root) |
CT 604 lifehouse-db | Dedicated Postgres 15 server for all Lifehouse data. | 192.168.100.250:5432 |
Network Routing
Vera reaches CT 604 via WireGuard → Helsinki 2 → vmbr1 (Proxmox internal bridge).
Routing is symmetric — no NAT/MASQUERADE on this path.
Helsinki 2 iptables rules (persisted in /etc/iptables/rules.v4):
FORWARD:wg0 ↔ vmbr1(192.168.100.0/24) — allows packets in both directions
CT 604 static route (persisted in /etc/network/interfaces):
10.200.0.0/24 via 192.168.100.1— return path for WireGuard clients
Why no MASQUERADE? MASQUERADE caused asymmetric routing: SYNs arrived at CT 604 from
192.168.100.1(masqueraded) but SYN-ACKs returned directly to10.200.0.2via the static route, causing Vera's TCP stack to reject them. Removing MASQUERADE and adding the static route on CT 604 makes the full conversation symmetric:10.200.0.2 ↔ 192.168.100.250end-to-end.
Fieldwork Pipeline
Volunteer (Telegram voice note)
↓
Telegram Bot [NOT YET BUILT]
↓
ingestion-api (Vera, port 8015, forge-net + n8n_default)
↓
stt-service (Vera, GPU, Whisper large-v3 float16)
↓
fieldwork-extractor (CT 604, systemd service, Ollama llama3 on Vera)
↓
lifehouse DB (CT 604, schema: fieldwork)
Services
| Service | Image / Stack | Networks | Status |
|---|---|---|---|
ingestion-api | Custom Python/FastAPI | forge-net, n8n_default | ✅ Running |
stt-service | Custom Python/faster-whisper | forge-net, n8n_default | ✅ Running |
fieldwork-extractor | Python/FastAPI, systemd on CT 604 | localhost (CT 604) | ✅ Running |
| Telegram bot | TBD | — | ❌ Not built |
ingestion-api
- Receives audio uploads (multipart/form-data)
- Calls
stt-servicefor transcription - Persists every job to
fieldwork.transcription_jobson CT 604 - Endpoints:
POST /audio(sync),POST /audio/async,GET /status/{job_id},GET /health - DB degrades gracefully if CT 604 is unreachable
stt-service
- faster-whisper large-v3, float16
- GPU-enabled (NVIDIA runtime)
- Lazy model loading on first request
- Endpoint:
POST /transcribe,GET /health
fieldwork-extractor
- Runs as a systemd service directly on CT 604 (
/root/fieldwork-extractor/, Python venv) - Polls
fieldwork.transcription_jobsfor completed jobs not yet extracted - Locks each job (
error = 'extracting') before calling Ollama to prevent duplicate extraction on retry - Calls
workbench-ollamaon Vera (http://10.200.0.2:11434, modelllama3:latest) with a structured extraction prompt - Writes results to
fieldwork.doorstep_contacts,fieldwork.people,fieldwork.households,fieldwork.skills,fieldwork.resources,fieldwork.concerns - Clears the lock and sets
doorstep_contact_idon success; sets a descriptive error on failure - Source:
/home/southville-forge/fieldwork-extractor/main.py(Vera), deployed to/root/fieldwork-extractor/main.py(CT 604) - Ollama timeout: 300s (llama3 cold-load can take ~90s)
Database — CT 604 lifehouse
Host: 192.168.100.250:5432
Database: lifehouse
Schema: fieldwork
Tables
| Table | Owner | Purpose |
|---|---|---|
transcription_jobs | ingestion-api | Raw STT pipeline tracking. One row per audio file. |
doorstep_contacts | fieldwork-extractor | Structured extraction per doorstep conversation. |
people | fieldwork-extractor | Individuals. Identifiable fields nullable (GDPR). |
households | fieldwork-extractor | Physical addresses. Deduplication unit. |
locations | fieldwork-extractor | PostGIS geometry, postcode, ward. |
demographics | fieldwork-extractor | Sortition stratification fields. Separate from identifiable data. |
organisations | fieldwork-extractor | Groups and orgs encountered in fieldwork. |
skills | fieldwork-extractor | LLM-extracted skills per person. |
resources | fieldwork-extractor | LLM-extracted resources per person. |
concerns | fieldwork-extractor | Issues raised. For agenda building and driver mapping. |
sortition_pool | View | Consented people + demographics. Ready for Phase 2 sortition engine. |
DB Users
| User | Access | Notes |
|---|---|---|
fieldwork_api | SELECT, INSERT, UPDATE on all fieldwork.* tables | Used by ingestion-api and fieldwork-extractor. Change default password before real data. |
postgres | Superuser | Admin only. Not used by application services. |
Schema file
/home/southville-forge/Southville-Forge-System/Sites/Life House/schema/fieldwork_schema.sql
/root/lifehouse/schema/fieldwork_schema.sql (Helsinki 2 copy)
Workbench Pipeline
Separate from Fieldwork. Uses classifier-llm (SGLang/Qwen2.5-3B) and classifier-api.
Do not share LLM services between Fieldwork and Workbench.
| Service | Image | Networks | Status |
|---|---|---|---|
classifier-llm | lmsysorg/sglang | classifier-net | ✅ Running |
classifier-api | Custom Python/FastAPI | classifier-net | ✅ Running (stub) |
workbench-ollama | ollama/ollama | workbench_default | ✅ Running |
workbench-api | Custom | workbench_default | ✅ Running |
workbench-postgres | postgres | workbench_default | ✅ Running |
Phase 2 — Sortition (Future)
Deterministic sortition engine. Not AI-driven. Rules and random seed are fixed and auditable.
- Load consented population from
fieldwork.sortition_poolview - Apply eligibility filters (consent, cooldown windows)
- Stratify by demographic bands (age, gender, area, housing tenure, etc.)
- Random selection within strata using a recorded seed (Python/NumPy RNG)
- All parameters and results persisted for audit
The LLM role in Phase 2 is supportive only: clean messy inputs, explain selections in plain language. It never selects people or adjusts weights.
What's Not Built Yet
| Component | Priority | Notes |
|---|---|---|
fieldwork-extractor | ✅ Done | Running on CT 604 as systemd service |
| Telegram bot | High | Receives voice notes, forwards to ingestion-api |
| Address normalisation | Medium | Postcode lookup / geocoding for locations table |
| Consent web form | Medium | Simple self-hosted form for Phase 2 readiness |
| Sortition engine | Low | Phase 2 — after population pool is populated |
| Map visualisation | Low | Leaflet + PostGIS — after data exists |
Key Credentials (Vera)
⚠️ Change
fieldwork_apiDB password before capturing real data.
- lifehouse DB:
fieldwork_api/fw_api_changeme_2026→192.168.100.250:5432/lifehouse - forge postgres:
forge/forgepass→postgres:5432/forge_db - n8n:
https://vera-n8n.southvilleforge.com—admin/admin-132a1e24cb42 - HF_TOKEN: stored in
/home/southville-forge/southville-stack/.env
Repository / Version Control
No Git repository set up yet. Not required until there is a deployment target (website, public API, etc.). All source lives on Vera at /home/southville-forge/.
When Git is added, suggested structure:
lifehouse/
├── ingestion/ # ingestion-api
├── stt/ # stt-service
├── fieldwork-extractor/
├── workbench/
├── schema/ # SQL migration files
└── docs/ # Architecture and runbooks