Files
waggle-os/docs/production-readiness/06-BUILD_REPORT.md
Oleg Maslov b20b138fe4 moving
2026-09-02 10:14:22 +02:00

334 lines
14 KiB
Markdown

# Phase 6: Build & Deployment Readiness Report
> **Historical snapshot — superseded.** This report records evidence as of
> 2026-03-20 and is not current launch authority. See
> [`09-LAUNCH_RECOMMENDATION.md`](./09-LAUNCH_RECOMMENDATION.md) for the current
> Windows-first Solo scope, external gates, and recommendation.
**Date**: 2026-03-20
**Auditor**: Claude Opus 4.6 (automated)
**Scope**: TypeScript compilation, Vite build, Docker, Tauri, Render.com, npx launcher, CI/CD
---
## Deployment Mode Readiness Matrix
| Mode | Status | Evidence | Blockers |
|------|--------|----------|----------|
| **Web (Vite)** | :warning: BUILDS with warnings | 735 kB single chunk, 87 TS errors (non-blocking) | Chunk splitting needed; 87 type errors in app/ scope |
| **Docker** | :warning: MOSTLY READY | Multi-stage build, health checks, compose valid | No non-root user; no code signing |
| **Tauri Windows** | :warning: COMPILES, NOT SHIPPABLE | `cargo check` passes, NSIS hooks exist | Placeholder icon (32x32 only); empty updater pubkey; no `build-sidecar.mjs` tested in CI; missing macOS DMG bundle target |
| **Tauri macOS** | :warning: COMPILES (cross-target) | Release workflow builds aarch64 + x86_64 | Same icon/pubkey issues; no code signing configured; `targets` only includes "nsis" (Windows-specific) |
| **npx waggle** | :x: NOT PUBLISHABLE | `packages/launcher/` exists with bin field | `bin` points to `.ts` file (not compiled JS); package not published; missing `files` field |
| **Render.com** | :white_check_mark: WELL CONFIGURED | Blueprint with web + postgres + redis + disk + health | Minor: `plan: starter` may be undersized for production |
| **CI/CD** | :warning: BASIC | `ci.yml` (test) + `release.yml` (Tauri builds) | No linting step; no Docker build/push; no deploy pipeline; CI branch is `master` not `main` |
---
## 1. TypeScript Compilation
### Root monorepo (`npx tsc --noEmit`)
- **Result**: CLEAN PASS -- 0 errors
- The root `tsconfig.json` uses project references for 10 packages
- All backend packages compile without errors
### App scope (`cd app && npx tsc --noEmit`)
- **Result**: 87 errors across ~25 files
- **Breakdown by error code**:
| Code | Count | Severity | Description |
|------|-------|----------|-------------|
| TS6133 | 69 | Low | Unused imports/variables (mostly `React` imports) |
| TS2345 | 5 | Medium | Type argument mismatches |
| TS2339 | 3 | Medium | Property does not exist on type (e.g., `customModels` on `WaggleConfig`) |
| TS6196 | 2 | Low | Declared but never used |
| TS2352 | 2 | Medium | Type assertion issues |
| TS2305 | 2 | Medium | Module has no exported member |
| TS7006 | 1 | Low | Parameter implicitly has 'any' type |
| TS2719 | 1 | Medium | Type overlap issue |
| TS2554 | 1 | Medium | Wrong number of arguments |
| TS2322 | 1 | Medium | Type not assignable |
- **Impact**: Vite builds successfully despite these errors (Vite uses esbuild, not tsc, for transpilation). However, these indicate real type drift that could cause runtime bugs.
- **Hotspot files**: `src/App.tsx` (9 errors), `MissionControlView.tsx` (4 errors), `ModelsSection.tsx` (4 errors)
---
## 2. Vite Build
### Configuration (`app/vite.config.ts`)
- Framework: Vite 6.4.1 + React + Tailwind CSS v4
- Output: `dist/`
- Source maps: enabled
- `@tauri-apps/*` packages marked as external (correct for dual web/desktop mode)
### Build Result
- **Status**: SUCCESS (built in 5.12s)
- **Output**:
| File | Size | Gzipped |
|------|------|---------|
| `index.html` | 0.47 kB | 0.30 kB |
| `index-*.css` | 107.02 kB | 18.00 kB |
| `index-*.js` | 735.61 kB | 215.44 kB |
| Source map | 2,929.67 kB | -- |
| **Total dist/** | **5.5 MB** | -- |
### Warnings
1. **Single chunk exceeds 500 kB** -- The entire app is bundled into one JS file (735 kB). Should use `manualChunks` or dynamic `import()` for code splitting.
2. **CSS @import order** -- `@import url('https://fonts.googleapis.com/...')` appears after other rules. Should be moved to top or loaded via `<link>` tag.
### Blockers
- None (builds successfully)
### Recommendations
- Add `manualChunks` in rollup config to split vendor (React, lucide-react, cmdk) from app code
- Move Google Fonts import to `index.html` `<link>` tag
- Consider disabling source maps for production web builds (saves 2.9 MB)
---
## 3. Docker
### Dockerfile Assessment
- **Multi-stage build**: YES (builder + production)
- **Base image**: `node:20-alpine` (good -- small, current LTS)
- **Native module handling**: Installs `python3 make g++` for `better-sqlite3` rebuild
- **Health check**: YES (`wget --spider http://localhost:3333/health`, 30s interval)
- **Data volume**: YES (`/data` volume for persistence)
- **Environment**: `NODE_ENV=production`, `WAGGLE_FRONTEND_DIR`, `WAGGLE_DATA_DIR`
- **Entry point**: `npx tsx packages/server/src/local/start.ts --skip-litellm`
### Security Issues
| Issue | Severity | Detail |
|-------|----------|--------|
| No non-root user | HIGH | Container runs as root. Should `adduser waggle` and `USER waggle` |
| `npx tsx` in CMD | MEDIUM | Uses tsx (TypeScript executor) in production. Should pre-compile to JS |
| Build tools in prod image | LOW | `python3 make g++` left in production layer after `npm rebuild` |
### docker-compose.production.yml
- **Services**: waggle + postgres:16-alpine + redis:7-alpine
- **Health checks**: All 3 services have health checks with `service_healthy` conditions
- **Restart policy**: `unless-stopped` on all services
- **Volumes**: Named volumes for data persistence (waggle-data, pgdata, redisdata)
- **Env vars**: Properly uses `${VAR:-default}` pattern
- **Status**: VALID (passes `docker compose config`)
### docker-compose.yml (dev)
- **Services**: postgres + redis + litellm
- **Warning**: Dev compose exposes API keys via environment interpolation. The `docker compose config` output revealed a real Anthropic API key in the `.env` file. While `.env` is gitignored and dockerignored, this is a reminder to rotate the key if it was ever committed.
- **Status**: VALID (passes `docker compose config`)
### .dockerignore Assessment
- Properly excludes: `node_modules`, `.git`, `.claude`, `*.mind`, `app/dist`, `app/src-tauri`, `sidecar`, `docs`, `.env*`
- Good: prevents secrets and large files from entering the build context
---
## 4. Tauri Desktop App
### tauri.conf.json Assessment
| Field | Value | Status |
|-------|-------|--------|
| productName | "Waggle" | OK |
| version | "1.0.0" | OK |
| identifier | "com.waggle.app" | OK |
| frontendDist | "../dist" | OK |
| devUrl | "http://localhost:1420" | OK |
| bundle.targets | ["nsis"] | INCOMPLETE -- only Windows NSIS |
| bundle.icon | ["icons/icon.ico"] | INCOMPLETE -- only .ico |
| CSP | Configured | OK -- allows localhost, ws, data: images |
| trayIcon | Configured | OK -- tooltip "Waggle -- AI Agent Swarm" |
| updater.endpoints | GitHub releases URL | OK |
| updater.pubkey | "" (empty) | BLOCKER -- auto-update won't work |
### Rust Compilation
- `cargo check`: PASSES (55s compile time, all dependencies resolve)
- Tauri plugins: shell, autostart, global-shortcut, notification, single-instance, updater
- Rust toolchain: cargo 1.94.0, rustc 1.94.0
### Sidecar Build (`scripts/build-sidecar.mjs`)
- Bundles server into single JS via esbuild
- Externals: better-sqlite3, bullmq, ioredis, pg, drizzle-orm, etc.
- Copies marketplace.db seed
- **Status**: Script exists but NOT tested in this audit (requires esbuild)
### Icon Issues
| Issue | Severity |
|-------|----------|
| Only 1 icon file: `icon.ico` at 32x32 | HIGH -- needs multi-resolution (16, 32, 48, 256) |
| No `.png` icons for macOS/Linux | HIGH -- macOS requires `.icns` or `.png` |
| No installer header/sidebar BMPs | LOW -- NSIS will use defaults |
### NSIS Installer Hooks
- Pre-install: branding message
- Post-install: desktop shortcut, Start Menu entry, auto-launch
- Post-uninstall: removes shortcuts, offers to delete `~\.waggle\` data
- **Status**: Well-structured
### Missing for macOS
- `bundle.targets` only includes `"nsis"` -- needs `"dmg"` and/or `"app"` for macOS
- No `.icns` icon file
- No code signing identity configured
- No notarization config
- Release workflow handles this at CI level (good), but local `tauri:build` won't produce macOS bundles
---
## 5. Render.com Blueprint (`render.yaml`)
### Services
| Service | Type | Plan | Config |
|---------|------|------|--------|
| waggle-server | web (Node.js) | starter | Build + start commands, health check, disk |
| waggle-postgres | database | starter | DB name: waggle, user: waggle |
| waggle-redis | redis | starter | Default config |
### Assessment
- **Build command**: `npm install && cd app && npm run build` -- correct
- **Start command**: `npx tsx packages/server/src/local/start.ts --skip-litellm` -- works but uses tsx in production
- **Health check**: `/health` endpoint -- correct
- **Environment variables**: 7 vars configured (NODE_ENV, WAGGLE_SKIP_LITELLM, WAGGLE_FRONTEND_DIR, WAGGLE_DATA_DIR, DATABASE_URL, REDIS_URL, ANTHROPIC_API_KEY)
- **Persistent disk**: 10 GB at `/data` -- good for .mind files and SQLite
- **Auto-deploy**: enabled
- **Secret vars**: `sync: false` for API keys (correct -- must be set manually)
- **Missing**: No `CORS_ORIGIN` or `PORT` override
- **Status**: WELL CONFIGURED
---
## 6. npx Launcher (`packages/launcher/`)
### package.json
```json
{
"name": "waggle",
"bin": { "waggle": "./src/cli.ts" },
"dependencies": { "@waggle/server": "*" }
}
```
### Issues
| Issue | Severity | Detail |
|-------|----------|--------|
| `bin` points to `.ts` file | BLOCKER | npx cannot run TypeScript directly without tsx |
| No `files` field | HIGH | Would publish entire package including tests |
| No `engines` field | MEDIUM | Should specify `node >= 18` (code checks for it) |
| Not published to npm | BLOCKER | Package exists locally only |
| Depends on `@waggle/server: "*"` | HIGH | Workspace protocol won't work when published |
| No `prepublishOnly` script | MEDIUM | Should build before publish |
### Entry Point (`src/cli.ts`)
- Well-structured: argument parsing, browser opening, first-run detection
- Cross-platform browser launch (Windows, macOS, Linux)
- Node version check (>= 18)
- Progress callback for startup status
- **Quality**: Good code, but needs build pipeline to be publishable
---
## 7. CI/CD (`.github/workflows/`)
### ci.yml
```yaml
on:
push: { branches: [master] }
pull_request: { branches: [master] }
jobs:
test:
runs-on: ubuntu-latest
steps: [checkout, setup-node@20, npm install, npm test]
```
### Issues
| Issue | Severity |
|-------|----------|
| Branch is `master`, main branch appears to be `main` | HIGH |
| No TypeScript lint step (`tsc --noEmit`) | MEDIUM |
| No Docker build step | MEDIUM |
| No coverage reporting | LOW |
| No caching (`actions/cache` for npm) | LOW |
| Missing PostgreSQL/Redis services for integration tests | MEDIUM |
### release.yml
- **Trigger**: Tag push `v*` or manual dispatch
- **Windows build**: checkout, Node 20, Rust stable, npm install, build-sidecar, build frontend, tauri-action
- **macOS build**: Same + matrix for aarch64-apple-darwin and x86_64-apple-darwin
- **Update manifest**: Generates `latest.json` for auto-updater
- **Artifacts**: Published as GitHub Release (draft)
- **Status**: WELL STRUCTURED -- covers both platforms, uses Rust cache, proper action versions
### Missing CI/CD Capabilities
- No Docker image build and push (to GHCR or DockerHub)
- No Render.com deploy trigger
- No E2E test step (Playwright is configured but not in CI)
- No npm publish step for the launcher package
- No security scanning (Dependabot, CodeQL, etc.)
- No branch protection rules verified
---
## Critical Blockers Summary
### Must-Fix Before V1 Ship
| # | Issue | Mode | Effort |
|---|-------|------|--------|
| 1 | Dockerfile runs as root | Docker | 5 min |
| 2 | 87 TypeScript errors in app/ | All | 2-4 hrs |
| 3 | Tauri updater pubkey is empty | Desktop | 30 min (generate key pair) |
| 4 | Icon is placeholder 32x32 only | Desktop | Design task |
| 5 | `bundle.targets` missing macOS targets | Desktop macOS | 5 min config |
| 6 | Launcher bin points to .ts | npx | 1 hr (add build step) |
| 7 | CI triggers on `master` not `main` | CI | 5 min |
| 8 | Vite bundle is single 735 kB chunk | Web | 1 hr (add code splitting) |
| 9 | Production server uses `npx tsx` | Docker/Render | 2 hrs (pre-compile) |
| 10 | No non-root user in Docker | Docker | 5 min |
### Security Findings
| # | Finding | Severity |
|---|---------|----------|
| 1 | Docker container runs as root | HIGH |
| 2 | Anthropic API key present in `.env` (gitignored, but exercise caution) | MEDIUM |
| 3 | Updater pubkey empty -- updates could be MITM'd | HIGH |
| 4 | Build tools (python3, make, g++) left in production Docker image | LOW |
| 5 | No Dependabot or security scanning in CI | MEDIUM |
---
## Detailed Recommendations
### Quick Wins (< 1 hour each)
1. **Add non-root user to Dockerfile**:
```dockerfile
RUN addgroup -S waggle && adduser -S waggle -G waggle
RUN chown -R waggle:waggle /app /data
USER waggle
```
2. **Fix CI branch**: Change `master` to `main` in `ci.yml`
3. **Add macOS to Tauri bundle targets**: Change `"targets": ["nsis"]` to `"targets": "all"` or `["nsis", "dmg", "app"]`
4. **Add TypeScript lint to CI**: Add `npm run lint` step after `npm install`
### Medium Effort (1-4 hours each)
5. **Code split Vite bundle**: Add `manualChunks` to split React, UI library, and app code
6. **Pre-compile server for production**: Use esbuild or tsc to produce JS, change Dockerfile CMD to `node` instead of `npx tsx`
7. **Fix 87 TypeScript errors**: Mostly unused imports (69 of 87) -- quick cleanup
8. **Prepare launcher for npm publish**: Add build step, `files` field, `engines`, resolve `@waggle/server` dependency
### Larger Effort (1+ days)
9. **Generate Tauri signing keys**: Create key pair, configure updater, add to GitHub secrets
10. **Design production icons**: Multi-resolution `.ico`, `.icns`, `.png` files; NSIS installer graphics
11. **Add Docker build/push to CI**: Build image, push to GHCR, optionally deploy to Render
12. **Add E2E tests to CI**: Playwright tests with PostgreSQL + Redis service containers