Realtime tracker for TransPalu and Trans Donggala BRT, hosted at cektrans.banuacoder.com.
A Vue 3 static SPA + a small Rust proxy run as two containers on the
shared Banuacoder VPS, fronted by Traefik (with Let's Encrypt TLS).
The proxy talks to the upstream BRT Nusantara GpsApi — handling
AES-256-CBC token + body crypto, REST caching, and Socket.IO fan-out —
and exposes a clean public API (/api/* + /socket.io/) to the
browser. Upstream endpoints and the shared secret are operator-only;
supply them via runtime env (see .env.example).
trans_sulteng_tracker/
├─ apps/
│ ├─ web/ # Vue 3 + Vite + TS + Pinia + Tailwind v4
│ └─ proxy/ # Rust: axum + socketioxide + rust_socketio
├─ ops/
│ ├─ docker-compose.yml
│ ├─ web.Dockerfile
│ ├─ proxy.Dockerfile
│ └─ nginx-web.conf
├─ docs/ # design notes, API findings, ops runbooks
└─ scripts/ # one-off helpers
Prerequisites: Node.js ≥ 20, pnpm ≥ 10, Rust stable (via rustup), Docker (for the full compose stack).
# 1. One-time install (JS + Rust deps)
make install
# or: pnpm install && cargo fetch --manifest-path apps/proxy/Cargo.toml
# 2. Drop in your local env
cp .env.example .env # fill in BRT_KEY + upstream URLs
# 3. Start both apps with one command
pnpm dev
# → web http://localhost:5173 (proxies /api + /socket.io to :8080)
# → proxy http://localhost:8080 (Axum REST + Socket.IO bridge)Run them separately with pnpm dev:web / pnpm dev:proxy, or via
the Makefile (make dev, make dev-web, make dev-proxy). Both
apps read the same root-level .env.
Or run both containers behind Traefik (mirrors prod):
# 1. Bring up the shared Traefik stack (Archipelago infra)
# so the `traefik-public` network exists.
# 2. Build + start cektrans
docker compose -f ops/docker-compose.yml up --build -d
# 3. Add `cektrans.banuacoder.com 127.0.0.1` to /etc/hosts
# or override the Host rule in compose for local testing.All runtime config lives in environment variables. Never commit a real .env.
| Var | Where | Notes |
|---|---|---|
BIND_ADDR |
proxy | Default 0.0.0.0:8080. |
BRT_REST_BASE |
proxy | Upstream REST base URL. |
BRT_SOCKET_BASE |
proxy | Upstream Socket.IO base URL. |
BRT_KEY |
proxy | 32-byte UTF-8 AES-256-CBC secret. Rotate when upstream rotates. |
ALLOW_ORIGIN |
proxy | CORS allow-origin for the public site. |
RUST_LOG |
proxy | tracing-subscriber filter. |
VITE_PROXY_TARGET |
web (dev only) | Where vite forwards /api + /socket.io in dev. |
VITE_BUILD_SHA |
web (build only) | Surfaced in the footer / health UI. |
In production these are injected by the deploy environment (GitHub Actions
secrets → container env). The frontend bundle is built with build-time
VITE_* vars — only public values belong there.
- Visual: Banuacoder warm-minimal (ink
#0A0E14/ paper#F7F7F4/ cyan accent#1D9CD4) with Geist + Inter + JetBrains Mono. - Map: CARTO Voyager raster tiles.
- Buses: TransJakarta-style — small filled circle, white ring, corridor
code (e.g.
K1) inside in bold mono, rotated to bus heading. Stale (>5 min since last fix) buses get a hatched gray treatment.
BRT Nusantara · Mitra Darat (Kementerian Perhubungan). Upstream endpoints, crypto schema, and wire DTOs are kept in operator-private notes — never published in this repo.
Operational note: the upstream is dev-grade infrastructure and may break or rotate keys without warning. The proxy is built to tolerate 401 + retry; full key rotation is a single env var change.
Apache-2.0 — see LICENSE.