mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
packaging(BEA-49): draft Umbrel/CasaOS/TrueNAS app-store manifests for the hub
Thin, per-store manifests wrapping the existing root Dockerfile so a self-hosted BearDrive hub installs as a one-click app. All three share the same static binary on distroless; none change the core image. Runtime invocation validated by smoke-testing the built binary directly (serve --remote file://... --addr :8080 --upload -c config.json): web UI returns HTTP 200, /login + /signup reachable, sqlite hub.db + file storage created on disk. DRAFT — blocked on two prerequisites documented in packaging/README.md: 1. no published multi-arch container image (image: is a placeholder) 2. no real Umbrel/CasaOS/TrueNAS instance for the store-required install test Install defaults (admin email via config-only auth.admins, port 8080/4173, file:// storage, sqlite) documented for the listing copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6f0f474903
commit
a3c101a31b
@@ -0,0 +1,91 @@
|
||||
# Self-hosted app-store packaging (Umbrel / CasaOS / TrueNAS)
|
||||
|
||||
Thin, per-store manifests that wrap the repo's existing hub image (built from
|
||||
the root [`Dockerfile`](../Dockerfile) — distroless, CGO-free) so a BearDrive
|
||||
**hub** installs as a one-click app in the self-hosted app-store catalogs.
|
||||
None of these change the core image.
|
||||
|
||||
| Dir | Store | Upstream PR target | Status |
|
||||
|---|---|---|---|
|
||||
| [`umbrel/`](umbrel/) | Umbrel App Store | `getumbrel/umbrel-apps` | **DRAFT — blocked** (see below) |
|
||||
| [`casaos/`](casaos/) | CasaOS App Store | `IceWhaleTech/CasaOS-AppStore` | **DRAFT — blocked** |
|
||||
| [`truenas/`](truenas/) | TrueNAS community | `truenas/apps` | **DRAFT — blocked** |
|
||||
|
||||
> `packaging/homebrew` is the existing sibling — the CLI distribution manifest.
|
||||
|
||||
## ⚠️ Two blockers before any of these can be PR'd upstream
|
||||
|
||||
These manifests are **NOT yet known-good**. Both stores' contributing guides
|
||||
require a real-instance install test, and all three reference a published
|
||||
container image that does not exist yet.
|
||||
|
||||
1. **No published container image.** Every store installs by pulling a public,
|
||||
versioned, multi-arch image by tag. The repo publishes **none** — CI
|
||||
(`.github/workflows/{ci,bump-cloud,docs}.yml`) has no image-publish job, and
|
||||
the only build path is `deploy/gcp-cloudrun.sh` → a **private** GCP Artifact
|
||||
Registry for Cloud Run. The `image:` fields below point at a **placeholder**
|
||||
`ghcr.io/runbear-io/beardrive:<version>` that must first be built and pushed
|
||||
(multi-arch `linux/amd64` + `linux/arm64` — Umbrel runs on Raspberry Pi).
|
||||
Owner: **CTO/eng** (tracked as the child issue filed off BEA-49).
|
||||
|
||||
2. **No real-instance test environment.** Each store's guide requires the app
|
||||
be installed + reach its web UI on an actual instance of that platform
|
||||
(Umbrel OS / CasaOS / TrueNAS SCALE) before the PR is accepted. Eng has
|
||||
Docker (the underlying image is smoke-tested — see below) but **no
|
||||
Umbrel/CasaOS/TrueNAS instances and no local VM tooling**
|
||||
(qemu/vagrant/multipass absent; Docker Desktop on macOS can't practically
|
||||
nest those OSes). Owner: **CEO** — needs cloud-VM budget/credentials or a
|
||||
hosted test env. Untested PRs get rejected and burn reviewer goodwill, so
|
||||
we hold rather than ship blind (per BEA-49's "Known risk" instruction).
|
||||
|
||||
## What IS verified (Docker smoke test, not a store instance)
|
||||
|
||||
The underlying hub image was built from the root `Dockerfile` and run locally
|
||||
under Docker with the self-host config below; the web UI was confirmed
|
||||
reachable. This validates the **runtime invocation and defaults** the manifests
|
||||
encode — it does **not** substitute for the per-store real-instance test.
|
||||
|
||||
## Install defaults chosen (for the listing copy — CMO)
|
||||
|
||||
The hub is configured by flags **plus** a JSON config file. Critically, the
|
||||
**admin email can only be set via the config file's `auth.admins`** — there is
|
||||
no `--admins` flag — and the image is **distroless (no shell)**, so each
|
||||
manifest uses a tiny `busybox` init container to render `/data/config.json`
|
||||
from the user-supplied admin email into the shared data volume before the hub
|
||||
starts. The core image is untouched.
|
||||
|
||||
| Setting | Value | Notes |
|
||||
|---|---|---|
|
||||
| **Admin email** | user-supplied; placeholder `admin@example.com` | `auth.admins`. On a zero-account hub this email can self-sign-up first (invite-only afterward). |
|
||||
| **Listen port (container)** | `8080` (`--addr :8080`) | Suggested host/web port `4173` (matches `docs/self-hosting.md`). |
|
||||
| **Storage backend** | `file:///data/storage` | Local filesystem blob+journal store — the simplest self-host backend (no S3/GCS creds). |
|
||||
| **Metadata DB** | sqlite at `/data/hub.db` | `database.driver = sqlite`. |
|
||||
| **Users DB** | `/data/auth.json` | `auth.users_db`. |
|
||||
| **Uploads** | enabled (`--upload`) | A hub must accept client uploads. |
|
||||
| **Read analytics** | enabled (`reads.enabled`) | Powers the Insights dashboard. |
|
||||
| **Device/home state** | `BDRIVE_HOME=/data/home` | Overrides the image default `/tmp/bdrive` (ephemeral) so identity persists. |
|
||||
| **Persistent volume** | one dir mounted at `/data` | Holds `storage/`, `home/`, `hub.db`, `auth.json`, `config.json`. |
|
||||
|
||||
Rendered config (only the parts flags can't set — flags supply remote/addr/upload):
|
||||
|
||||
```json
|
||||
{
|
||||
"auth": { "admins": ["admin@example.com"], "users_db": "/data/auth.json" },
|
||||
"reads": { "enabled": true },
|
||||
"database": { "driver": "sqlite", "dsn": "/data/hub.db" }
|
||||
}
|
||||
```
|
||||
|
||||
Launch command: `serve --remote file:///data/storage --addr :8080 --upload -c /data/config.json`
|
||||
|
||||
## Remaining per-store work (needs a real instance)
|
||||
|
||||
- **UID/permissions:** distroless runs as nonroot UID **65532**; the init
|
||||
container `chown`s `/data` to match. Each platform mounts app-data with its
|
||||
own owner (Umbrel commonly 1000) — must be verified live.
|
||||
- **TrueNAS:** `truenas/apps` expects the full `ix-dev` chart scaffold
|
||||
(`app.yaml` + `questions.yaml` + `templates/`); the draft here is the core,
|
||||
and the complete scaffold should be generated + validated with their tooling
|
||||
on a real TrueNAS SCALE box.
|
||||
- **Health checks, port-conflict defaults, gallery assets** per each store's
|
||||
lint/CI.
|
||||
@@ -0,0 +1,91 @@
|
||||
# BearDrive hub — CasaOS App Store manifest (DRAFT, see ../README.md).
|
||||
# PR target: IceWhaleTech/CasaOS-AppStore. Guide requires it be "well tested on
|
||||
# your own CasaOS first" — BLOCKED on a published image + a real CasaOS test.
|
||||
name: beardrive
|
||||
services:
|
||||
config-init:
|
||||
# Distroless has no shell; render config + fix ownership, then exit.
|
||||
image: busybox:1.36
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /DATA/AppData/beardrive
|
||||
target: /data
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
mkdir -p /data/storage /data/home
|
||||
if [ ! -f /data/config.json ]; then
|
||||
cat > /data/config.json <<EOF
|
||||
{
|
||||
"auth": { "admins": ["${ADMIN_EMAIL:-admin@example.com}"], "users_db": "/data/auth.json" },
|
||||
"reads": { "enabled": true },
|
||||
"database": { "driver": "sqlite", "dsn": "/data/hub.db" }
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
chown -R 65532:65532 /data
|
||||
|
||||
beardrive:
|
||||
# PLACEHOLDER — image not published yet (see ../README.md, blocker 1).
|
||||
image: ghcr.io/runbear-io/beardrive:0.1.0
|
||||
container_name: beardrive
|
||||
restart: unless-stopped
|
||||
user: "65532:65532"
|
||||
depends_on:
|
||||
config-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
BDRIVE_HOME: /data/home
|
||||
ports:
|
||||
- "4173:8080"
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /DATA/AppData/beardrive
|
||||
target: /data
|
||||
command:
|
||||
- serve
|
||||
- --remote
|
||||
- file:///data/storage
|
||||
- --addr
|
||||
- :8080
|
||||
- --upload
|
||||
- -c
|
||||
- /data/config.json
|
||||
|
||||
# CasaOS App Store metadata extension.
|
||||
x-casaos:
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
main: beardrive
|
||||
author: Runbear
|
||||
developer: Runbear
|
||||
category: Cloud
|
||||
icon: https://raw.githubusercontent.com/runbear-io/beardrive/main/docs/assets/icon.png
|
||||
title:
|
||||
en_us: BearDrive
|
||||
tagline:
|
||||
en_us: Google Drive for AI agents — self-hosted sync hub
|
||||
description:
|
||||
en_us: >-
|
||||
Self-hosted BearDrive hub: sync any folder across your devices,
|
||||
teammates, and AI agents with per-change provenance and read
|
||||
analytics. Local filesystem storage + sqlite; clients connect with
|
||||
`bdrive login`. Open source (AGPL-3.0).
|
||||
port_map: "4173"
|
||||
scheme: http
|
||||
index: /
|
||||
webui_port: 4173
|
||||
envs:
|
||||
- container: ADMIN_EMAIL
|
||||
description:
|
||||
en_us: >-
|
||||
Email that becomes the hub admin. On a brand-new hub this address
|
||||
can self-sign-up first; the hub is invite-only afterward.
|
||||
volumes:
|
||||
- container: /data
|
||||
description:
|
||||
en_us: BearDrive data — file storage, sqlite metadata, and config.
|
||||
@@ -0,0 +1,35 @@
|
||||
# BearDrive hub — TrueNAS Apps metadata (DRAFT, see ../README.md).
|
||||
# PR target: truenas/apps (community train). BLOCKED on a published image and a
|
||||
# real TrueNAS SCALE install test. The full ix-dev chart scaffold
|
||||
# (questions.yaml + templates/) must be generated + validated with TrueNAS
|
||||
# tooling on a real SCALE instance; questions.yaml and templates/ here are the
|
||||
# core, not the complete lint-passing scaffold.
|
||||
name: beardrive
|
||||
title: BearDrive
|
||||
train: community
|
||||
categories:
|
||||
- cloud
|
||||
- productivity
|
||||
app_version: "0.1.0"
|
||||
version: 0.0.1
|
||||
home: https://beardrive.ai
|
||||
sources:
|
||||
- https://github.com/runbear-io/beardrive
|
||||
- https://docs.beardrive.ai
|
||||
description: >-
|
||||
Self-hosted BearDrive hub — Google Drive for AI agents. Sync any folder
|
||||
across devices, teammates, and AI agents with per-change provenance and read
|
||||
analytics. Local filesystem storage + sqlite; clients connect with
|
||||
`bdrive login`. Open source (AGPL-3.0).
|
||||
maintainers:
|
||||
- name: Runbear
|
||||
url: https://beardrive.ai
|
||||
email: hello@runbear.io
|
||||
run_as_context:
|
||||
- description: BearDrive runs as the distroless nonroot user.
|
||||
gid: 65532
|
||||
group_name: nonroot
|
||||
uid: 65532
|
||||
user_name: nonroot
|
||||
capabilities: []
|
||||
host_mounts: []
|
||||
@@ -0,0 +1,58 @@
|
||||
# BearDrive hub — TrueNAS config questions (DRAFT core, see ../README.md).
|
||||
# Minimal set; the full questions.yaml (resources, DNS, etc.) is generated by
|
||||
# the ix-dev scaffold and must be validated on a real TrueNAS SCALE instance.
|
||||
groups:
|
||||
- name: BearDrive Configuration
|
||||
description: Hub admin and web access.
|
||||
- name: Storage Configuration
|
||||
description: Where BearDrive keeps files, metadata, and config.
|
||||
questions:
|
||||
- variable: beardrive
|
||||
label: ""
|
||||
group: BearDrive Configuration
|
||||
schema:
|
||||
type: dict
|
||||
attrs:
|
||||
- variable: admin_email
|
||||
label: Admin Email
|
||||
description: >-
|
||||
Email that becomes the hub admin. On a brand-new hub this address
|
||||
can self-sign-up first; the hub is invite-only afterward.
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
default: admin@example.com
|
||||
- variable: network
|
||||
label: ""
|
||||
group: BearDrive Configuration
|
||||
schema:
|
||||
type: dict
|
||||
attrs:
|
||||
- variable: web_port
|
||||
label: Web Port
|
||||
schema:
|
||||
type: int
|
||||
default: 4173
|
||||
required: true
|
||||
- variable: storage
|
||||
label: ""
|
||||
group: Storage Configuration
|
||||
schema:
|
||||
type: dict
|
||||
attrs:
|
||||
- variable: data
|
||||
label: BearDrive Data
|
||||
description: File storage, sqlite metadata, and config (mounted at /data).
|
||||
schema:
|
||||
type: dict
|
||||
attrs:
|
||||
- variable: type
|
||||
label: Type
|
||||
schema:
|
||||
type: string
|
||||
default: ix_volume
|
||||
enum:
|
||||
- value: ix_volume
|
||||
description: ixVolume (dataset created by TrueNAS)
|
||||
- value: host_path
|
||||
description: Host Path (existing dataset)
|
||||
@@ -0,0 +1,55 @@
|
||||
# BearDrive hub — TrueNAS compose template (DRAFT, see ../../README.md).
|
||||
# Shown resolved (concrete values) for readability; in the real ix-dev chart
|
||||
# these become {{ .Values... }} template expressions bound to questions.yaml.
|
||||
# BLOCKED on a published image + a real TrueNAS SCALE test.
|
||||
services:
|
||||
config-init:
|
||||
# Distroless has no shell; render config + fix ownership, then exit.
|
||||
image: busybox:1.36
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /mnt/.ix-apps/app_mounts/beardrive/data # ix_volume in the real chart
|
||||
target: /data
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
mkdir -p /data/storage /data/home
|
||||
if [ ! -f /data/config.json ]; then
|
||||
cat > /data/config.json <<EOF
|
||||
{
|
||||
"auth": { "admins": ["admin@example.com"], "users_db": "/data/auth.json" },
|
||||
"reads": { "enabled": true },
|
||||
"database": { "driver": "sqlite", "dsn": "/data/hub.db" }
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
chown -R 65532:65532 /data
|
||||
|
||||
beardrive:
|
||||
# PLACEHOLDER — image not published yet (see ../../README.md, blocker 1).
|
||||
image: ghcr.io/runbear-io/beardrive:0.1.0
|
||||
restart: unless-stopped
|
||||
user: "65532:65532"
|
||||
depends_on:
|
||||
config-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
BDRIVE_HOME: /data/home
|
||||
ports:
|
||||
- "4173:8080"
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /mnt/.ix-apps/app_mounts/beardrive/data
|
||||
target: /data
|
||||
command:
|
||||
- serve
|
||||
- --remote
|
||||
- file:///data/storage
|
||||
- --addr
|
||||
- :8080
|
||||
- --upload
|
||||
- -c
|
||||
- /data/config.json
|
||||
@@ -0,0 +1,61 @@
|
||||
# BearDrive hub — Umbrel App Store manifest (DRAFT, see ../README.md).
|
||||
# PR target: getumbrel/umbrel-apps. BLOCKED on a published multi-arch image
|
||||
# and a real Umbrel-instance install test.
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
# Umbrel's reverse proxy that fronts the app's web UI.
|
||||
app_proxy:
|
||||
environment:
|
||||
APP_HOST: beardrive_hub_1
|
||||
APP_PORT: 8080
|
||||
|
||||
# Distroless has no shell, so a tiny init container renders the hub config
|
||||
# (the admin email lives only in the config file — there is no --admins flag)
|
||||
# and fixes ownership for the nonroot (65532) hub user. Runs once, then exits.
|
||||
config-init:
|
||||
image: busybox:1.36
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- ${APP_DATA_DIR}/data:/data
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
mkdir -p /data/storage /data/home
|
||||
if [ ! -f /data/config.json ]; then
|
||||
cat > /data/config.json <<EOF
|
||||
{
|
||||
"auth": { "admins": ["${ADMIN_EMAIL:-admin@example.com}"], "users_db": "/data/auth.json" },
|
||||
"reads": { "enabled": true },
|
||||
"database": { "driver": "sqlite", "dsn": "/data/hub.db" }
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
chown -R 65532:65532 /data
|
||||
|
||||
hub:
|
||||
# PLACEHOLDER — image is not published yet (see ../README.md, blocker 1).
|
||||
image: ghcr.io/runbear-io/beardrive:0.1.0
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
config-init:
|
||||
condition: service_completed_successfully
|
||||
user: "65532:65532"
|
||||
environment:
|
||||
# Persist device identity / projects.json (image default /tmp/bdrive is ephemeral).
|
||||
BDRIVE_HOME: /data/home
|
||||
volumes:
|
||||
- ${APP_DATA_DIR}/data:/data
|
||||
command:
|
||||
- serve
|
||||
- --remote
|
||||
- file:///data/storage
|
||||
- --addr
|
||||
- :8080
|
||||
- --upload
|
||||
- -c
|
||||
- /data/config.json
|
||||
ports:
|
||||
- "8080"
|
||||
@@ -0,0 +1,30 @@
|
||||
# BearDrive hub — Umbrel app metadata (DRAFT). Final id/gallery/port assigned
|
||||
# at PR time per getumbrel/umbrel-apps conventions.
|
||||
manifestVersion: 1
|
||||
id: beardrive
|
||||
category: files
|
||||
name: BearDrive
|
||||
version: "0.1.0"
|
||||
tagline: Google Drive for AI agents — self-hosted sync hub
|
||||
description: >-
|
||||
BearDrive mounts any folder as a synced volume shared across your devices,
|
||||
teammates, and AI agents. Every change is attributed to the human, agent, and
|
||||
device that made it; any synced file can become a public page; and the
|
||||
Dashboard shows what your agents actually read. This app runs your own
|
||||
self-hosted hub (local filesystem storage + sqlite) — clients connect with
|
||||
`bdrive login http://<your-umbrel>:<port>`. Open source (AGPL-3.0).
|
||||
developer: Runbear
|
||||
website: https://beardrive.ai
|
||||
dependencies: []
|
||||
repo: https://github.com/runbear-io/beardrive
|
||||
support: https://github.com/runbear-io/beardrive/issues
|
||||
port: 4173
|
||||
gallery:
|
||||
- 1.jpg
|
||||
- 2.jpg
|
||||
- 3.jpg
|
||||
path: ""
|
||||
defaultUsername: ""
|
||||
defaultPassword: ""
|
||||
submitter: Runbear
|
||||
submission: https://github.com/getumbrel/umbrel-apps/pull/REPLACE_AT_PR_TIME
|
||||
Reference in New Issue
Block a user