|
All checks were successful
Deploy to Production / deploy (push) Successful in 1m10s
Adds /opt/bmm-ops/ scripts (deployed separately from the app, so tar
overlays don't clobber them) for three previously-missing production
readiness items:
1. Backup hardening (backup.sh):
- Previous cron one-liner did pg_dump | gzip with no validation.
- Now: pipefail-safe pg_dump, gunzip -t integrity check, pg_dump
header sanity (scans first 5 lines — line 1 is just "--", actual
"PostgreSQL database dump" comment lands on line 2), size-warning
under 1KB, atomic move-into-place so partial backups never replace
the previous good file. 14-day retention preserved.
- Optional offsite via BMM_BACKUP_REMOTE (rclone). Reads env via
grep+cut, NOT `source` — the .env.production has unquoted text
values (e.g. ADMIN_NAME) that crash a sourced shell.
2. Restore drill (restore-test.sh, Sun 04:30 UTC weekly):
- Restores the newest backup into a throwaway DB inside the same
Postgres container, verifies the core tables exist (users,
sessions, oauth_tokens, mcp_servers), drops the temp DB. Proves
backups are actually restorable, not just byte-streams that look
like backups. Silent-corruption detector.
3. Self-hosted uptime monitor (uptime-check.sh, every 5 min):
- Probes homepage + /api/health + /robots.txt.
- Edge-triggered alerting: SMS via Twilio only on up→down and
down→up transitions (avoids SMS storm during sustained outages).
- Pings HEALTHCHECKS_HEARTBEAT_URL on every success — when the box
itself dies the heartbeat stops and the external watchdog alerts
(covers the gap that self-hosted monitors can't see their own
box failing).
notify.sh is the shared helper: Twilio SMS if all four creds set,
optional webhook to HEALTHCHECKS_FAIL_URL, always logs to syslog. Never
fails loudly — broken notification path still lands in journalctl
-t bmm-ops.
README.md documents the 3-2-1 strategy, manual full-recovery
procedure, and how to enable offsite (R2 / B2 / Hetzner Storage Box).
Smoke-tested all three on prod: backup wrote 8004 bytes with checks
passing, restore-test confirmed schema, uptime probe returned up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .gitea/workflows | ||
| apps | ||
| infra/nginx | ||
| ops/bmm | ||
| packages | ||
| scripts | ||
| .dockerignore | ||
| .env.example | ||
| .env.production.example | ||
| .gitignore | ||
| biome.json | ||
| BuildMyMCPServer_MASTER_PROMPT.md | ||
| CHOICES.md | ||
| DEPLOY.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| TEMPLATE_SECURITY_AUDIT.md | ||
| tsconfig.base.json | ||
| turbo.json | ||
BuildMyMCPServer
Describe your tool. We host the server. AI uses it.
Prompt-to-production MCP servers with OAuth 2.1 and Streamable HTTP. Production-grade infrastructure for hosting Model Context Protocol servers your AI clients (Claude Desktop, Cursor, ChatGPT) can install with a copy-paste snippet.
Quick start
# 1. Install
pnpm install
# 2. Copy env. Defaults work for local dev. Set ANTHROPIC_API_KEY if you want real generation.
cp .env.example .env
# 3. Boot everything
pnpm dev
pnpm dev will:
- Load
.env. docker compose up -d --waitpostgres + redis.- Push the Drizzle schema (
drizzle-kit push --force). - Start the full stack in parallel: web (Next.js, :3000), api (Fastify, :4000), generator (BullMQ worker).
Then open:
- Dashboard: http://localhost:3000
- API: http://localhost:4000/health
Click Start building, enter your email, copy the magic-link URL printed to the
api terminal output, paste it in your browser. You land on /dashboard. Click
New server, paste a prompt, and watch the build stream live over WebSocket.
If ANTHROPIC_API_KEY is unset, the generator returns a deterministic mock spec
(an echo and a now tool) so the full end-to-end flow stays demoable.
If Docker is unavailable, the build will fail at the deploy step with a clear error.
Otherwise: a fresh container is launched on a host port from
RUNNER_PORT_RANGE_START…RUNNER_PORT_RANGE_END, the server is marked live, and the
dashboard renders install snippets for Claude Desktop, Cursor and ChatGPT.
Architecture
See BuildMyMCPServer_MASTER_PROMPT.md for the full specification and CHOICES.md
for decisions made during this Sprints 1–3 build.
apps/
web/ Next.js 15 dashboard + marketing landing
api/ Fastify control plane (auth, server CRUD, OAuth 2.1 AS, JWKS, WS stream)
generator/ BullMQ worker — Claude → spec → render → docker build → local deploy
runner-template/ Hosted MCP server template (Streamable HTTP + OAuth 2.1 RS)
packages/
db/ Drizzle schema + client
auth/ Magic-link + session
types/ Shared Zod contracts
Scripts
| Command | Effect |
|---|---|
pnpm dev |
Bootstrap + parallel dev for web, api, generator |
pnpm dev:no-docker |
Skip docker-compose (assumes postgres + redis already up) |
pnpm build |
Turbo build all apps |
pnpm typecheck |
Turbo typecheck all apps |
pnpm lint |
Biome check |
pnpm lint:fix |
Biome check --write |
pnpm db:push |
Push schema to postgres (drizzle-kit) |
pnpm db:generate |
Generate SQL migration files |
pnpm db:migrate |
Apply pending migrations |
pnpm stop |
docker compose down |
Acceptance check
After pnpm dev is up:
http://localhost:3000renders the landing page.http://localhost:4000/healthreturns{ "ok": true }.- Sign in via magic link (URL printed in the api terminal).
- New Server → paste prompt → live WebSocket stream
queued → generating → building → deploying → live. - If Docker is running, a container is launched and
http://localhost:<port>/mcpresponds 401 + WWW-Authenticate without a token, 200 with a valid token issued by/oauth/token. - Install snippets render with copy buttons for Claude Desktop, Cursor, ChatGPT.
Repo conventions
- TypeScript strict, zero
any(Biome lintsnoExplicitAnyas error). - ESM-only, Node 20 LTS.
- Conventional commits.
- Tailwind v4 (
@import 'tailwindcss'). - Geist + Geist Mono.