Adding a path to .bdriveignore only stopped future uploads: anything that synced before the rule existed stayed on the hub forever, with no command that removed it without deleting it from local disk on every device. Two engine changes make an explicit removal safe: - materialize's delete loop now consults the filter. A cached path absent from the replayed target that the rules exclude is dropped from tracking instead of unlinked — without this, any delete op for a now-filtered path wipes every peer's local copy, which is the data loss this issue is about. - the filter is reloaded mid-cycle from the pulled .bdriveignore, before materialize. A peer receiving the new rules and the deletes they justify in one batch would otherwise materialize with stale rules and the guard would never fire. materialize's write side is split into materializeFile so the ignore file can land on its own. On top of that, Session.Prune journals a delete for every path the replayed state still holds that the SHARED rules exclude — reconciling against the replay, not the local cache, because a path filtered out in an earlier cycle was dropped from the cache back then and is invisible locally today. The rules are deliberately ignore-only: the include scope lives in each device's own .bdrive/config.json and does not sync, so pruning against it would let a narrow-scope device delete a whole-folder teammate's files. Plain `bdrive sync` and the daemon are unchanged — pruning is never a side effect of editing .bdriveignore. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs.beardrive.ai
The public product documentation: CLI, sync model, self-hosting. Astro + Starlight, static output, Pagefind search.
npm install
npm run dev # http://localhost:4321
npm run build # -> dist/
npm run preview
Why this is a standalone site
Unlike the hub frontend (internal/webapp/static) and the cloud landing page
(cloud/internal/landing/dist), this is not embedded into a Go binary:
- Docs change far more often than the binary. Embedding them would mean a Go rebuild and redeploy to fix a typo.
- A Pagefind search index has no business shipping inside every self-hoster's install.
It lives in the OSS repo because that is what it documents. "Edit this page" resolves to something an outside contributor can open a PR against.
Design tokens
scripts/tokens.mjs reads the @theme block in
internal/webapp/frontend/src/tw.css — the source of truth for the BearDrive
palette — and emits src/styles/tokens.gen.css. That file is generated,
gitignored, and regenerated by npm run dev and npm run build, so the palette
cannot drift. src/styles/custom.css maps Starlight's --sl-color-* variables
onto it and invents no colors of its own.
(The cloud landing page can't do this — it sits in a different Go module and
keeps a copy of the tokens, policed by its own check-tokens.mjs.)
Adding a page
Drop a .md file under src/content/docs/<section>/ with title and
description frontmatter, then add its slug to the sidebar in
astro.config.mjs. Sidebar order is explicit, not alphabetical.
Write description for every page: it is the meta description, the search
result snippet, and what llms.txt shows.
llms.txt
starlight-llms-txt generates /llms.txt, /llms-small.txt, and
/llms-full.txt at build time.
Convention puts llms.txt at the root domain, not a docs subdomain — so
beardrive.ai/llms.txt should redirect or proxy to docs.beardrive.ai/llms.txt.
That redirect lives in the cloud landing page and is the one cross-repo
coordination point this split introduces.
Structure
The sidebar order in astro.config.mjs is the recommended path, and the
recommended path is agent-first:
- Start here — what it is, set up with your agent, first hour. No
brew installappears in this group. - Working with agents — the workflows the product exists for.
- Manual setup (optional) — the CLI route: install, set up by hand, skills and hooks in detail. Same destination, more steps; one click away, never on the critical path.
- Use cases — job-shaped titles ("Share work across your team's agents"),
persona named in the first line and in the
description. These pages ROUTE: who it's for, what you get, the one setup difference, links out. The moment one starts teaching a feature, it links to the guide that owns it instead. - Self-hosting, Reference, Concepts — unchanged in intent.
Keep new onboarding content out of Manual. If a page teaches someone how to get started, it belongs in Start here and should say what to ask an agent, not what to type.
Deploying
Static output in dist/. Any static host works; build command npm run build,
output directory dist, project root web/docs.
Redirects
The docs were reorganized around the agent-first path, so three old URLs moved:
| Old | New |
|---|---|
/start/install |
/manual/install/ |
/start/quickstart |
/manual/setup-by-hand/ |
/guides/connect-an-agent |
/start/setup/ |
astro.config.mjs declares these, which in a static build emits meta-refresh
pages — fine for humans, weak for search engines. Real 301s belong in the host.
Firebase Hosting (simplest static option on GCP — CDN, TLS, and custom domains included):
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*"],
"redirects": [
{ "source": "/start/install", "destination": "/manual/install/", "type": 301 },
{ "source": "/start/quickstart", "destination": "/manual/setup-by-hand/", "type": 301 },
{ "source": "/guides/connect-an-agent", "destination": "/start/setup/", "type": 301 }
]
}
}
Cloud Storage behind an external Application Load Balancer: put the rules in the URL map, which redirects before the bucket is ever reached.
gcloud compute url-maps edit docs-url-map # pathMatchers[].pathRules[]:
# - paths: ["/start/install"]
# urlRedirect:
# pathRedirect: "/manual/install/"
# redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
# stripQuery: false
Whichever host wins, keep the Astro redirects block as well: it is the
portable fallback, and it keeps local npm run preview honest.
Note that the build reads a file outside web/docs (the token source), so
the host must check out the whole repository rather than just this
subdirectory.