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