# 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).
New workflow module? Step-by-step guide: `docs/new_workflow_module.md`.
Pull safety: sync matches Langflow flows to files **by name** and
overwrites silently — renaming or repurposing a flow in Langflow (or a
duplicate with the same name) lands in the matched file. Commit before
syncing and review the `pulled` report with `git diff`; uncommitted
work in `backend/flows/` can be overwritten.
## 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).