update readme

This commit is contained in:
Davide Scaini
2026-03-29 13:52:23 +02:00
parent d9ddae57ba
commit 4899e51ed1
+161 -84
View File
@@ -1,68 +1,110 @@
# BincioActivity # BincioActivity
A federated, open-source, self-hosted activity stats platform. > **Your data. Your server. Your rules.**
Own your data. Share what you want. Follow friends by URL. > No cloud. No subscriptions. No lock-in.
## What it is BincioActivity is a self-hosted, federated activity stats platform. You point it at a folder of GPX/FIT/TCX files, it produces a static website. The website runs anywhere — a Raspberry Pi, GitHub Pages, a USB stick. No database. No server process. No account required.
BincioActivity turns a folder of GPX/FIT/TCX files into a beautiful, modern **The philosophy in one sentence:** your activity data is yours, it lives as plain files on your disk, and this tool turns those files into a beautiful site you control entirely.
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.
## How it works
```
GPX / FIT / TCX files
bincio extract ← Python CLI. Reads files, writes plain JSON.
~/bincio_data/ ← BAS data store. Human-readable JSON + GeoJSON.
bincio render / npm ← Astro build. Reads JSON, writes static HTML/JS/CSS.
site/dist/ ← Drop anywhere. Open index.html. Done.
```
Everything in `~/bincio_data/` is plain text you can read, edit, back up, or publish to a CDN. The site build is fully reproducible from those files.
---
## Quick start ## Quick start
```bash ```bash
# Install # 1. Install Python package
pip install bincio # or: uv add bincio pip install bincio # or: uv add bincio
# Extract your activities # 2. Configure
cp extract_config.example.yaml extract_config.yaml cp extract_config.example.yaml extract_config.yaml
# edit extract_config.yaml with your paths $EDITOR extract_config.yaml # set input dirs, output dir, your name
# 3. Extract activities → BAS JSON
bincio extract bincio extract
# Build the site (requires Node 20) # 4. Build the site (requires Node >= 20)
cd site && npm install cd site && npm install
BINCIO_DATA_DIR=~/bincio_data npm run build ln -sf ~/bincio_data public/data
# open site/dist/index.html npm run build
# → open site/dist/index.html
``` ```
## Two stages For live development with hot reload:
```bash
### Stage 1 — Extract (`bincio extract`) cd site
ln -sf ~/bincio_data public/data
Reads GPX, FIT, TCX files (including `.gz` compressed) and writes a npm run dev
BincioActivity Schema (BAS) data store: plain JSON + GeoJSON files. # → http://localhost:4321
``` ```
---
## Cheatsheet
### Extract
```bash
bincio extract # uses extract_config.yaml bincio extract # uses extract_config.yaml
bincio extract --input ~/rides --output ~/bincio_data bincio extract --input ~/rides --output ~/bincio_data
bincio extract --file ride.gpx # single file stdout bincio extract --file ride.gpx # single file, prints JSON to stdout
bincio extract --since 2025-01-01 # incremental bincio extract --since 2025-01-01 # only files newer than date
``` ```
Supported sources: Supported formats: GPX, FIT, TCX — all with optional `.gz` compression.
- GPX (generic, Garmin extensions) Strava bulk export: point `metadata_csv` at `activities.csv` to pull in titles, descriptions, and gear.
- FIT (Garmin, Hammerhead Karoo)
- TCX (including Garmin's https:// namespace variant)
- All of the above gzip-compressed (`.gz`)
- Strava bulk export (`activities.csv` carries titles and descriptions)
### Stage 2 — Render (`bincio render`) Extraction is **incremental by default** (`incremental: true` in config). Re-running only processes new or changed files. To force a full re-extract, delete `~/bincio_data/` or set `incremental: false`.
Generates a static site from the BAS data store using Astro. ### Site dev
``` ```bash
cd site cd site
BINCIO_DATA_DIR=~/bincio_data npm run dev # development npm run dev # http://localhost:4321 — live reload on data or code changes
BINCIO_DATA_DIR=~/bincio_data npm run build # production build → site/dist/ npm run build # production build → site/dist/
npm run preview # serve site/dist/ locally to check the production build
``` ```
The site reads data from `site/public/data/`. Symlink your BAS store there:
```bash
ln -sf ~/bincio_data site/public/data
```
### Python / tests
```bash
uv run pytest # full test suite
uv run pytest tests/test_fit.py -x # single file, stop on first failure
uv run bincio --help # CLI help
uv sync # install / update dependencies
```
---
## Configuration ## Configuration
### extract_config.yaml ### `extract_config.yaml`
```yaml ```yaml
owner: owner:
@@ -73,7 +115,7 @@ input:
dirs: dirs:
- ~/Activities/gpx - ~/Activities/gpx
- ~/Activities/fit - ~/Activities/fit
metadata_csv: ~/strava_export/activities.csv # optional Strava metadata metadata_csv: ~/strava_export/activities.csv # optional Strava titles/descriptions
output: output:
dir: ~/bincio_data dir: ~/bincio_data
@@ -81,36 +123,46 @@ output:
default_privacy: public # public | blur_start | no_gps | private default_privacy: public # public | blur_start | no_gps | private
track: track:
rdp_epsilon: 0.0001 # GPS simplification (~11m at equator) rdp_epsilon: 0.0001 # GPS track simplification (~11 m at equator)
timeseries_hz: 1 # data samples per second stored in JSON
incremental: true # skip already-processed files incremental: true # skip files whose hash hasn't changed
``` ```
### site/.env ### Privacy levels
| Level | GPS track | Stats | Appears in index |
|---|---|---|---|
| `public` | Full | Yes | Yes |
| `blur_start` | First/last 200 m removed | Yes | Yes |
| `no_gps` | Not published | Yes | Yes |
| `private` | Not published | No | No |
Privacy is enforced at extract time. A `private` activity never enters `index.json` and is never served.
---
## The BAS data store
`bincio extract` produces a directory of plain files — the **BincioActivity Schema (BAS)** store:
``` ```
BINCIO_DATA_DIR=/path/to/bincio_data ~/bincio_data/
``` index.json ← summary of all activities + owner info
## The BincioActivity Schema (BAS)
The data store is a directory of plain JSON files:
```
bincio_data/
index.json ← activity feed + owner manifest
activities/ activities/
{id}.json ← full activity with timeseries 2024-05-15T08:30:00Z.json ← full activity: stats, laps, timeseries
{id}.geojson ← simplified GPS track 2024-05-15T08:30:00Z.geojson ← simplified GPS track (RDP)
``` ```
See `SCHEMA.md` for the full specification. The schema is versioned and `index.json` is everything the feed page needs — no extra fetches until you open an activity. `{id}.json` contains the full timeseries (elevation, speed, HR, cadence, power at 1 Hz) for charts and the detail map. Both are human-readable and editable with any text editor.
published as a standalone document so anyone can write importers in any
language.
## Federation See `SCHEMA.md` for the full specification.
Add a friend's `index.json` URL to your `site_config.yaml`: ---
## Federation (work in progress)
Add a friend's published `index.json` URL to your `site_config.yaml`:
```yaml ```yaml
data_sources: data_sources:
@@ -118,45 +170,70 @@ data_sources:
path: ~/bincio_data path: ~/bincio_data
- type: remote - type: remote
handle: alice handle: alice
url: https://alice.github.io/bincio/index.json url: https://alice.example.com/bincio/index.json
``` ```
At build time the renderer fetches their public data and renders it under At build time the renderer fetches their public data and renders it under `/friends/alice/`. Your site, their data — with full attribution. They control what they publish; you control what you display.
`/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 ## Tech stack
| Layer | Technology | | Layer | Technology |
|---|---| |---|---|
| Extract | Python 3.12, click, fitdecode, gpxpy, lxml, rdp | | Extract | Python 3.12, click, fitdecode, gpxpy, lxml |
| Site framework | Astro (static generation) | | Site framework | Astro 4 (static output) |
| UI components | Svelte 5 | | UI components | Svelte 5 |
| Styling | Tailwind CSS | | Styling | Tailwind CSS v3 |
| Charts | Observable Plot | | Charts | Observable Plot |
| Maps | MapLibre GL + OpenFreeMap tiles | | Maps | MapLibre GL v5 + OpenFreeMap tiles |
| Package manager (Python) | uv | | Python packages | uv |
| Package manager (Node) | npm | | Node packages | npm |
## Development ---
```bash ## Project layout
# Python
uv sync
uv run pytest
uv run bincio --help
# Site
cd site && npm install
BINCIO_DATA_DIR=/tmp/bincio_test npm run dev
``` ```
bincio/ Python package
extract/
cli.py `bincio extract` entry point
parsers/ GPX, FIT, TCX parsers
sport.py sport name normalisation
metrics.py haversine stats (single-pass)
timeseries.py 1 Hz downsampling
simplify.py RDP track simplification
dedup.py hash-based + near-duplicate detection
strava_csv.py Strava activities.csv reader
writer.py BAS JSON + GeoJSON writer
render/
cli.py `bincio render` stub
schema/
bas-v1.schema.json JSON Schema for BAS format
SCHEMA.md Human-readable BAS specification
site/ Astro project
src/
pages/
index.astro Activity feed
activity/[id].astro Single activity detail
stats/index.astro Yearly heatmaps + totals
components/
ActivityFeed.svelte Card grid, sport filter, pagination
ActivityDetail.svelte Map + stats + charts
ActivityMap.svelte MapLibre GL map
ActivityCharts.svelte Observable Plot charts
StatsView.svelte Heatmap, percentile scaling, sport filter
lib/
types.ts BAS TypeScript types
format.ts Formatting helpers
```
---
## Why no database?
Databases add operational complexity — backups, migrations, running processes, credentials. Activity data is append-only and read-heavy. Plain JSON files handle this perfectly, are trivially backed up with `cp` or `rsync`, can be diffed in git, and work offline. The site is a folder you can zip and email.
## Why federation?
Strava, Garmin Connect, and similar platforms are silos. If the company shuts down or changes its terms, your data and your social graph go with it. BincioActivity's federation model is inspired by the open web: you host your own data at a URL, friends subscribe to that URL, and no central authority is involved.