# BincioActivity > **Your data. Your server. Your rules.** > No cloud. No subscriptions. No lock-in. 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. **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. --- ## 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. edits/*.md ← Optional sidecar edits (titles, descriptions, photos). │ ▼ bincio render ← Merges sidecars → _merged/. Runs Astro build. │ ▼ 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 ```bash # 1. Install Python package pip install bincio # or: uv add bincio # 2. Configure cp extract_config.example.yaml extract_config.yaml $EDITOR extract_config.yaml # set input dirs, output dir, your name # 3. Extract activities → BAS JSON bincio extract # 4. Build the site (requires Node >= 20) cd site && npm install cp .env.example .env # configure BINCIO_DATA_DIR bincio render # merges edits + runs astro build # → open site/dist/index.html ``` For live development with hot reload: ```bash bincio render --serve # merges edits, links data, starts astro dev # → http://localhost:4321 # Optional: enable the activity edit UI bincio edit # starts edit server on http://localhost:4041 # Set PUBLIC_EDIT_URL=http://localhost:4041 in site/.env ``` --- ## Cheatsheet ### Extract ```bash bincio extract # uses extract_config.yaml bincio extract --input ~/rides --output ~/bincio_data bincio extract --file ride.gpx # single file, prints JSON to stdout bincio extract --since 2025-01-01 # only files newer than date ``` Supported formats: GPX, FIT, TCX — all with optional `.gz` compression. Strava bulk export: point `metadata_csv` at `activities.csv` to pull in titles, descriptions, and gear. 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`. ### Site dev ```bash cd site npm run dev # http://localhost:4321 — live reload on data or code changes 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 ### `extract_config.yaml` ```yaml owner: handle: yourname display_name: Your Name input: dirs: - ~/Activities/gpx - ~/Activities/fit metadata_csv: ~/strava_export/activities.csv # optional — Strava titles/descriptions output: dir: ~/bincio_data default_privacy: public # public | blur_start | no_gps | private track: rdp_epsilon: 0.0001 # GPS track simplification (~11 m at equator) timeseries_hz: 1 # data samples per second stored in JSON incremental: true # skip files whose hash hasn't changed ``` ### 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/ index.json ← summary of all activities + owner info activities/ 2024-05-15T08:30:00Z.json ← full activity: stats, laps, timeseries 2024-05-15T08:30:00Z.geojson ← simplified GPS track (RDP) ``` `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. See `SCHEMA.md` for the full specification. --- ## Federation (work in progress) Add a friend's published `index.json` URL to your `site_config.yaml`: ```yaml data_sources: - type: local path: ~/bincio_data - type: remote handle: alice url: https://alice.example.com/bincio/index.json ``` 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. --- ## Tech stack | Layer | Technology | |---|---| | Extract | Python 3.12, click, fitdecode, gpxpy, lxml | | Site framework | Astro 4 (static output) | | UI components | Svelte 5 | | Styling | Tailwind CSS v3 | | Charts | Observable Plot | | Maps | MapLibre GL v5 + OpenFreeMap tiles | | Python packages | uv | | Node packages | npm | --- ## Project layout ``` 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` — merge + astro build/serve merge.py sidecar edit overlay (produces _merged/) edit/ cli.py `bincio edit` — local edit server server.py FastAPI write API for the edit drawer 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 + photo gallery ActivityMap.svelte MapLibre GL map ActivityCharts.svelte Observable Plot charts StatsView.svelte Heatmap, percentile scaling, sport filter EditDrawer.svelte Slide-in activity editor 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.