A web application for uploading, viewing, and managing sticker locations across the world with automatic GPS extraction from images.
- Podman
- At least 4 GB of available RAM
- Ports 5432, 5555, 8080, 8181, and 8282 available
compose.yml builds all services from local source and imports the bundled Keycloak realm automatically:
podman compose up -dWait 30–60 seconds for all services to initialize.
Access the application:
| Service | URL |
|---|---|
| Application | https://localhost:8282 |
| API docs (Swagger) | http://localhost:5555/api/v1/docs |
| Keycloak Admin | https://localhost:8282/auth |
Stop:
podman compose downgraph TD
Browser["Browser\n(Angular + MapLibre GL)"]
Backend["Backend\n(FastAPI)"]
DB["PostgreSQL + PostGIS"]
KC["Keycloak"]
Browser -->|"REST / JSON + JWT"| Backend
Backend --> DB
Browser <-->|"OIDC"| KC
Backend -->|"JWKS key fetch"| KC
| Layer | Technology |
|---|---|
| Frontend | Angular 22 (zoneless), TypeScript 6.0, MapLibre GL 6.x |
| Backend | Python 3.14, FastAPI, Pydantic |
| Database | PostgreSQL with PostGIS (SRID 4326) |
| Authentication | Keycloak, JWT RS256 |
StickerMap uses Keycloak with four hierarchical client roles (scoped to the stickermap-client client):
| Role | Permissions |
|---|---|
sm-viewer |
View sticker details |
sm-uploader |
Upload stickers, edit own stickers |
sm-editor |
Edit any sticker's poster, date, and location |
sm-admin |
Full access — edit all fields, delete stickers |
Each role includes all permissions of the roles below it (composite roles).
Authentication flow:
sequenceDiagram
participant User
participant App as Angular App
participant KC as Keycloak
participant API as FastAPI
User->>App: Navigate to /add-sticker
App->>KC: Redirect (OIDC / silent SSO check)
KC->>App: JWT access token
App->>API: Request + Bearer token
API->>KC: Fetch JWKS public keys
API->>App: 200 OK / 401 / 403
For full Keycloak setup and manual role configuration see docs/authentication.md.
See the individual module READMEs for detailed setup:
- backend/README.md — Python / uv / FastAPI
- frontend/README.md — Angular / pnpm
- database_migrations/README.md — Alembic schema migrations
Backend quick setup
cd backend
uv sync
# Start dependencies
podman network create stickermap
podman run --name stickermap-postgis -d --network stickermap -p 5432:5432 \
-e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=stickermap \
docker.io/postgis/postgis:18-3.6
podman run --name stickermap-keycloak -d --network stickermap -p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.5 start-dev
cp .env.example .env
cd ../database_migrations && uv run alembic upgrade head && cd ../backend
uv run fastapi dev main.py --port 5555Backend: http://localhost:5555
A Helm chart is available at helm/stickermap/. It requires Helm v4 and has no chart dependencies — all templates are inlined (air-gap friendly).
The chart deploys the backend, frontend, and a one-shot migration Job. The PostgreSQL/PostGIS database and Keycloak are not included — bring your own and reference them via values.
See helm/README.md for mandatory values, configuration examples, and OCI packaging instructions.
See docs/troubleshooting.md for a full reference.
Common issues
Port conflicts — change port mappings in compose.yml.
Database connection errors:
podman ps | grep stickermap-postgis
podman compose restart backendKeycloak authentication fails:
podman ps | grep stickermap-keycloak
# Verify realm and client exist in the admin console
# Clear browser localStorage if stuck in redirect loopToken validation fails (401):
# Verify JWKS endpoint is reachable
curl http://localhost:8080/realms/stickermap/protocol/openid-connect/certs
# Check KEYCLOAK_URL in backend/.envImage upload fails — check file size (max 16 MB) and type (JPEG, PNG, GIF, WebP).
No GPS data extracted — use original camera photos; many platforms strip EXIF on upload.
Map not loading — check browser console for errors; verify MapLibre GL is loading and tile requests are succeeding in the network tab. A blank map with a 404 for maplibre-gl-worker.mjs means the MapLibre v6 web worker is not being served — see the MapLibre section in CLAUDE.md.
Enable verbose backend logging:
# backend/.env
LOG_LEVEL=DEBUG- Podman Documentation
- FastAPI Documentation
- Angular Documentation
- MapLibre GL Documentation
- Keycloak Documentation
- PostGIS Documentation
This codebase is developed with the assistance of Anthropic's Claude, primarily through Claude Code. Project-scoped configuration for Claude Code (shared permissions, subagents, slash commands, and hooks) lives under .claude/, and conventions Claude follows when working in this repo are documented in CLAUDE.md.
See LICENSE for details.