- bincio/render/merge.py: parse sidecar .md files (YAML frontmatter +
markdown body), produce data/_merged/ with symlinks for unmodified
activities and real merged files for overridden ones; filters private
activities from index.json; sorts highlighted activities first.
Keeps extracted data pristine — re-running extract never clobbers edits.
- bincio/edit/: FastAPI edit server (port 4041) with embedded HTML/JS
edit UI; GET/POST /api/activity/{id} reads/writes sidecars; multipart
image upload to edits/images/{id}/; DELETE for image cleanup.
- bincio render now calls merge_all() before build/serve and symlinks
public/data → data/_merged/ instead of data/ directly.
- ActivityDetail.svelte: edit button (links to edit server) when
PUBLIC_EDIT_URL env var is set; respects custom.hide_stats to suppress
stat panels; description supports whitespace-preserving rendering.
- 15 unit tests covering parse_sidecar, apply_sidecar, and merge_all.
BincioActivity
A federated, open-source, self-hosted activity stats platform. Own your data. Share what you want. Follow friends by URL.
What it is
BincioActivity turns a folder of GPX/FIT/TCX files into a beautiful, modern static website — no database, no server required. It can run from the local filesystem, GitHub Pages, or any static host.
Federation: anyone can "follow" a friend's data by adding a URL to their config. Friends' activities appear in your site, attributed to them.
Quick start
# Install
pip install bincio # or: uv add bincio
# Extract your activities
cp extract_config.example.yaml extract_config.yaml
# edit extract_config.yaml with your paths
bincio extract
# Build the site (requires Node ≥ 20)
cd site && npm install
BINCIO_DATA_DIR=~/bincio_data npm run build
# open site/dist/index.html
Two stages
Stage 1 — Extract (bincio extract)
Reads GPX, FIT, TCX files (including .gz compressed) and writes a
BincioActivity Schema (BAS) data store: plain JSON + GeoJSON files.
bincio extract # uses extract_config.yaml
bincio extract --input ~/rides --output ~/bincio_data
bincio extract --file ride.gpx # single file → stdout
bincio extract --since 2025-01-01 # incremental
Supported sources:
- GPX (generic, Garmin extensions)
- FIT (Garmin, Hammerhead Karoo)
- TCX (including Garmin's https:// namespace variant)
- All of the above gzip-compressed (
.gz) - Strava bulk export (
activities.csvcarries titles and descriptions)
Stage 2 — Render (bincio render)
Generates a static site from the BAS data store using Astro.
cd site
BINCIO_DATA_DIR=~/bincio_data npm run dev # development
BINCIO_DATA_DIR=~/bincio_data npm run build # production build → site/dist/
Configuration
extract_config.yaml
owner:
handle: yourname
display_name: Your Name
input:
dirs:
- ~/Activities/gpx
- ~/Activities/fit
metadata_csv: ~/strava_export/activities.csv # optional Strava metadata
output:
dir: ~/bincio_data
default_privacy: public # public | blur_start | no_gps | private
track:
rdp_epsilon: 0.0001 # GPS simplification (~11m at equator)
incremental: true # skip already-processed files
site/.env
BINCIO_DATA_DIR=/path/to/bincio_data
The BincioActivity Schema (BAS)
The data store is a directory of plain JSON files:
bincio_data/
index.json ← activity feed + owner manifest
activities/
{id}.json ← full activity with timeseries
{id}.geojson ← simplified GPS track
See SCHEMA.md for the full specification. The schema is versioned and
published as a standalone document so anyone can write importers in any
language.
Federation
Add a friend's index.json URL to your site_config.yaml:
data_sources:
- type: local
path: ~/bincio_data
- type: remote
handle: alice
url: https://alice.github.io/bincio/index.json
At build time the renderer fetches their public data and renders it under
/friends/alice/.
Privacy
Privacy is enforced at the data layer — activities never leave your control:
| Level | GPS track | Stats visible |
|---|---|---|
public |
Full track | Yes |
blur_start |
First/last 200 m removed | Yes |
no_gps |
Not published | Yes |
private |
Not published | Not in index |
Tech stack
| Layer | Technology |
|---|---|
| Extract | Python 3.12, click, fitdecode, gpxpy, lxml, rdp |
| Site framework | Astro (static generation) |
| UI components | Svelte 5 |
| Styling | Tailwind CSS |
| Charts | Observable Plot |
| Maps | MapLibre GL + OpenFreeMap tiles |
| Package manager (Python) | uv |
| Package manager (Node) | npm |
Development
# Python
uv sync
uv run pytest
uv run bincio --help
# Site
cd site && npm install
BINCIO_DATA_DIR=/tmp/bincio_test npm run dev