Deep dives into interesting problems I've solved. Each section covers the motivation and execution.
flowchart LR
subgraph Browser
UI[claude.jomcgi.dev]
end
subgraph Cluster
Web[Claude Code Web]
vLLM[vLLM Server]
SigNoz[(SigNoz MCP)]
K8s[(Kubernetes API)]
end
subgraph External
Claude[Claude API]
end
UI --> Web
Web -->|Complex tasks| Claude
Web -->|Code tasks| vLLM
Web -->|Logs & Traces| SigNoz
Web -->|Pod status| K8s
style UI fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Web fill:#ffa502,stroke:#ffa502,color:#fff
style vLLM fill:#ffd93d,stroke:#ffd93d,color:#000
style SigNoz fill:#6bcb77,stroke:#6bcb77,color:#fff
style K8s fill:#4d96ff,stroke:#4d96ff,color:#fff
style Claude fill:#9b59b6,stroke:#9b59b6,color:#fff flowchart LR
subgraph Capture
GoPro[GoPro Hero]
Queue[(SQLite)]
end
subgraph Process
EXIF[EXIF + GPS]
S3[(SeaweedFS)]
NATS[(NATS JetStream)]
end
subgraph Deliver
Proxy[imgproxy]
CDN[Cloudflare CDN]
end
subgraph Display
UI[trips.jomcgi.dev]
end
GoPro -->|27MP| Queue
Queue --> EXIF
EXIF -->|Images| S3
EXIF -->|Events| NATS
S3 --> Proxy
Proxy --> CDN
CDN --> UI
NATS -->|WebSocket| UI
style GoPro fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Queue fill:#ffa502,stroke:#ffa502,color:#fff
style EXIF fill:#ffd93d,stroke:#ffd93d,color:#000
style S3 fill:#6bcb77,stroke:#6bcb77,color:#fff
style NATS fill:#4d96ff,stroke:#4d96ff,color:#fff
style Proxy fill:#9b59b6,stroke:#9b59b6,color:#fff
style CDN fill:#e056fd,stroke:#e056fd,color:#fff
style UI fill:#ff6b6b,stroke:#ff6b6b,color:#fff The GoPro shoots on interval while driving. A Python controller manages the camera over WiFi, handling connection drops and queueing downloads for later.
Trip points are events in NATS JetStream. The API replays the stream on startup to rebuild state. No database needed—just an append-only log.
GoPro images are 27MB each. imgproxy generates thumbnails and display sizes on-the-fly. Cloudflare CDN caches everything at the edge—most requests never hit my homelab.
The web interface at trips.jomcgi.dev shows the route on a map, photos by day, elevation profiles, and trip statistics. WebSocket connection for live updates during active trips.
flowchart LR
YAML[YAML Schema]
Parse[Parse & Validate]
Gen[Code Generator]
Types[types.go]
Trans[transitions.go]
Metrics[metrics.go]
Status[status.go]
YAML --> Parse --> Gen
Gen --> Types
Gen --> Trans
Gen --> Metrics
Gen --> Status
style YAML fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Parse fill:#ffa502,stroke:#ffa502,color:#fff
style Gen fill:#ffd93d,stroke:#ffd93d,color:#000
style Types fill:#6bcb77,stroke:#6bcb77,color:#fff
style Trans fill:#4d96ff,stroke:#4d96ff,color:#fff
style Metrics fill:#9b59b6,stroke:#9b59b6,color:#fff
style Status fill:#e056fd,stroke:#e056fd,color:#fff states:
- name: Pending
initial: true
- name: Creating
fields:
requestID: string
- name: Ready
terminal: true
transitions:
- from: Pending
to: Creating
action: StartCreation
params:
- requestID: string flowchart LR
Deploy[Deployment]
Watch[Operator Watch]
DNS[DNS Record]
ZT[Zero Trust App]
Config[Tunnel Config]
CF[(Cloudflare)]
Deploy -->|Annotations| Watch
Watch --> DNS
Watch --> ZT
Watch --> Config
DNS --> CF
ZT --> CF
Config --> CF
style Deploy fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Watch fill:#ffa502,stroke:#ffa502,color:#fff
style DNS fill:#ffd93d,stroke:#ffd93d,color:#000
style ZT fill:#6bcb77,stroke:#6bcb77,color:#fff
style Config fill:#4d96ff,stroke:#4d96ff,color:#fff
style CF fill:#9b59b6,stroke:#9b59b6,color:#fff metadata:
annotations:
cloudflare.ingress.hostname: myapp.jomcgi.dev
cloudflare.zero-trust.policy: joe-only flowchart TB
subgraph Acquire
LP[Light Pollution Atlas]
Roads[OSM Road Network]
Elev[SRTM Elevation]
Weather[MET Norway API]
end
subgraph Process
Dark[Dark Region Extract]
Buffer[Road Buffering]
Zones[Zone Classification]
end
subgraph Score
Cloud[Cloud Cover]
Humid[Humidity]
Wind[Wind Speed]
Final[Final Score]
end
LP --> Dark
Roads --> Buffer
Elev --> Zones
Dark --> Final
Buffer --> Final
Zones --> Final
Weather --> Cloud --> Final
Weather --> Humid --> Final
Weather --> Wind --> Final
style LP fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Roads fill:#ffa502,stroke:#ffa502,color:#fff
style Elev fill:#ffd93d,stroke:#ffd93d,color:#000
style Weather fill:#6bcb77,stroke:#6bcb77,color:#fff
style Dark fill:#4d96ff,stroke:#4d96ff,color:#fff
style Buffer fill:#9b59b6,stroke:#9b59b6,color:#fff
style Zones fill:#e056fd,stroke:#e056fd,color:#fff
style Cloud fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style Humid fill:#ffa502,stroke:#ffa502,color:#fff
style Wind fill:#ffd93d,stroke:#ffd93d,color:#000
style Final fill:#6bcb77,stroke:#6bcb77,color:#fff flowchart LR
subgraph Anywhere
Laptop[Laptop]
CI[CI]
Claude[Claude Code]
end
Fmt[format]
subgraph Build
Code[Formatters]
Helm[Manifests]
OCI[Images]
end
Cache[(BuildBuddy)]
subgraph Output
Git[Git]
Reg[Registry]
end
Laptop --> Fmt
CI --> Fmt
Claude --> Fmt
Fmt --> Code
Fmt --> Helm
Fmt --> OCI
Code --> Cache
Helm --> Cache
OCI --> Cache
Cache --> Git
Cache --> Reg
style Laptop fill:#ff6b6b,stroke:#ff6b6b,color:#fff
style CI fill:#ffa502,stroke:#ffa502,color:#fff
style Claude fill:#ffd93d,stroke:#ffd93d,color:#000
style Fmt fill:#6bcb77,stroke:#6bcb77,color:#fff
style Code fill:#4d96ff,stroke:#4d96ff,color:#fff
style Helm fill:#4d96ff,stroke:#4d96ff,color:#fff
style OCI fill:#4d96ff,stroke:#4d96ff,color:#fff
style Cache fill:#9b59b6,stroke:#9b59b6,color:#fff
style Git fill:#e056fd,stroke:#e056fd,color:#fff
style Reg fill:#e056fd,stroke:#e056fd,color:#fff Helm charts render through Bazel. Output goes to the source tree so Git tracks it. PR diffs show exactly what's changing in the cluster.
arm64 on my laptop, amd64 in CI. Same rules build both and push a multi-platform index.