SWEN - Secure Wallet & Expense Navigator¶
Self-hosted personal accounting with German FinTS bank sync and ML classification.
SWEN is a privacy-first personal finance application you run on your own hardware. It is based on double-entry bookkeeping and unfolds its full power when automating bank imports with FinTS/HBCI. No cloud, no telemetry, no third party ever sees your bank data.
Features¶
-
FinTS / HBCI Bank Sync
Connect directly to your German bank via the standardised FinTS protocol. Transactions are imported automatically with robust duplicate detection.
-
Double-Entry Bookkeeping
Every transaction is recorded as a proper journal entry. Full audit trail, no "lost" money. Define your own chart of accounts or start from a template.
-
ML Classification
A four-tier ML pipeline (IBAN anchor → embedding similarity → keyword patterns → fallback) automatically assigns counter-accounts. Learns from your corrections.
-
Fully Self-Hosted
Ships as a single
docker compose upcommand. All data stays on your machine — no SaaS subscription, no data sharing, full transparency with open source.
Get Started in 5 Minutes¶
Save the following as docker-compose.yml:
services:
postgres:
image: postgres:18-alpine
container_name: swen-postgres
restart: unless-stopped
env_file:
- ./config/.env
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- swenetwork
backend:
image: maltewin/swen-backend:latest
container_name: swen-backend
restart: unless-stopped
volumes:
- ./config/.env:/app/config/.env:ro
depends_on:
postgres:
condition: service_healthy
ml:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
- swenetwork
ml:
image: maltewin/swen-ml:latest
container_name: swen-ml
restart: unless-stopped
volumes:
- ./config/.env:/app/config/.env:ro
- ml-model-cache:/app/data/cache
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://localhost:8100/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 300s # covers first-run model download (~1.5 GB)
networks:
- swenetwork
frontend:
image: maltewin/swen-frontend:latest
container_name: swen-frontend
restart: unless-stopped
ports:
- "3000:3000"
depends_on:
backend:
condition: service_healthy
networks:
- swenetwork
searxng:
image: searxng/searxng:latest
container_name: swen-searxng
restart: unless-stopped
networks:
- swenetwork
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
swenetwork:
driver: bridge
volumes:
postgres-data:
driver: local
ml-model-cache:
driver: local
Then pull or build the images, run the interactive setup wizard to generate config/.env, and start:
docker compose pull # or: docker compose build
mkdir -p config
docker run --rm -it \
-v ./config:/app/config \
maltewin/swen-backend:latest \
swen setup
docker compose up -d
The wizard will ask for the environment (choose Production), registration mode (admin-only or free), and optional SMTP. Then it writes config/.env automatically. The webapp can be reached on port 3000. The use a reverse proxy is highly recommended!. First registered user becomes admin.
Architecture at a Glance¶
graph LR
Browser["Browser<br>(React)"] --> nginx["nginx<br>:3000"]
nginx --> backend["Backend<br>(FastAPI :8000)"]
nginx --> backend
backend --> postgres[("PostgreSQL")]
backend --> ml["ML Service<br>(FastAPI :8100)"]
ml --> searxng["SearXNG<br>(internal)"]
All services run in the same Docker network such that only port 3000 is exposed to the host.
Status¶
SWEN is at v0.1. It is a personal project, actively used and maintained. Key known limitations:
- Only the decoupled app TAN method (App based 2FA) is functional all banks
- Not all banks are tested yet.
- Frontend is largely AI-generated (React/TypeScript), pending thorough review
See the GitHub Issues for the current backlog.