From 4bc2f56d345fcf024519e76d5b91894d8e188d6e Mon Sep 17 00:00:00 2001 From: m3ultra Date: Fri, 17 Jul 2026 22:24:18 +1000 Subject: [PATCH] README: local-first rewrite + ignore the .env.* family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README still read as a fal.ai front-end with a local afterthought: the headline `mb` example ran fal_trellis2, the "recommended chain for best image→3D" was four fal_* calls, and the operator table listed 5 local ops against 9 cloud ones. That is backwards now — 22 of the 31 operators run entirely on the fleet. It also predated most of what we have: the whole audio/video/text suite (tts_local, stt_local, voice_clone_local, music_local, lipsync_local, wan_video, llm_local, motion_local), comfyui_sd, flux_local, mflux_image_edit, seedvr2_upscale and bg_remove_local were all missing from the catalog entirely. - lead with local; cloud demoted to its own opt-in "costs real money" section - all 31 operators documented, grouped by what you're trying to make - recommended chain is now local: bg_remove_local -> [seedvr2_upscale] -> trellis_mac (quality) or hunyuan3d_mlx (faster, ~3x lighter, the only one that runs on M1) - setup section reordered: most local ops need nothing, hunyuan3d_mlx needs no HF login at all, only sf3d/trellis_mac/bg_remove_local are gated - lanes corrected: gpu+cpu are a NODE POOL (gpu 1/node, cpu per-node cpu_slots), not the old flat "gpu=1, cpu=3, net=6" - manifest `python` documented as repo-relative (remote nodes can't resolve absolute) - link PERFCHECK.md; drop the stale "on the M3 Ultra" framing — it's a fleet - verified: every operator on disk appears, no phantom ids, no broken doc links Also: .gitignore had `*.env` and `.env`, neither of which matches a suffixed name like `.env.remote` — which is sitting un-ignored in the m1's checkout right now, one `git add -A` away from being committed. Ignore the `.env.*` family (keeping .env.example committable). Nothing has leaked: no .env file is tracked. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 5 ++ README.md | 151 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 119 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 7427ab7..d489547 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,11 @@ web/dist/ # Secrets & local config *.env .env +# `.env` matches only the exact name and `*.env` only matches names ENDING in .env — neither +# catches a suffixed one like `.env.remote` (which sits un-ignored on the m1 today). Secrets +# live in .env files by convention here, so ignore the whole family; keep examples committable. +.env.* +!.env.example server/.env # Large model weights / caches diff --git a/README.md b/README.md index 85d0daa..3b37cb7 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,148 @@ # MODELBEAST -Local-first web app that turns videos, images, and 3D files into meshes, splats, mocap, and rigged characters on the M3 Ultra. See [PLAN.md](PLAN.md) for the full verified tool matrix and roadmap, and [HANDOFF.md](HANDOFF.md) for the agent build brief (phases 1–4 instructions). +**Local-first generation farm for a fleet of Macs.** Video, image, audio, text or 3D in → +meshes, splats, images, video, speech, music, motion and text out. Everything below runs *on +your own machines* — nothing leaves the tailnet unless you explicitly pick a `fal_*` or +`openrouter_*` operator, and guests can't pick those at all. -> 🖥️ **Multi-Mac fleet? Read [CLUSTER.md](CLUSTER.md) first** — the local-first policy, the M3/M1/M4 node roles, and how the central queue routes jobs across machines. +> 🖥️ **Multi-Mac fleet? Read [CLUSTER.md](CLUSTER.md) first** — the local-first policy, node roles, and how the central queue routes jobs across machines. > 💾 **What runs on which Mac?** [HARDWARE.md](HARDWARE.md) — min/recommended RAM per operator across every Apple Silicon tier (8GB → 256GB). +> 📊 **Is the fleet healthy / how fast is it?** [PERFCHECK.md](PERFCHECK.md) — nightly GPU canary + per-node capability table. [BENCHMARKS.md](BENCHMARKS.md) — what each model actually costs. > 🎬 **Green-screen keying → 3D:** [CORRIDORKEY.md](CORRIDORKEY.md) — CorridorKey status, our Ultra tuning, and the video→cutout→mesh/splat use-case map. +See [PLAN.md](PLAN.md) for the verified tool matrix and roadmap, [HANDOFF.md](HANDOFF.md) for the +agent build brief. + ## Run ```bash /opt/homebrew/bin/uv run uvicorn server.main:app --host 0.0.0.0 --port 8777 ``` -Open http://localhost:8777 (or `http://:8777` from any device on the tailnet). Prefer `scripts/serve.sh` — it runs headless and prints the one-time owner password on first start. +Open http://localhost:8777 (or `http://:8777` from any device on the tailnet). +Prefer `scripts/serve.sh` — it runs headless and prints the one-time owner password on first start. -Drag/drop/paste any video, image, or 3D file; or drop files into `data/inbox/` for auto-ingest. Pick an operator, tune its parameters, run. Add API keys / HuggingFace token under ⚙ Settings. Use ⊞ Compare to view several outputs side by side, and **📊 Dashboard** for a live CPU/RAM/GPU/queue snapshot. +Drag/drop/paste any video, image, audio or 3D file; or drop files into `data/inbox/` for +auto-ingest. Pick an operator, tune its parameters, run. Several operators (`flux_local`, +`comfyui_sd`, `tts_local`, `llm_local`, `music_local`, `motion_local`) need **no input asset** — +just a prompt. Use ⊞ Compare to view outputs side by side, and **📊 Dashboard** for a live +CPU/RAM/GPU/queue snapshot. ## Accounts & sharing -Login is required. The first `scripts/serve.sh` creates an **owner** (`monster`) and prints a one-time password to `data/server.log` — change it in ⚙ Settings → Users (or `uv run python scripts/users.py passwd monster`). The owner can add **guest** accounts for friends: guests are **local-only** — they can run only on-device operators (never the owner's paid fal/OpenRouter keys, enforced server-side), see only their own assets/jobs, and have a per-user concurrent-job cap. So mates cost you electricity, nothing else. Public hosting via a VPS + Caddy is in [docs/VPS.md](docs/VPS.md); the CUDA-worker offload plan is in [docs/CUDA-WORKER.md](docs/CUDA-WORKER.md). +Login is required. The first `scripts/serve.sh` creates an **owner** (`monster`) and prints a +one-time password to `data/server.log` — change it in ⚙ Settings → Users (or `uv run python +scripts/users.py passwd monster`). The owner can add **guest** accounts for friends: guests are +**local-only** — they can run only on-device operators (never the owner's paid keys, enforced +server-side), see only their own assets/jobs, and have a per-user concurrent-job cap. So mates +cost you electricity, nothing else. Public hosting via a VPS + Caddy is in +[docs/VPS.md](docs/VPS.md); the CUDA-worker offload plan is in [docs/CUDA-WORKER.md](docs/CUDA-WORKER.md). ## Headless / agents -Everything is driveable without the UI via the zero-dependency [`mb`](mb) CLI (works from any tailnet machine with `MB_HOST=http://100.89.131.57:8777`) or raw REST. **[AGENTS.md](AGENTS.md) is the complete handover brief** — endpoints, `mb` reference, operator catalog, recipes, job semantics, etiquette. Server ops: `scripts/serve.sh` (start/restart headless), `scripts/install_launchagent.sh` (optional boot persistence, owner-run). +Everything is driveable without the UI via the zero-dependency [`mb`](mb) CLI (works from any +tailnet machine) or raw REST. **[AGENTS.md](AGENTS.md) is the complete handover brief** — +endpoints, `mb` reference, operator catalog, recipes, job semantics, etiquette. Server ops: +`scripts/serve.sh` (start/restart headless), `scripts/install_launchagent.sh` (optional boot +persistence, owner-run). ```bash export MB_HOST=http://100.89.131.57:8777 MB_TOKEN=mbt_... # token from ⚙ Settings → Users -./mb run fal_trellis2 --file photo.jpg -p resolution=1024 --wait --download out/ +./mb run flux_local -p prompt="a brass astrolabe on velvet" --wait --download out/ +./mb run bg_remove_local --file photo.jpg --wait --download out/ +./mb run hunyuan3d_mlx --file cutout.png --wait --download out/ ``` -## Develop - -- Backend: `server/` — FastAPI + SQLite (`data/modelbeast.db`), job runner runs operators as subprocesses across concurrency lanes (gpu=1, cpu=3, net=6). Settings/secrets in `server/settings.py` (env-injected, log-redacted). -- Frontend: `web/` — React + Vite + three.js + `@mkkellogg/gaussian-splats-3d`. After editing: `cd web && npm run build` (the server serves `web/dist`). -- Data: `data/assets/` (store), `data/jobs/` (job workdirs), `data/inbox/` (watch folder). Delete `data/` to reset. Tests use `MODELBEAST_DATA=`. -- Tests: `./tests/smoke.sh` (12 framework checks). Benchmarks in [BENCHMARKS.md](BENCHMARKS.md). -- Heavy tools live in `vendor/` (repos) + `venvs/` (per-tool envs), both gitignored. Reinstall with `scripts/install_*.sh`. - -## Setup for the local mesh generators (one-time) - -`sf3d` and `trellis_mac` are installed but their weights are HuggingFace-gated. Accept the licenses (stabilityai/stable-fast-3d, facebook/dinov3-vitl16-pretrain-lvd1689m, briaai/RMBG-2.0), then `huggingface-cli login` or paste an HF token in Settings. Cloud `fal_*` operators need `FAL_KEY` in Settings. - ## Operators -Each subfolder of `server/operators/` with a `manifest.json` is an operator. The UI auto-renders its parameter form from `params_schema` (JSON Schema) and filters by the selected asset's kind (`accepts`). +Each subfolder of `server/operators/` with a `manifest.json` is an operator. The UI auto-renders +its parameter form from `params_schema` (JSON Schema) and filters by the selected asset's kind +(`accepts`). -Contract: the runner invokes ` run.py --input --outdir --params ''`. Write outputs into the outdir; optionally write `result.json` (`{"outputs": [{"path": ..., "name": ..., "meta": ...}]}`) to control what gets registered as assets. stdout/stderr become the job log. +### Local — free, offline, on-fleet -Manifest fields: `id, name, category, description, accepts, produces, resources` (gpu/cpu/net lane), `requires_env` (gates the operator until the key is set), `python` (absolute venv path for heavy tools), `params_schema`. Operators can tag output asset kind via `result.json` output `meta.kind` (e.g. `splat`, `colmap_dataset`). +This is the default. Reach for cloud only when local genuinely can't do it. -Current operators: | id | lane | what | |---|---|---| -| `ffprobe` | cpu | media inspection | -| `ffmpeg_frames` | cpu | video → frames (fps, mpdecimate dedupe, blur cull, max cap) | -| `blender_convert` | cpu | universal 3D format conversion via headless Blender | +| **image → 3D** | | | +| `hunyuan3d_mlx` | gpu | image → GLB + PBR (Hunyuan3D 2.1, **native MLX**). Weights public — no HF login. The only local 3D op that runs on M1. ~4.3 min | +| `trellis_mac` | gpu | image → GLB + PBR (TRELLIS.2 MPS port). Best local quality — sharper than hunyuan at defaults. ~5 min *[HF-gated]* | +| `sf3d` | gpu | image → GLB in ~5s. Draft tier — good on solid objects, struggles on thin/open geometry *[HF-gated]* | +| **image gen / edit** | | | +| `flux_local` | gpu | prompt → image via mflux/MLX. FLUX.2 Klein 4B is Apache + ungated, ~9s/image | +| `comfyui_sd` | gpu | prompt → image via local SD/SDXL checkpoints **+ LoRAs** (ComfyUI on Metal). `flux_local` cannot load SD/SDXL — this is the one that can | +| `mflux_image_edit` | gpu | image + instruction → edited image (Qwen-Image-Edit, ungated) | +| `seedvr2_upscale` | gpu | image → faithful upscale (SeedVR2, MIT) — sharpens without inventing detail | +| `bg_remove_local` | gpu | image → subject cutout (RMBG-2.0). **The single biggest quality lever before any image→3D** | +| **video** | | | +| `wan_video` | gpu | prompt → MP4, or image + prompt → MP4 (Wan 2.2 TI2V-5B on the resident ComfyUI). 24fps, 1280×704 | +| **audio / speech** | | | +| `tts_local` | gpu | text → speech (Kokoro-82M, ~50 voices, Apache, ~350MB) | +| `stt_local` | gpu | audio → transcript + timestamps (Whisper large-v3-turbo on MLX) | +| `voice_clone_local` | gpu | ~10s reference voice + text → that voice speaking it (Chatterbox, MIT) | +| `music_local` | gpu | style tags (+ optional lyrics) → music WAV (ACE-Step 3.5B, Apache) | +| `lipsync_local` | cpu | speech WAV → viseme timing JSON (Rhubarb, fully offline) | +| **text / motion** | | | +| `llm_local` | gpu | prompt → text (Qwen3-30B-A3B-4bit MoE on MLX) | +| `motion_local` | cpu | text → character motion as BVH + mp4 preview (MoMask) | +| **scan → splat** | | | +| `ffmpeg_frames` | cpu | video → frames (fps sampling, mpdecimate dedupe, blur cull) | | `colmap_poses` | cpu | frames → camera poses + sparse cloud (COLMAP 4.x + GLOMAP) | -| `brush_train` | gpu | colmap dataset → 3D gaussian splat (Brush, native Metal) | -| `sf3d` | gpu | image → GLB locally (Stable-Fast-3D, MPS) *[HF-gated]* | -| `trellis_mac` | gpu | image → GLB+PBR locally (TRELLIS.2 MPS port) *[HF-gated]* | -| `hunyuan3d_mlx` | gpu | image → GLB+PBR locally (Hunyuan3D 2.1, native MLX — runs on M1 too; weights public, no HF login) | -| `fal_trellis` / `fal_trellis2` / `fal_hunyuan3d` / `fal_hunyuan3d_v21` / `fal_rodin` | net | image → mesh via fal.ai API *[needs FAL_KEY]* | -| `fal_bg_remove` | net | image → subject cutout (BiRefNet v2) — run before any image→3D for a big quality jump | -| `fal_upscale` | net | image → faithful upscale (SeedVR) | -| `fal_image_edit` | net | image + instruction → edited image (nano-banana) | -| `fal_text_image` | net | prompt → image (Ideogram v3, readable text) — no input asset needed | +| `brush_train` | gpu | colmap dataset → gaussian splat .ply (Brush, native Metal) | +| **utility** | | | +| `ffprobe` | cpu | media inspection | +| `blender_convert` | cpu | GLB/GLTF/OBJ/FBX/USD/STL/PLY/BLEND ⇄ conversion via headless Blender | -Recommended fal chain for best image→3D: `fal_bg_remove` → (`fal_upscale` if thin) → `fal_trellis2` or `fal_hunyuan3d_v21`. Note: Hunyuan v21 **multi-view** is broken on fal (verified 2026-07) — v21 is single-image only; use v2 for multi-view. +**Recommended local image→3D chain:** `bg_remove_local` → (`seedvr2_upscale` if thin/blurry) → +`trellis_mac` (quality) or `hunyuan3d_mlx` (faster, ~3× lighter, runs on M1). -Heavy operators get their own uv venv (`"python": ""` in the manifest). Remaining roadmap (object_capture, freemocap, retargeting, tripo/meshy character APIs, workflow presets, LLM copilot) is in [HANDOFF.md](HANDOFF.md) §5–8. +### Cloud — costs real money, opt-in, owner-only + +Use when local can't, or for on-demand SOTA without the local wait. + +| id | lane | what | +|---|---|---| +| `fal_trellis2` / `fal_hunyuan3d_v21` / `fal_hunyuan3d` / `fal_trellis` / `fal_rodin` | net | image → mesh via fal.ai *[FAL_KEY]* | +| `fal_bg_remove` / `fal_upscale` / `fal_image_edit` / `fal_text_image` | net | cutout / upscale / nano-banana edit / Ideogram v3 (readable text) *[FAL_KEY]* | +| `openrouter_image` | net | prompt → image via OpenRouter (nano-banana family; exact cost reported per job) *[OPENROUTER_API_KEY]* | + +Note: fal's Hunyuan **v21 multi-view is broken** (verified 2026-07) — v21 is single-image only; +use v2 for multi-view. `fal_rodin` is the film/hero tier (quad topology, ~$0.40+). + +## Setup for the gated local models (one-time) + +Most local operators work out of the box. Only these need anything: + +- `sf3d`, `trellis_mac` — weights are HuggingFace-gated. Accept the licences + (stabilityai/stable-fast-3d, facebook/dinov3-vitl16-pretrain-lvd1689m), then + `huggingface-cli login` or paste an HF token in ⚙ Settings. +- `bg_remove_local` — briaai/RMBG-2.0 licence, same deal. +- `hunyuan3d_mlx` — **nothing**. Weights are public. +- Cloud operators — `FAL_KEY` / `OPENROUTER_API_KEY` in ⚙ Settings. + +Heavy tools live in `vendor/` (repos) + `venvs/` (per-tool envs), both gitignored — reinstall +with `scripts/install_*.sh`. + +## Develop + +- Backend: `server/` — FastAPI + SQLite (`data/modelbeast.db`), job runner runs operators as subprocesses across concurrency lanes. **gpu and cpu are a node pool**: gpu = 1 job per node, cpu = per-node slots (`cpu_slots` in `nodes.json`; default 3 local / 2 remote), net = 6 on the primary only. Settings/secrets in `server/settings.py` (env-injected, log-redacted). +- Frontend: `web/` — React + Vite + three.js + `@mkkellogg/gaussian-splats-3d`. After editing: `cd web && npm run build` (the server serves `web/dist`). +- Data: `data/assets/` (store), `data/jobs/` (job workdirs), `data/inbox/` (watch folder). Delete `data/` to reset. Tests use `MODELBEAST_DATA=`. +- Tests: `./tests/smoke.sh` (12 framework checks). + +### Writing an operator + +Contract: the runner invokes ` run.py --input --outdir --params ''`. +Write outputs into the outdir; optionally write `result.json` +(`{"outputs": [{"path": ..., "name": ..., "meta": ...}]}`) to control what gets registered as +assets. stdout/stderr become the job log; the runner stamps `[node: ]` as log line 1. + +Manifest fields: `id, name, category, description, accepts, produces, resources` (gpu/cpu/net +lane — defaults to cpu if omitted), `requires_env` (gates the operator until the key is set), +`python` (venv path for heavy tools — **repo-relative**, so remote nodes resolve it), and +`params_schema`. Operators can tag output asset kind via `result.json` output `meta.kind` (e.g. +`splat`, `colmap_dataset`). + +Remaining roadmap (object_capture, freemocap, retargeting, character APIs, workflow presets, LLM +copilot) is in [HANDOFF.md](HANDOFF.md) §5–8.