Files
buildmymcpserver/apps/generator/src/config.ts

28 lines
1.3 KiB
TypeScript
Raw Normal View History

import { z } from 'zod';
const Env = z.object({
DATABASE_URL: z.string(),
REDIS_URL: z.string().default('redis://localhost:6379'),
ANTHROPIC_API_KEY: z.string().optional(),
GLM_API_KEY: z.string().optional(),
RUNNER_HOST: z.string().default('localhost'),
RUNNER_PORT_RANGE_START: z.coerce.number().default(4100),
RUNNER_PORT_RANGE_END: z.coerce.number().default(4999),
CONTROL_PLANE_URL: z.string().default('http://host.docker.internal:4000'),
CONTROL_PLANE_PUBLIC_URL: z.string().default('http://localhost:4000'),
OAUTH_ISSUER: z.string().optional(),
MODEL_GENERATE: z.string().default('glm-4.5'),
MODEL_FIX: z.string().default('claude-haiku-4-5-20251001'),
feat: oauth refresh-token grant + per-runner subdomain TLS plumbing OAUTH REFRESH-TOKEN - oauth_tokens.subject column added (migration applied to prod DB): stores the JWT sub claim from the original authorization so refreshes can re-mint with the same identity without re-walking the (consumed) code. - Authorization-code branch now writes subject AND uses a 30-day expires_at for the row (was 1h — same as access token, which killed refresh after 1h). - New refresh_token grant branch: * looks up token by refresh-hash + expiry * client_id must match, client_secret verified if confidential * RFC 8707: requested resource must equal stored resource * OAuth 2.1 rotation: atomic UPDATE WHERE old_hash → new access JWT, new refresh token, extended expiry; loser of a race sees invalid_grant - Access TTL (1h) and refresh TTL (30d) extracted as constants. Clients no longer have to re-authorize hourly. Closes Zb-001. PER-RUNNER SUBDOMAIN TLS (Z1-002) Code path: - New MCP_DOMAIN env (e.g. "mcp.buildmymcpserver.com") + RUNNER_MAP_DIR (default /var/runner-map) in generator config. - deployContainer: writes /var/runner-map/<slug>.conf with content "slug.MCP_DOMAIN port;" and computes publicUrl as https://<slug>.<MCP_DOMAIN>. Falls back to http://host:port when MCP_DOMAIN is unset (zero behaviour change until host is configured). - stopContainer (both api/lib/docker.ts and generator/lib/deploy.ts) now accepts an optional slug arg and removes the map fragment. Callers (DELETE /v1/servers/:id, admin template takedown) updated. Infra path (one-time host setup — Marco runs as root): - scripts/setup-runner-tls.sh: 1. nginx vhost matching *.mcp.buildmymcpserver.com via regex → reads slug→port from /opt/buildmymcpserver/runner-map.combined 2. systemd inotify service watches the map dir, combines fragments on any change, reloads nginx 3. installs inotify-tools if missing, idempotent - Prereqs documented at top: Cloudflare wildcard DNS proxied, Origin CA cert for *.mcp.buildmymcpserver.com, SSL mode Full (strict). - After running: edit docker-compose.prod.yml to mount the map dir into api + generator, set MCP_DOMAIN in env, recreate containers. Closes Zb-001 fully. Closes Z1-002 on the code side; one Marco-on-host action away from closing it on the infra side. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:09:06 +02:00
// When set (e.g. "mcp.buildmymcpserver.com"), each deployed runner gets a
// public URL of the form https://<slug>.<MCP_DOMAIN> instead of the legacy
// http://<RUNNER_HOST>:<port> form. Requires host-side nginx + DNS setup
// (see scripts/setup-runner-tls.sh). When unset, falls back to plain HTTP.
MCP_DOMAIN: z.string().optional(),
// Directory the generator drops per-runner map fragments into. A host-side
// inotify service combines them and reloads nginx. Mounted as a volume by
// docker-compose (see setup-runner-tls.sh).
RUNNER_MAP_DIR: z.string().default('/var/runner-map'),
});
export const config = Env.parse(process.env);