mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
A fresh hub following docs/self-hosting.md was a locked room: invite-only (the default) showed "Sign up disabled" with nobody to mint an invite, and the approval-gated posture stranded the first admin as pending forever. Emails on the config's admin list are operator-vetted, so they now activate immediately on signup (any posture), and while the hub has zero accounts they may sign up even on an invite-only hub. Strangers still can't take the bootstrap slot, and the door closes after the first account. Validated end to end from scratch: hub boot → admin signup → device-code login × 2 devices → init → bidirectional sync → hooks install. Also adds the missing GitHub Actions CI workflow (build/vet/test on ubuntu + macos) — the repo previously had no CI at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
165 lines
7.8 KiB
Markdown
165 lines
7.8 KiB
Markdown
# Self-hosting a BearDrive hub in ~10 minutes
|
|
|
|
Until BearDrive Cloud launches, self-hosting is *the* way to run a team
|
|
hub — and it stays a first-class, fully-supported path forever (AGPL, no
|
|
features held back). One static Go binary, one config file, any object
|
|
store (or a plain directory).
|
|
|
|
## 1. Install the binary
|
|
|
|
```sh
|
|
brew install runbear-io/tap/beardrive # macOS / Linuxbrew
|
|
# or: go install github.com/runbear-io/beardrive/cmd/bdrive@latest
|
|
# or: grab a release tarball — https://github.com/runbear-io/beardrive/releases
|
|
```
|
|
|
|
## 2. Pick storage
|
|
|
|
Any of: `s3://bucket/prefix`, `gs://bucket/prefix`, an S3-compatible
|
|
endpoint, or — simplest for a first run — a plain directory
|
|
(`file:///var/lib/bdrive/storage`). Files and change journals live
|
|
there; hub metadata (accounts, projects, orgs) lives in the database you
|
|
pick in step 3. Clients never see this storage — they sync through the
|
|
hub over HTTPS.
|
|
|
|
## 3. Write config.json
|
|
|
|
```jsonc
|
|
{
|
|
"remote": "file:///var/lib/bdrive/storage",
|
|
"addr": ":4173",
|
|
"upload": true,
|
|
"auth": {
|
|
// Signup is invite-only by default — the safe posture for a public
|
|
// URL. Bootstrap: while the hub has zero accounts, the emails listed
|
|
// in "admins" can sign up directly; then invite teammates from the
|
|
// web UI.
|
|
"admins": ["you@example.com"],
|
|
"users_db": "/var/lib/bdrive/auth.json"
|
|
},
|
|
"reads": { "enabled": true }, // agent read analytics (Insights)
|
|
"database": { "driver": "sqlite", "dsn": "/var/lib/bdrive/hub.db" }
|
|
}
|
|
```
|
|
|
|
Full knob reference for auth and databases: the sections below;
|
|
share-link rate limits and upload TTLs: the
|
|
[README's web-server section](../README.md#web-server).
|
|
|
|
## 4. Run it
|
|
|
|
```sh
|
|
bdrive web -c config.json
|
|
```
|
|
|
|
Put TLS in front (Caddy/nginx/your platform's LB) — device tokens travel
|
|
as bearer tokens. For containers, the repo ships a `Dockerfile`
|
|
(distroless, CGO-free) with a Cloud Run recipe in [deploy/](../deploy/)
|
|
— any container platform works the same way.
|
|
|
|
## 5. First sign-in and first project
|
|
|
|
1. Open `https://your-hub/` → sign up with one of the `admins` emails
|
|
from your config. On a brand-new hub these can always create the
|
|
first account (whatever the signup posture); it activates
|
|
immediately, and admin-listed emails skip the approval/verification
|
|
gates on any posture.
|
|
2. On any machine: `bdrive login https://your-hub` (browser flow), then
|
|
in the folder you want synced:
|
|
`bdrive init --name wiki --yes` — or `--shared docs` inside a repo to
|
|
sync only that subfolder.
|
|
3. Invite a teammate: sidebar footer → **Manage** → **New invite** —
|
|
the join link both creates their account and adds them to your org.
|
|
4. Connect agents: the project's home page in the web UI shows one-paste
|
|
setup for Claude Code/Cowork, Hermes, and Codex — hub URL and project
|
|
id already filled in. Teammates paste it into their own agent, which
|
|
installs the CLI, keeps the beardrive skill (`bdrive skill install`),
|
|
signs in, mounts the project, and registers the sync hooks.
|
|
|
|
## Authentication reference
|
|
|
|
Hubs always require sign-in — every change is attributed to a real account.
|
|
The whole API — web UI, uploads, project creation, device sync — needs a
|
|
session; only `/api/config` and the auth pages stay open (the plain-folder
|
|
viewer, `bdrive web ./folder`, remains auth-free). Accounts are
|
|
email + password + name, kept in a file-backed registry (`auth.json`:
|
|
bcrypt password hashes and SHA-256 token digests, atomically rewritten —
|
|
no plaintext credentials ever touch disk).
|
|
|
|
**Signup is invite-only by default** — the safe posture for a hub on a
|
|
public URL. New people get in only through an expiring invite link an owner
|
|
mints; the link lets them create an account (bypassing the gates below) and
|
|
join, in one step. (One exception so a fresh hub isn't a locked room: while
|
|
zero accounts exist, the config's `admins` emails may sign up directly.) To allow self-service signup instead, set
|
|
`"allow_signup": true` **with a gate** — the server refuses to start an open
|
|
hub that has none, so a fake email can never just walk in. Three postures:
|
|
|
|
- **Invite-only** (default): `allow_signup` unset/false. Only invite links create accounts.
|
|
- **Approval-gated**: `allow_signup: true` + `require_approval: true` — anyone can sign up, but a hub admin approves each new account before it works (no SMTP needed).
|
|
- **Domain-restricted + verified**: `allow_signup: true` + `allowed_domains: ["you.com"]` + `require_verification: true` (needs `smtp`) — only your company's addresses may sign up, each confirming an emailed link. Verification without SMTP is refused (the link would otherwise only reach the server log).
|
|
|
|
Admins tune verification/approval live from the web UI (**Admin → Signup &
|
|
access**); `allowed_domains`, the admin list, and `allow_signup` are
|
|
server-config-owned so a browser session can never widen who gets in.
|
|
|
|
`bdrive login <url>` on a client device opens the server's sign-in page in
|
|
a browser (sign up right there if needed); when the user signs in, the
|
|
page bounces a one-time code to the CLI's loopback listener and the
|
|
terminal finishes on its own, storing a long-lived per-device token
|
|
(revocable server-side). On headless/SSH machines, `bdrive login --device`
|
|
prints a short code to approve from any signed-in browser instead. Every
|
|
sync and every `bdrive init` then authenticates with that token.
|
|
|
|
"Forgot password" emails a one-hour reset link via the `auth.smtp` block —
|
|
plain SMTP, so any provider works. With no SMTP configured, the link is
|
|
printed to the server log so an admin can hand it over; reset is never
|
|
fully broken.
|
|
|
|
Two notes: put a hub behind TLS (reverse proxy
|
|
or tailscale) — `bdrive login` warns when signing in over plain http to a
|
|
non-localhost address. Internally all of this sits behind an
|
|
`AuthProvider` interface; the open-source server ships the built-in
|
|
email/password provider, and alternative identity backends can be swapped
|
|
in without touching the CLI or the API.
|
|
|
|
## Choosing a database
|
|
|
|
A hub keeps a little **metadata** — accounts, projects, orgs, invites,
|
|
shares, devices — separate from your files. (File content and the sync
|
|
journals always live in the object store; the database never holds them.)
|
|
You choose where that metadata lives with the `database` block:
|
|
|
|
```jsonc
|
|
"database": { "driver": "file" } // default — JSON under BDRIVE_HOME
|
|
"database": { "driver": "sqlite", "dsn": "/var/lib/bdrive/hub.db" }
|
|
"database": { "driver": "postgres", "dsn": "postgres://…@…pooler.supabase.com:6543/postgres" }
|
|
```
|
|
|
|
- **file** (default): zero dependencies, human-readable JSON, perfect for a
|
|
laptop or a small self-hosted hub.
|
|
- **sqlite**: one embedded database file — a real DB locally with no server
|
|
to run.
|
|
- **postgres**: a managed Postgres such as **Supabase** for production —
|
|
just point `dsn` at its connection string (use the transaction pooler for
|
|
many connections). Since Supabase *is* Postgres, this stays fully
|
|
open-source with no managed-only lock-in.
|
|
|
|
`file` and `sqlite` are single-writer (run one hub instance); Postgres is
|
|
transactional and can back more than one instance. Switching backends
|
|
doesn't migrate existing data — pick one when you set the hub up. Both SQL
|
|
drivers are pure Go, so the binary stays a CGO-free static build.
|
|
|
|
## Upgrading
|
|
|
|
`brew upgrade beardrive` (clients and hub are the same binary — keep
|
|
them roughly in step; the sync protocol is append-only journals + blobs,
|
|
which old clients read forward). After upgrading a client, re-run
|
|
`bdrive hooks install` once per project to pick up any hook improvements,
|
|
and `bdrive skill install` once per machine to refresh the agent skill.
|
|
|
|
## Backup
|
|
|
|
Everything irreplaceable is in two places: the storage root (blobs +
|
|
journals — files and their entire history) and the metadata database
|
|
(accounts/projects/orgs/shares). Snapshot both; restore is copy-back.
|