6.0 KiB
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
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
uv run bincio extract \
--input ~/activity-files \
--output /var/bincio/dave
Step 3 — Build the site
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.jsonshard manifest - Symlinks
site/public/data → /var/bincio - Builds the Astro site
Incremental rebuild (one user only):
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
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
uv run bincio serve \
--data-dir /var/bincio \
--site-dir /path/to/site
As a systemd service:
[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:
# 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)
# 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:
{
"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.