# Kirche Einfach AI-assisted administration platform for church organizations. Staff describe a request in plain words. The platform guides them through the process, checks the rules automatically, and asks a human only when something needs judgment. Every step is documented and traceable. ## Features - **Visual flow authoring** — design decision flows in Langflow, then bring them into the platform. - **Flows as files** — each flow lives as one JSON file under `backend/flows/`. The directory is the source of truth: committed to git, reviewable, diffable. - **Automatic sync** — the API watches `backend/flows/` and reconciles every change into the database and the runtime: new files become flows, edits become new versions, deleted files remove the flow (including its runs). Same on every startup; also triggerable via `POST /api/flows/sync` or `uv run python scripts/sync_flows.py --sync`. - **Versioned runtime** — every change creates an immutable version; versions deploy to the lightweight LFX runtime and can run synchronously or streamed. - **Approvals** — a flow can pause and ask a human for a decision. Approving can automatically start a follow-up ("continuation") flow. - **Access management** — public landing page (German) with an access-request form (request + approval). Approved users get a passwordless magic-link invite by email (SMTP via `.env`); sessions are HttpOnly cookies, tenants come from the session. First admin via `POST /api/auth/admin-bootstrap` (only on an empty user table, email must be in `ADMIN_EMAILS`). - **Dynamic forms** — flows declare form fields as typed inputs on a `CustomInput*` custom component (reusable library in `backend/components/`, mounted into Langflow via `LANGFLOW_COMPONENTS_PATH`). `GET /api/flows/{id}/input-schema` derives a JSON Schema + RJSF uiSchema from the deployed version; the webapp renders the matching form automatically. Flows without such a node fall back to a single textarea. See `backend/components/kirche/README.md` for authoring new form fields. - **Web UI** — landing page (*Start*), access request (*Zugang*), and the logged-in webapp: *Workflows* (choose + run with dynamic form), *Aufgaben* (approvals), *Verwaltung* (admin: access requests, import, sync, deploy). ## Future Ideas and planned features live in [ROADMAP.md](ROADMAP.md) (the single authoritative place for future implementation ideas). ## Infrastructure One `docker-compose.yml` at the repo root runs the whole stack — identical services in dev and prod. Dev-only needs (editor, SSH keys, autoreload, docker CLI) come from `.devcontainer/devcontainer.json`. ```mermaid flowchart LR subgraph Repo["Git repo — source of truth"] FLOWS["backend/flows/*.json
one Langflow export per flow"] end subgraph Stack["Compose stack (dev + prod)"] UI["frontend — React UI (3000)"] API["api — FastAPI (8000)
+ embedded flow-watcher"] DB[("app.db — SQLite file")] LF["langflow — authoring UI (7860)
langflow.db — SQLite file"] LFX["lfx — flow runtime (6004)"] WT["watchtower — nightly image updates (prod)"] end UI --> API FLOWS -->|watch + sync| API API -->|definitions · versions · runs · approvals| DB API -->|pull / export| LF API -->|upload · run · unregister| LFX LFX -->|approval callbacks| API WT -.->|updates :latest images| LF ``` | Service | Purpose | URL | | -------------- | ------------------------------------ | -------------------------- | | React frontend | Web UI (Vite, HMR in dev) | http://localhost:3000 | | FastAPI backend| API + flow orchestration (reload in dev) | http://localhost:8000 | | API docs | Swagger UI | http://localhost:8000/api/docs | | Langflow | Visual flow authoring | http://localhost:7860 | | LFX | Flow runtime (internal, no direct UI)| localhost:6004 | | SQLite files | `backend/data/kirche.db` (app), Langflow keeps its own file | — | Why SQLite: a flow run needs only a handful of single-row statements, and Langflow itself runs on SQLite by default — so the whole stack works without a database server. Postgres can return later by changing `DATABASE_URL` (migrations stay portable). `.env` holds only ubiquitous variables (identical value in dev and prod: secrets, service URLs). Dev-only variables travel via `devcontainer.json` (`APP_ENV=dev`); paths resolve from the repo root in code. LLM provider keys for Langflow are plain env vars (`OPENAI_API_KEY`, `OPENAI_BASE_URL`) — Langflow turns them into restart-proof global variables, LFX reads them at runtime. `LANGFLOW_SECRET_KEY` must be a valid Fernet key (see `.env.example`). ## Getting Started 1. Copy `.env.example` to `.env` and fill in the secrets. 2. Open the repo in VS Code → "Reopen in Container". Setup installs dependencies and syncs `backend/flows/`; the entrypoints migrate and serve automatically. 3. The stack is already running: UI at http://localhost:3000, API docs at http://localhost:8000/api/docs. ## Development Backend (attached `api` container, from `backend/`): - `uv run pytest` — full test suite (throwaway SQLite file, no external DB). - `uv run alembic upgrade head` — migrate the dev database. - `uv run python scripts/sync_flows.py --sync` — reconcile `backend/flows/`. - `uv run python scripts/sync_flows.py --pull` — export all Langflow flows into `backend/flows/` first. - `uv run python scripts/deploy_flow.py --langflow-id ` — import one Langflow flow via the API. - `uv run python scripts/make_flow_fixtures.py` — regenerate the bundled flows (writes test fixtures and `backend/flows/`). - `uv run python scripts/smoke_e2e.py` — manual end-to-end smoke. Frontend (node lives in the `frontend` container): - `docker compose exec frontend npm run dev` — HMR dev server (already running via compose; this is for a second instance or flags). - `docker compose exec frontend npm run build` — typecheck + build. - Without a docker socket: VS Code "Attach to Running Container" → frontend. Flow workflow: design in Langflow → press "Aus Langflow synchronisieren" in Verwaltung (manual full pass: Langflow → `backend/flows/.json` → DB + LFX deploy) → commit. File-only alternatives: `scripts/sync_flows.py --pull --sync`, UI download, or the single-flow import by Langflow ID. The watcher deploys file edits automatically. Deleting a file removes the flow including its runs (git keeps the JSON). ## Production (self-hosted — TLS/reverse proxy required, not included) 1. Check out the repo on the host, create `.env` from `.env.example` with real secrets (`openssl rand -hex 32`) and `FRONTEND_MODE=preview`. 2. Validate it: `uv run python scripts/check_prod.py --env ../.env` (run from `backend/`; blocks on weak secrets or dev modes). 3. `docker compose up -d --build`. 4. Code updates via `git pull` + `docker compose restart api frontend` (the checkout is bind-mounted); image rebuilds only for dependency changes (`docker compose up -d --build`). Internet exposure checklist (both forwarded ports sit behind your HTTPS proxy — nothing here substitutes for it): - Proxy: force HTTPS (+ HSTS), and put an extra login (basic-auth or OIDC) in front of Langflow `:7860`. Langflow has a history of critical unauthenticated RCEs — treat its UI as privileged tooling, never share flows publicly ("Shareable Playground" = RCE risk). - Secrets: all `*_KEY`/`*_TOKEN`/`*_PASSWORD` long and random; rotate after any incident or staff change; re-run `check_prod.py`. - Langflow/LFX upgrades (pinned in lockstep): bump `LANGFLOW_VERSION` (`.env`), `Dockerfile.lfx` ARG, and backend `lfx~=` together, then `lfx upgrade --upgrade-flow=safe` + `lfx validate --level 4` on `backend/flows/*.json`. Check Langflow security advisories monthly. - Backups: `backend/data/` (SQLite) and the `langflow-data` volume. Container updates: registry images (`langflowai/langflow:latest`) auto-update nightly via Watchtower (`nickfedor/watchtower`). Locally built images (`api`, `lfx`, `frontend`) update with `docker compose build --pull`. ## Continuous integration Workflows in `.forgejo/workflows/` run on Forgejo: backend pytest, frontend build, compose validation. They need a Runner with the `docker` label online (repo Settings → Actions → Runners) and action resolution for `actions/checkout` (github proxy or `code.forgejo.org` mirror).