Files
btcprice/CLAUDE.md
T
magnusandClaude Opus 5 2af8b6f62d Add citadel deployment
Port of ../elprice/deploy-citadel.sh: build the image locally for the
remote's architecture, ship it over SSH with docker save | docker load,
and run docker compose up -d --no-build in /home/pmp/apps/btcprice.

The deployment compose is standalone rather than joining btc-network,
because citadel runs no btcdata — BTCDATA_URL points at the instance
already running on omarchy (http://192.168.50.42:4102).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BdMTk61JgUdNH86ttMi4ce
2026-09-10 14:24:38 +02:00

3.6 KiB

CLAUDE.md

Bitcoin price tracker UI — Python FastAPI web application. Serves the HTML/Tailwind UI and proxies live price data from btcdata over WebSocket.

Build & Development Commands

  • make run — Start dev server on port 4000; creates .venv and installs requirements.txt on first run (the venv target, keyed off requirements.txt)
  • cp .env.example .env — optional; .env is -included, so a missing one is not fatal
  • make run-local — Same, but with BTCDATA_URL=$BTCDATA_LOCAL_URL
  • make tailwind — Watch Tailwind CSS for changes
  • make test — Runs pytest tests/, but there is no tests/ directory yet and pytest is not in requirements.txt
  • make venv — Bootstrap the virtualenv without starting the server

Docker

Docker runs alongside local dev on different ports:

Service Local dev Docker
btcprice localhost:4000 localhost:4041 (container port 4040)
btcdata localhost:4100 localhost:4102

docker compose up -d here starts btcprice only. Both projects attach to the external btc-network (docker network create btc-network once), and btcdata must be started from ../btcdata. Inside the network btcprice reaches it at http://btcdata:${DOCKER_BTCDATA_PORT:-4102}.

Note: Dockerfile bakes in a stale ENV BTCDATA_URL=http://btcdata:4101; compose always overrides it, so it only matters if the image is run outside compose. ENV PORT is likewise unused — the port in CMD is hardcoded.

Deployment

./deploy-citadel.sh — build locally for the remote's arch, docker save | ssh docker load, docker compose up -d --no-build in /home/pmp/apps/btcprice on citadel (192.168.50.195). Mirrors ../elprice/deploy-citadel.sh; no registry.

  • docker-compose.citadel.yml → remote docker-compose.yml; .env.citadel → remote .env.
  • Standalone (no btc-network): citadel has no btcdata, so BTCDATA_URL points at the btcdata on omarchy, http://192.168.50.42:4102.
  • Live at http://192.168.50.195:4041. A separate btcprice also still runs on omarchy at :4041.

Architecture

  • Framework: FastAPI
  • Server: Uvicorn
  • Templates: Inline HTML strings + HTMX (WebSocket extension)
  • Styling: Tailwind CSS v4
  • Data backend: btcdata (separate service at BTCDATA_URL, default http://localhost:4100)
  • No database — all data comes from btcdata

btcdata connection

The browser connects only to btcprice: ws-connect="/ws/price" and ws-connect="/ws/strike". Each endpoint runs _ws_proxy, which opens an upstream WebSocket to btcdata's /api/price/ws and /api/strike/ws, converts each JSON message to an HTML fragment (price_json_to_html, strike_json_to_html) and forwards it. BTCDATA_URL is the single point of contact; _btcdata_ws_url rewrites its scheme to ws/wss.

Source Layout

btcprice acts as a WebSocket proxy between the browser and btcdata:

  1. Browser connects via HTMX WebSocket to /ws/price or /ws/strike
  2. btcprice proxies to btcdata's /api/price/ws or /api/strike/ws
  3. JSON from btcdata is transformed into HTML fragments (in price_json_to_html / strike_json_to_html)
  4. HTMX swaps the HTML into the DOM by matching element IDs (price-display, strike-display)

Key Conventions

  • Numbers use Danish locale formatting (dots for thousands, commas for decimals) via da_fmt()
  • Price changes are color-coded: green (up), red (down), gray (unchanged)
  • All HTML is built as string concatenation in Python — no template engine