From 3422d3509f93412060463c8b13f1475356039c0c Mon Sep 17 00:00:00 2001 From: Snow Lee Date: Wed, 15 Jul 2026 12:13:44 -0700 Subject: [PATCH] =?UTF-8?q?feat(gtm):=20round=203=20=E2=80=94=20never=20de?= =?UTF-8?q?fault=20a=20stranger=20into=20a=20dead=20cloud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewer round 3 (9/7/9/8/7/9) caught the sharpest truth bug yet: the README quickstart's bare 'bdrive login' defaults to beardrive.ai, which is waitlist-only and not accepting logins — a launch-day visitor's first command would fail silently. Fixes: - quickstart step 1 now routes to self-hosting ('bdrive login https://your-hub', ~10-minute guide linked) with Cloud honestly framed as waitlist; website closer promises 'Self-host in one binary' instead of 'under a minute, done' - handoff gains a P0 gate: verify the FULL first run (login → init → edit → log) against a working hub before any launch; bare 'bdrive login' must not be advertised until Cloud accepts logins - handoff: seed 3-5 good-first-issues (CONTRIBUTING promises them); replace the mailto waitlist with a real form before Product Hunt - README trimmed 583→524 lines: the Authentication and Choosing-a- database operator reference moved verbatim into docs/self-hosting.md with a compact pointer left behind Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt --- README.md | 85 +++++++---------------------------------- docs/gtm-handoff.md | 31 ++++++++++----- docs/gtm-quality-prd.md | 1 + docs/launch-plan.md | 2 +- docs/metrics.md | 4 +- docs/self-hosting.md | 76 +++++++++++++++++++++++++++++++++++- website/index.html | 4 +- 7 files changed, 116 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index e5d49e1..c528c19 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,11 @@ go install github.com/runbear-io/beardrive/cmd/bdrive@latest ## Quick start ```sh -# 1. Sign this device in (once). Default server: beardrive.ai; -# self-hosters pass their own URL. -bdrive login +# 1. Sign this device in against your hub (once per device). +# Self-host a hub in ~10 minutes (docs/self-hosting.md), then: +bdrive login https://your-hub +# (BearDrive Cloud — zero-setup, bare `bdrive login` — is coming; +# join the waitlist at beardrive.ai. Self-host to try it today.) # 2. Start syncing a project — interactive: create or connect a project, # sync the whole folder or just ./shared. Re-run any time to resume. @@ -360,77 +362,18 @@ distinct-reader counts, and last-read times — never who read what; `?by=device` adds the agent-only per-device folder breakdown (device identity is already public via history; human emails never appear). -### Authentication +### Authentication & database -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). +Hubs always require sign-in — every change is attributed to a real +account. **Signup is invite-only by default** (the safe posture for a +public URL); self-service signup opens only with a gate (admin approval, +or allowed domains + email verification). Hub metadata (accounts, +projects, orgs, shares) lives in a file-backed store by default, or +SQLite/Postgres (incl. Supabase) via the `database` config block. -**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. 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: +Full reference — the three signup postures, SMTP, admins, CLI device +sign-in, and database selection: **[docs/self-hosting.md](docs/self-hosting.md)**. -- **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 ` 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. ### Uploads diff --git a/docs/gtm-handoff.md b/docs/gtm-handoff.md index 2d36062..47263aa 100644 --- a/docs/gtm-handoff.md +++ b/docs/gtm-handoff.md @@ -25,10 +25,21 @@ Draft copy is ready to paste; adjust voice as you like. bdrive version`, and `go install github.com/runbear-io/beardrive/cmd/bdrive@latest`. If the tap is stale, cut a fresh `goreleaser release` first. +6. **Verify the full first run, not just the binary**: on a clean + machine, `bdrive login && bdrive init && edit a file + && bdrive log` must succeed end to end. Until BearDrive Cloud is + live, every try-path (README, website, launch posts) must route + people to self-hosting — bare `bdrive login` defaults to + beardrive.ai and MUST NOT be advertised until that hub actually + accepts logins. Gate any launch on this check. +7. **Seed 3–5 `good first issue` / `help wanted` issues** from + ROADMAP.md (more agent platforms in `bdrive hooks install`; per-path + access scopes design discussion; demo assets; docs gaps) — + CONTRIBUTING.md promises curated starter issues; make it true. ## P1 — demo assets (an hour) -6. **Record a 45–60s demo GIF/video** for the README hero and any +8. **Record a 45–60s demo GIF/video** for the README hero and any launch post. Script (uses two terminals + a browser): - T1: `bdrive init --name demo --yes` in a folder with a few notes → show `bdrive status` (daemon running). @@ -40,29 +51,31 @@ Draft copy is ready to paste; adjust voice as you like. - T1: `bdrive share wiki/findings.md` → open the public URL. Tools: `vhs` (charmbracelet) or QuickTime + gifski. Put the result at `docs/assets/demo.gif` and add it above the fold in README. -7. **Verify beardrive.ai serves the updated landing page** (the repo's +9. **Verify beardrive.ai serves the updated landing page** (the repo's `website/` — including the new `assets/insights.png`) after the next deploy. ## P2 — when you're ready to be seen (launch window) -8. **Show HN post** — full drafts in [launch-plan.md](launch-plan.md): +10. **Show HN post** — full drafts in [launch-plan.md](launch-plan.md): title, body, and the first-comment founder note. Post from your account; be present for the first 3 hours to answer everything. -9. **Product Hunt** — drafts in launch-plan.md. Schedule after HN, not - the same day. -10. **Claude Code plugin discoverability** — submit/announce the +11. **Product Hunt** — drafts in launch-plan.md. Schedule after HN, not + the same day. Before PH: replace the mailto waitlist on the site + with a real form (any hosted waitlist works) — email-composer + friction kills launch-day conversion. +12. **Claude Code plugin discoverability** — submit/announce the marketplace entry wherever Anthropic surfaces community plugins (Discord, awesome-lists PRs from your account). ## P3 — measurement plumbing (see metrics.md) -11. **Turn on GitHub traffic watching**: stars/clones/views are under +13. **Turn on GitHub traffic watching**: stars/clones/views are under Insights → Traffic; consider a weekly note of the numbers in Discussions → Announcements. -12. **Homebrew install counts**: `brew info --analytics +14. **Homebrew install counts**: `brew info --analytics runbear-io/tap/beardrive` (public analytics take ~30 days to appear). -13. **Decide on hub-side opt-in telemetry** (proposal in metrics.md — +15. **Decide on hub-side opt-in telemetry** (proposal in metrics.md — requires your explicit approval before any implementation; nothing is wired today). diff --git a/docs/gtm-quality-prd.md b/docs/gtm-quality-prd.md index 45ac035..ea61cdc 100644 --- a/docs/gtm-quality-prd.md +++ b/docs/gtm-quality-prd.md @@ -67,6 +67,7 @@ fabricated proof) — record such rejections under Won't-fix with reasons. |---|---|---|---|---|---|---|---| | 1 (baseline) | 6 | 4 | 8 | 3 | 3 | 2 | 3 blockers, 3 majors (all fixed this round; screenshots captured from the live demo hub — real UI, not fabricated) | | 2 | 8 | 7 | 9 | 8 | 8 | 8 | 1 major (residual dead-deployment-mode copy ×3 on the site — fixed) + 4 minors fixed; share.png re-captured with real rendered content | +| 3 | 9 | 7 | 9 | 8 | 7 | 9 | 1 major (quickstart defaulted into the not-yet-live beardrive.ai — quickstart/closer rewritten to self-host-first, first-run gate added to handoff) + README trimmed 583→524, starter-issues & waitlist handoffs added | ## Won't-fix / disputed diff --git a/docs/launch-plan.md b/docs/launch-plan.md index 9b559bc..4b36e5a 100644 --- a/docs/launch-plan.md +++ b/docs/launch-plan.md @@ -39,7 +39,7 @@ CLAUDE.md's invariants. Answer every comment for the first 3 hours. **Prep checklist:** install path verified from a clean machine (`brew install runbear-io/tap/beardrive && bdrive version` — a 404 tap on -launch day is fatal), demo GIF live in README (handoff #6), Discussions +launch day is fatal), full first-run verified against a working hub (handoff #6), demo GIF live in README (handoff #8), Discussions enabled, `docs/self-hosting.md` linked from README, hub demo instance warm (expect self-host attempts within minutes). diff --git a/docs/metrics.md b/docs/metrics.md index 0b26e91..6264029 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -17,8 +17,8 @@ within a week. Everything else is upstream of it. | # | Metric | Source | Stage | |---|---|---|---| -| 1 | README/landing views → GitHub stars | GitHub Insights → Traffic (handoff #11) | attention | -| 2 | Installs: brew + `go install` | brew analytics (handoff #12); go proxy stats are noisy — treat as directional | acquisition | +| 1 | README/landing views → GitHub stars | GitHub Insights → Traffic (handoff #13) | attention | +| 2 | Installs: brew + `go install` | brew analytics (handoff #14); go proxy stats are noisy — treat as directional | acquisition | | 3 | Activation: `bdrive init` → first successful sync | today: anecdotal/self-reported; future: opt-in telemetry (below) | activation | | 4 | Team formation: project gains a 2nd member (invite redeemed) | hub data (self-hosted: invisible to us; beardrive.ai once live) | expansion | | 5 | Wedge proof: first agent read-telemetry event in a project | hub read ledger (same visibility caveat) | wedge | diff --git a/docs/self-hosting.md b/docs/self-hosting.md index b7c6277..df3eaff 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -41,8 +41,8 @@ hub over HTTPS. } ``` -Full knob reference (allowed domains, email verification via SMTP, -admin approval, Postgres/Supabase, share-link rate limits): the +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 @@ -70,6 +70,78 @@ as bearer tokens. For containers, the repo ships a `Dockerfile` copy-paste setup for Claude Code/Cowork, Hermes, and Codex; or run `bdrive hooks install` in the folder. +## 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. 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 ` 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 diff --git a/website/index.html b/website/index.html index 6f5632a..b77a9dd 100644 --- a/website/index.html +++ b/website/index.html @@ -494,8 +494,8 @@ every tool works content-addressed blobs append-only, no locks, offl