Architecture table said Redis, but local dev uses Valkey and prod uses Dragonfly. Both speak the Redis protocol but neither is Redis.
5.8 KiB
Marktvogt
The central portal for medieval markets ("Mittelaltermaerkte") in the DACH region.
Marktvogt connects visitors, merchants, artists, camp groups, and organizers on one platform — replacing the patchwork of Facebook groups, phone calls, mailing lists, and scattered websites that the scene currently runs on.
The name comes from the historical Marktvogt — the keeper of order at medieval markets. Same job, different century.
The Problem
The medieval-market scene in the DACH region is large (hundreds of markets per year) but digitally fragmented:
- Finding markets: Facebook groups,
mittelalterkalender.info, private sites - Bookings (merchants, artists, camps): phone, e-mail, Facebook Messenger
- Camp groups: organize almost exclusively on Facebook
- Tickets: usually box-office-only or one-off organizer sites
There is no single portal that brings all participants together. Marktvogt is that portal.
The Solution
A two-sided marketplace:
- Supply side — organizers list markets, open plots, programs, tickets
- Demand side — visitors discover markets and buy tickets; merchants, artists, and camp groups apply for spots
Network effects: organizers attract applicants, applicants make the platform indispensable for organizers, and visitors follow the content.
Roles
Six roles, with the Veranstalter (organizer) as the central actor:
| Role | Description |
|---|---|
| Gast | Anonymous visitor — browse markets, no account |
| User | Registered visitor — tickets, favorites, profile |
| Haendler | Merchant group applying for plots |
| Kuenstler | Artist group applying for performance slots |
| Lager | Reenactment camp group applying for camp space |
| Veranstalter | Organizer — creates markets, reviews applications, sells tickets |
All applicants (Haendler / Kuenstler / Lager) operate as groups. A solo merchant is just a one-person group. Mitarbeiter (staff) is a sub-role under Veranstalter with granular permissions.
Architecture
Monorepo. Components are plain directories, not git submodules.
| Layer | Stack |
|---|---|
| Backend | Go REST API + WebSocket; PostgreSQL (PostGIS), Valkey/Dragonfly, S3 |
| Web | SvelteKit + Tailwind 4, SSR for SEO, runs on Bun |
| Mobile | Flutter (Android + iOS) |
| Auth | Custom (Go), e-mail+password / magic link / OAuth / 2FA |
| Payments | Stripe Connect |
| LLM | Google Gemini (gemini-2.5-flash-lite) for enrichment |
| CI/CD | Woodpecker (ci.somegit.dev) — .gitlab-ci.yml retained as fallback |
| Hosting | Kubernetes (itsh.dev), Helm chart at helm/marktvogt/ |
| Monitoring | Prometheus, Loki, Grafana, Sentry |
Repo Layout
backend/ Go REST API + WebSocket chat
web/ SvelteKit frontend (SSR, Bun runtime)
app/ Flutter mobile app
helm/ Monolithic Helm chart (backend + web + Postgres + Dragonfly)
planning/ Vision, roadmap, MVP scope, feature specs (German, ASCII-only)
scripts/ Out-of-band tooling (e.g. K8s secret sync)
.woodpecker/ CI pipelines for somegit.dev
The admin dashboard lives in its own repo and will be added later.
Getting Started
The repo is driven by a Justfile. Run just for the full list.
# One-time setup: pre-commit hooks, web deps, golangci-lint
just hooks-install
# Backend
just backend-dev # Postgres + Valkey via docker compose
just backend-run # go run ./cmd/api
just backend-test
just backend-lint
# Web
just web-dev # SvelteKit dev server
just web-check # svelte-check (types)
just web-lint
# Everything
just lint
just test
just fmt
For component-specific details, see backend/README.md, web/README.md, and
app/README.md.
Deployment
Single Helm release marktvogt in namespace tenant-2, deployed from
helm/marktvogt/ (monolithic chart covering backend, web, Postgres, and
Dragonfly). CI runs:
helm upgrade marktvogt --reuse-values \
--set-string backend.image.tag=<sha> \
--set-string web.image.tag=<sha>
--set-string is mandatory — all-digit short SHAs get float-coerced into
scientific notation otherwise, which then fails K8s label validation.
K8s Secrets are pre-created out-of-band by scripts/k8s-secrets-sync.sh
reading from .env.helm (gitignored). CI never touches secret values.
The container registry (registry.itsh.dev/vikingowl/marktvogt.de/{backend,web})
is Zot-backed and requires attestations on every pushed image. The
Woodpecker pipelines use woodpeckerci/plugin-docker-buildx, which handles
this by default.
Status
Active development as of 2026-04-29. backend/, web/, and app/ all contain
working code (Go API scaffolding + auth, SvelteKit pages, Flutter skeleton).
- Current phase scope:
planning/15-mvp.md - Phased feature plan:
planning/17-roadmap.md - Vision:
planning/00-vision.md
Planning docs are in German and use ASCII only (no umlauts) for cross-platform compatibility.
Conventions
- API design: REST for CRUD, WebSocket for real-time chat
- Auth: custom-built using Go libraries — no external auth provider
- Offline capability: required for QR-ticket validation and venue maps
- Commits: conventional commit messages
- Source of truth for product:
planning/— read it before adding features