Start Web Server Command
Overview
The symphony web command starts the Symphony web server in isolation—either in development mode (Nuxt dev server) or production mode (built Nitro server). This is a granular command complementing the comprehensive symphony start command, useful for development workflows where you want to start only the web UI without the orchestrator.
Key Purpose: Provide fast web server startup for development iteration and production deployment scenarios.
Entry Point: cli/commands/web.ts
Shared Launch Helper: cli/utils/webServer.ts
CLI Usage:
symphony web [--port <number>]Modes:
- Development:
npx nuxt dev --port <port>(hot reload, HMR) - Production:
node .output/server/index.mjswithPORT/HOSTenv vars (prebuilt, optimized)
How It Works
1. Initialization Phase
Port Resolution:
- Default:
3000 - Override via
--port <number>flag - Passed to process spawn as command argument or env var
NODE_ENV Determination:
- Dev mode:
process.env.NODE_ENV !== 'production' - Prod mode:
NODE_ENV === 'production'
PID Management:
- Writes CLI process PID to
.symphony/symphony.pid - Removed on graceful shutdown
- Note: Unlike
start.ts, does NOT check if Symphony already running (no double-start prevention)
2. Process Spawning Phase (Dev Mode)
npx nuxt dev --port 3000Command Structure:
command:'npx'args:['nuxt', 'dev', '--port', '3000']cwd: Symphony project rootreadyPattern:/Local:\s+http/(Nuxt dev server indicates ready)
Nuxt Dev Server Output:
[web] ➜ Local: http://localhost:3000/ProcessManager watches stdout, matches pattern, marks process as ready: true.
3. Process Spawning Phase (Production Mode)
node .output/server/index.mjsCommand Structure:
command:'node'args:['/absolute/path/to/.output/server/index.mjs']cwd: Symphony project rootenv:{ PORT: '3000', HOST: '0.0.0.0' }readyPattern:/Listening/(custom Nitro startup message)
Nitro Production Server:
- Built by
nuxt build→.output/directory - Listens on
PORT(via env var) andHOST(0.0.0.0 = all interfaces) - Emits startup message including "Listening"
4. Output & Logging Phase
ProcessManager pipes both stdout and stderr with [web] prefix:
[web] ➜ Local: http://localhost:3000/
[web] [nitro] Built in 1234ms
[web] [error] Something failedKey Components
Files & Responsibilities
| File | Purpose |
|---|---|
cli/commands/web.ts | Entry point; mode detection, shutdown coordination, delegates web spawn contract to shared helper |
cli/utils/webServer.ts | Shared web launch helper used by web.ts and start.ts; owns dev/prod command, args, env merge, and ready patterns |
cli/utils/processManager.ts | Process lifecycle (spawn, stdout/stderr piping, readiness detection, graceful shutdown) |
cli/utils/pid.ts | PID file read/write/cleanup and liveness checking |
cli/utils/output.ts | Terminal formatting (heading, success, error, dim helpers) |
cli/index.ts | CLI router; dispatches 'web' case to startWeb() |
nuxt.config.ts | Nuxt configuration (SSR disabled, Nitro settings, file watch ignores) |
Process Lifecycle
Design Decisions
1. Granular vs Comprehensive Command (HIGH IMPACT)
Decision: Offer both symphony web (granular) and symphony start (comprehensive).
Rationale:
- Granular: Fast iteration in development (no DB init, project detection overhead)
- Comprehensive: Full initialization for production or clean startup
Trade-off: Requires two similar implementations, but enables flexible workflows.
2. Dev vs Production Mode Separation
Decision: Different commands (nuxt dev vs node .output/server/index.mjs) based on NODE_ENV.
Rationale:
- Dev mode supports hot reload, faster iteration
- Prod mode is prebuilt, zero startup latency
Trade-off: Requires Nuxt build as prerequisite for production. Could auto-build if missing, but keeps it explicit (fail-fast).
3. Readiness Detection via Regex Patterns
Decision: Each mode emits a distinct readiness signal; ProcessManager watches stdout for pattern match.
Rationale:
- Non-blocking: waits for server to be truly ready before returning control
- Avoids port-probing heuristics (fragile with proxies)
- Clear separation of concerns (ProcessManager doesn't know server internals)
Trade-off: Regex patterns can drift if Nuxt/Nitro output changes. Requires maintenance on version upgrades.
4. Single-Process Model (vs start dual-process)
Decision: Spawn only the web server, not the orchestrator.
Rationale:
- Simpler mental model for development workflows
- Lower resource overhead
- Allows independent orchestrator startup (useful for debugging)
Trade-off: Orchestrator must be started separately; users need to understand two-process model.
5. No Double-Start Check (unlike start.ts)
Decision: startWeb() does NOT check if Symphony already running (no PID liveness check).
Rationale:
- Allows starting web server alongside separate orchestrator process
- Simpler code (no process detection)
- Port collision detected naturally by OS (EADDRINUSE)
Trade-off: User error (running two web servers) results in port error instead of user-friendly message.
6. Graceful Shutdown: SIGTERM → SIGKILL
Decision: Send SIGTERM, wait 10 seconds, then force SIGKILL if needed.
Rationale:
- Allows process to clean up resources (flush logs, close connections)
- Prevents orphaned child processes
- SIGKILL as fallback guarantees cleanup
Trade-off: 10s delay on shutdown. Could be user-configurable if shutdown speed becomes critical.
7. PID File Even for Granular Command
Decision: startWeb() writes PID file like startAll() does.
Rationale:
- Consistency: both CLI entry points write the same PID file
- Allows
symphony statusto detect active web process
Trade-off: PID file represents web process, not orchestrator (confusing if both running). Could use separate pid files per process.
8. Environment Variable Pass-Through in Production
Decision: Production mode spawns Nitro with { PORT, HOST } env vars from options.
Rationale:
- Allows port/host override without rebuilding
- Production deployments often require env-var configuration
Trade-off: Dev mode (nuxt dev) hardcodes port in args (no env var fallback). Asymmetry.
Workflow Examples
Development Iteration
# Terminal 1: Start web server (hot reload enabled)
$ symphony web --port 3000
# Terminal 2: Start orchestrator separately for testing
$ symphony orchestrator
# Modify app code → browser auto-refreshesProduction Deployment
# Build production bundle
$ npm run build
# Start production server (prebuilt, zero startup latency)
$ NODE_ENV=production symphony web --port 8080Debugging
# Start just web server, keep orchestrator stopped
$ symphony web
# Make requests, test API endpoints, verify Nitro behaviorKnown Gaps & Future Work
1. Port Collision Detection
Currently: OS-level EADDRINUSE error. Better: Check port availability before spawning, provide user-friendly message.
2. Readiness Pattern Drift
Risk: Nuxt/Nitro versions change output format, readyPattern no longer matches. Mitigation: Version-specific pattern override in config, or timeout-based fallback.
3. PID File Conflict with startAll()
If user runs:
symphony web &
symphony start # Checks PID, finds web process, thinks Symphony runningResult: Confusing error message.
4. Production Build Prerequisite
symphony web in production mode requires .output/server/index.mjs to exist. If missing: Cryptic "ENOENT" error. Better: Check for .output/, suggest npm run build with clear message.
5. No Process Validation on Startup
Dev mode assumes Nuxt is installed (npx nuxt). Prod mode assumes build output exists. No pre-checks; failures occur during spawn.
6. Asymmetric Port Handling
- Dev:
--portpassed as arg (['nuxt', 'dev', '--port', '3000']) - Prod:
--portpassed as env var ({ PORT: '3000' })
These should be consistent.
7. No Reverse Proxy / Routing Wrapper
Currently: Direct Nitro server on port. Future: Optional reverse proxy (nginx/Caddy) for:
- SSL termination
- Request logging
- Route rewriting (useful in mono-repo setups)
8. Lack of Health Check Endpoint
ProcessManager detects readiness via regex; no structured health checks. Better: Call GET /api/health → { status: 'ok' } instead of pattern matching.
Related Docs
- Start Command (
docs/cli/commands/start-command.md) — Comprehensive startup with DB/project init - Process Manager — Used by both
startandwebcommands - Orchestrator Command (
docs/cli/commands/orchestrator-command.md) — Separate process for agent dispatch - Nuxt Configuration — SPA mode, Nitro settings, file watch ignores
- Development Setup (
docs/development-setup.md) — Project structure, local development workflows