# Multi-user deployment Multiple users share one bincio instance. Activities are public within the instance by default. The `private` flag hides individual activities. The whole instance requires login to view (private by default). ## Architecture ``` internet │ ▼ nginx / caddy ├── /* → static files (site/dist/) └── /api/* → proxy → bincio serve (127.0.0.1:4041) ``` `bincio serve` owns all dynamic behaviour — auth, user management, write operations. nginx serves static files and proxies API routes. `bincio serve` never handles static files. Sessions are httpOnly cookies (`bincio_session`), stored in SQLite. The Astro site calls `GET /api/me` on page load to detect the logged-in user. ## Data layout ``` /data/ ← BINCIO_DATA_DIR instance.db ← SQLite: users, sessions, invites index.json ← shard manifest (no activity data) {handle}/ index.json ← user's BAS feed (activities) _merged/ ← sidecar-merged output (served to browser) activities/ edits/ strava_token.json ``` The root `index.json` is a shard manifest — it lists user shard URLs but contains no activity data. Each user's `{handle}/index.json` is a valid standalone BAS feed (usable for federation). The browser resolves shards concurrently and merges them. ## Step 1 — Initialise the instance ```bash uv sync --extra serve uv run bincio init \ --data-dir /var/bincio \ --handle dave \ --password 'your-password' \ --display-name "Dave" \ --name "My Bincio" ``` This creates: - `/var/bincio/instance.db` — SQLite database - `/var/bincio/dave/` — admin user data directory - `/var/bincio/index.json` — root shard manifest (with `"private": true`) - Prints a first invite code `bincio init` is idempotent — safe to re-run. ## Step 2 — Extract activities ```bash uv run bincio extract \ --input ~/activity-files \ --output /var/bincio/dave ``` ## Step 3 — Build the site ```bash cd site && npm install && cd .. uv run bincio render \ --data-dir /var/bincio \ --site-dir site # Output: site/dist/ ``` In multi-user mode, `bincio render`: - Runs `merge_all()` for each user's directory - Rewrites the root `index.json` shard manifest - Symlinks `site/public/data → /var/bincio` - Builds the Astro site Incremental rebuild (one user only): ```bash uv run bincio render --data-dir /var/bincio --handle dave # Re-merges dave's shard, rewrites root manifest — does not rebuild the site ``` ## Step 4 — Configure nginx ```nginx server { listen 443 ssl; server_name example.com; root /var/www/bincio; # → site/dist/ location / { try_files $uri $uri/ $uri.html =404; } location /api/ { proxy_pass http://127.0.0.1:4041; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` Copy `site/dist/` to `/var/www/bincio` after each build. ## Step 5 — Start bincio serve ```bash uv run bincio serve \ --data-dir /var/bincio \ --site-dir /path/to/site ``` As a systemd service: ```ini [Unit] Description=bincio serve After=network.target [Service] Type=simple User=bincio WorkingDirectory=/home/bincio/bincio-activity ExecStart=uv run bincio serve --data-dir /var/bincio --site-dir site Restart=on-failure [Install] WantedBy=multi-user.target ``` ## Inviting users After initialising, `bincio init` prints an invite code. To generate more: ```bash # From the admin account, via the browser at /invites/ # Or directly in the database: python3 -c " from pathlib import Path from bincio.serve.db import open_db, create_invite db = open_db(Path('/var/bincio')) print(create_invite(db, 'dave')) " ``` Share the invite link: `https://example.com/register/?code=XXXXXXXX` Invite limits: admins — unlimited. Regular users — 3 invites each (configurable in `bincio/serve/db.py`, `_MAX_USER_INVITES`). ## Instance privacy By default, `bincio init` sets `"private": true` in the root `index.json`. This means every page (except `/login/` and `/register/`) redirects unauthenticated visitors to `/login/`. To make the instance public, edit `/var/bincio/index.json` and set `"private": false`. The next `bincio render` will preserve this setting. ## Local testing (before deploying) ```bash # 1. Initialise a test instance uv run bincio init --data-dir /tmp/bincio_test --handle dave --password test # 2. Extract activities into the user's dir uv run bincio extract --input ~/activity-files --output /tmp/bincio_test/dave # 3. Build + start the dev server (terminal 1) uv run bincio render --data-dir /tmp/bincio_test --site-dir site --serve # 4. Start bincio serve (terminal 2) uv run bincio serve --data-dir /tmp/bincio_test ``` The Astro dev server proxies `/api/*` to `localhost:4041` (configured in `astro.config.mjs`), so cookies work same-origin. Set `site/.env`: ``` BINCIO_DATA_DIR=/tmp/bincio_test PUBLIC_EDIT_URL= ``` `PUBLIC_EDIT_URL` empty = edit UI enabled via proxy. The edit/upload button appears when `bincio serve` is running. In production nginx plays the same proxy role. ## Per-user Strava sync Each user connects their own Strava account. The OAuth token is stored in `/var/bincio/{handle}/strava_token.json`. The "Connect Strava" and "Sync" buttons in the upload modal work per-session — each user syncs only their own activities. ## Federation To follow another bincio instance, add a shard entry to the root `index.json`: ```json { "shards": [ { "handle": "dave", "url": "dave/_merged/index.json" }, { "handle": "alice", "url": "https://alice.example.com/index.json" } ] } ``` The browser fetches and merges remote shards concurrently. Remote activities appear in the combined feed with `@alice` attribution. ## See also - [CLI reference — bincio init](../reference/cli.md#bincio-init) - [CLI reference — bincio serve](../reference/cli.md#bincio-serve) - [API reference](../reference/api.md) - [BAS schema — instance manifest](../../SCHEMA.md#instance-manifest)