An image board demonstrating Codemowers Cloud: direct browser uploads, searchable OCR, object detection, and asynchronous image processing on Kubernetes.
Demo: can-i-haz-kubernetes.codemowers.io
| Service | Language | Role |
|---|---|---|
| uploader | Go | Authorize direct S3 uploads, confirm posts, publish Kafka events |
| browse | Go | Board listing, thread view, comments |
| search | Rust / Rocket | Public title/OCR/tag search in Postgres |
| comments | Go | Post/delete comments |
| admin | Go | Authorize board administration and create/delete boards |
| tagger | Python/YOLO | Kafka consumer → auto-tag images via YOLOv8 |
| ocr | Python/Tesseract | Kafka consumer → extract caption text into image metadata |
| thumbnailer | Python/libjpeg-turbo | Kafka + on-demand JPEG thumbnails cached in S3 |
| exif | Go | Kafka consumer → extract EXIF camera, exposure, dimensions and GPS metadata |
| frontend | Next.js 14 | UI and OIDC session handling |
Traefik in Kubernetes and nginx in local development route
/api/{browse,search,comments,upload,admin,thumbnails} directly to the matching backend service
without rewriting the path. API traffic does not pass through the Next.js
process; only /api/auth belongs to the frontend.
Images live in S3-compatible storage (<namespace>-images in Kubernetes,
lolcatz-images in Compose). Browsers transfer image bytes directly using signed
S3 URLs; the public storage endpoint comes from operator-generated settings.
Metadata lives in PostgreSQL (CNPG), board-list caches in Dragonfly, and events
in Redpanda. NextAuth stores sessions, refresh tokens and ID tokens in its
encrypted HttpOnly cookie; only the access token is exposed to browser code.
Postgres enables pgvector for the optional InsightFace exercise and PostGIS for EXIF
locations. EXIF coordinates are stored as indexed geometry(PointZ, 4326)
for map and proximity queries.
docker compose up --buildOpen localhost:3000. Compose supplies a local development
identity (developer@localhost) with upload, comment, and board-admin access.
The MinIO console is at localhost:9001
(minioadmin / minioadmin). Browser uploads require minio.localhost to resolve
to 127.0.0.1; add it to /etc/hosts if needed.
Enable the optional YOLO worker with:
docker compose --profile ai up --buildReset disposable local data with docker compose down -v.
The Helm chart targets Codemowers Cloud. Inspect the target cluster's admission policies for platform defaults and requirements; keep those settings out of application configuration. Namespace lifecycle belongs to the platform.
Skaffold builds and pushes images using your local Docker credentials. Configure your own registry namespace for the context you use, for example:
skaffold config set \
--kube-context 'admin@ee-west-1.codemowers.io' \
default-repo zot.ee-west-1.codemowers.io/YOUR-NAMESPACE
docker login zot.ee-west-1.codemowers.io
skaffold dev --kube-context 'admin@ee-west-1.codemowers.io'The lolcatz deployment namespace needs a zot-pull-secret that can pull your
images. Open the application through its Ingress hostname. Skaffold port-forwards
are for debugging individual HTTPS services.
Chart values define image overrides and optional components.
CI tests the application and publishes
ghcr.io/codemowers/lolcatz-<service> images from main, tagged latest and with
the commit SHA. Version tags also publish release tags. The release-values
artifact pins image digests and records the source revision; use the chart from
that revision with those values. Public packages need no pull credentials;
private packages require imagePullSecrets.
Passmower is the single OIDC issuer. NextAuth owns authorization-code/PKCE login
and renewal, retaining refresh and ID tokens in its encrypted HttpOnly cookie.
The browser receives the access token and calls each API directly. APIs verify
the signature, issuer, expiry, and public-origin-plus-/api audience, then check
operation scopes and ownership.
| Operation | Required scope |
|---|---|
| List own uploads | lolcatz:images:read |
| Upload or delete own images | lolcatz:images:write |
| Post comments | lolcatz:comments:write |
| Manage boards | lolcatz:boards:write and github.com:codemowers:admins membership |
Browse and search are public. Login links the verified ID-token email and subject
to a local user; API requests resolve ownership from that subject. Client secrets
and long-lived storage credentials stay server-side. New upload clients use
/api/upload/presign followed by /api/upload/confirm.
Uploads publish keyed events to lolcatz-images. EXIF, OCR, YOLO, and thumbnail
workers use independent consumer groups and commit offsets after processing.
YOLO publishes replacement detections to lolcatz-tags; deletion tombstones
retire derived data. Image bytes stay in S3 and metadata in PostgreSQL.
To replay a worker after changing its model, stop its consumer group, rewind its Kafka offsets, and restore its previous replica count. Use the cluster's Kafka credentials and TLS trust. Bump the producer version to identify stale results; thumbnail output changes also require a new cache URL/storage version.
- Paws or Claws: real-time voting.
- Caption correction: build an LLM worker using OCR and YOLO output while preserving the original caption.
- InsightFace: consume person detections and implement private face embeddings and similarity search. This optional implementation is participant work and is excluded from default builds.
Dockerfiles live in services/ and use the repository root as their build context.
Go services share one module; search uses
Rust/Rocket. Compose provides the databases and dependencies
for integration tests:
docker compose up --build -d
docker compose run --build --rm frontend-tests
docker compose run --build --rm go-tests
docker compose run --build --rm search-tests
docker compose run --rm node-tools node test/integration.mjsFor frontend unit and browser checks, run npm ci, npm test,
npx playwright install chromium, and npm run test:browser from
services/frontend/. Python worker unit tests run with
PYTHONPATH=services python3 -m unittest discover -s services/tests after installing
the worker dependencies. See CI for the complete
check commands.
This is a recreatable demo. Services initialize a fresh schema; reset incompatible data when changing it.