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
Description
No description provided
Readme 1.7 MiB
Languages
Go 60.3%
Svelte 20.3%
Dart 11.1%
TypeScript 5%
PLpgSQL 1.1%
Other 2.1%