fix: resolve open docker and gateway-optional regressions (#291)

This commit is contained in:
nyk 2026-03-12 01:53:00 +07:00 committed by GitHub
parent 44aaf150c2
commit a18240381c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 70 additions and 2 deletions

View File

@ -6,4 +6,5 @@ node_modules
*.md *.md
.github .github
ops ops
scripts scripts/*
!scripts/check-node-version.mjs

View File

@ -71,6 +71,9 @@ NEXT_PUBLIC_GATEWAY_URL=
# Keep gateway auth secrets server-side only (OPENCLAW_GATEWAY_TOKEN / GATEWAY_TOKEN). # Keep gateway auth secrets server-side only (OPENCLAW_GATEWAY_TOKEN / GATEWAY_TOKEN).
# Gateway client id used in websocket handshake (role=operator UI client). # Gateway client id used in websocket handshake (role=operator UI client).
NEXT_PUBLIC_GATEWAY_CLIENT_ID=openclaw-control-ui NEXT_PUBLIC_GATEWAY_CLIENT_ID=openclaw-control-ui
# Gateway optional mode: set to 'true' if deploying on VPS with firewall blocking WebSocket ports.
# In optional mode, Mission Control runs standalone; core CRUD features work but live gateway events do not.
# NEXT_PUBLIC_GATEWAY_OPTIONAL=false
# === Data Paths (all optional, defaults to .data/ in project root) === # === Data Paths (all optional, defaults to .data/ in project root) ===
# MISSION_CONTROL_DATA_DIR=.data # MISSION_CONTROL_DATA_DIR=.data

View File

@ -73,6 +73,32 @@ pnpm dev # http://localhost:3000
Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run. Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run.
If `AUTH_PASS` contains `#`, quote it (e.g. `AUTH_PASS="my#password"`) or use `AUTH_PASS_B64`. If `AUTH_PASS` contains `#`, quote it (e.g. `AUTH_PASS="my#password"`) or use `AUTH_PASS_B64`.
## Gateway Optional Mode (Standalone Deployment)
Mission Control can run in standalone mode without a gateway connection. This is useful when:
- Deploying on a VPS with firewall rules blocking non-standard WebSocket ports (18789/18790)
- Testing UI/core workflows without a running gateway
- Running Mission Control primarily for project/task operations
Enable with:
```bash
NEXT_PUBLIC_GATEWAY_OPTIONAL=true
```
When enabled, the HUD status shows `Gateway Optional (Standalone)` instead of `Disconnected`.
Works without gateway:
- Task board, projects, agents, sessions, scheduler, webhooks, alerts, activity/audit, cost tracking
Requires active gateway:
- Real-time session updates
- Agent-to-agent messaging
- Gateway log streaming
For production VPS setups, you can also proxy gateway WebSockets over 443. See `docs/deployment.md`.
### Docker Hardening (Production) ### Docker Hardening (Production)
For production deployments, use the hardened compose overlay: For production deployments, use the hardened compose overlay:

View File

@ -21,12 +21,12 @@ services:
- NET_BIND_SERVICE - NET_BIND_SERVICE
security_opt: security_opt:
- no-new-privileges:true - no-new-privileges:true
pids_limit: 256
deploy: deploy:
resources: resources:
limits: limits:
memory: 512M memory: 512M
cpus: '1.0' cpus: '1.0'
pids: 256
networks: networks:
- mc-net - mc-net
restart: unless-stopped restart: unless-stopped

View File

@ -175,3 +175,36 @@ Then restart the gateway and reconnect from Mission Control.
Device identity signing uses WebCrypto and requires a secure browser context. Device identity signing uses WebCrypto and requires a secure browser context.
Open Mission Control over HTTPS (or localhost), then reconnect. Open Mission Control over HTTPS (or localhost), then reconnect.
### "Gateway shows offline on VPS deployment"
Browser WebSocket connections to non-standard ports (like 18789/18790) are often blocked by VPS firewall/provider rules.
Quick option:
```bash
NEXT_PUBLIC_GATEWAY_OPTIONAL=true
```
This runs Mission Control in standalone mode (core features available, live gateway streams unavailable).
Production option: reverse-proxy gateway WebSocket over 443.
nginx example:
```nginx
location /gateway-ws {
proxy_pass http://127.0.0.1:18789;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
```
Then point UI to:
```bash
NEXT_PUBLIC_GATEWAY_URL=wss://your-domain.com/gateway-ws
```

View File

@ -18,10 +18,12 @@ export function ConnectionStatus({
}: ConnectionStatusProps) { }: ConnectionStatusProps) {
const { connection } = useMissionControl() const { connection } = useMissionControl()
const displayUrl = connection.url || 'ws://<gateway-host>:<gateway-port>' const displayUrl = connection.url || 'ws://<gateway-host>:<gateway-port>'
const isGatewayOptional = process.env.NEXT_PUBLIC_GATEWAY_OPTIONAL === 'true'
const getStatusColor = () => { const getStatusColor = () => {
if (isConnected) return 'bg-green-500 animate-pulse' if (isConnected) return 'bg-green-500 animate-pulse'
if (connection.reconnectAttempts > 0) return 'bg-yellow-500' if (connection.reconnectAttempts > 0) return 'bg-yellow-500'
if (isGatewayOptional && !isConnected) return 'bg-blue-500'
return 'bg-red-500' return 'bg-red-500'
} }
@ -32,6 +34,9 @@ export function ConnectionStatus({
if (connection.reconnectAttempts > 0) { if (connection.reconnectAttempts > 0) {
return `Reconnecting... (${connection.reconnectAttempts}/10)` return `Reconnecting... (${connection.reconnectAttempts}/10)`
} }
if (isGatewayOptional && !isConnected) {
return 'Gateway Optional (Standalone)'
}
return 'Disconnected' return 'Disconnected'
} }