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