nyk commited on
Commit
6060dbd
·
unverified ·
1 Parent(s): a6b2e2f

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

Browse files
.dockerignore CHANGED
@@ -6,4 +6,5 @@ node_modules
6
  *.md
7
  .github
8
  ops
9
- scripts
 
 
6
  *.md
7
  .github
8
  ops
9
+ scripts/*
10
+ !scripts/check-node-version.mjs
.env.example CHANGED
@@ -71,6 +71,9 @@ NEXT_PUBLIC_GATEWAY_URL=
71
  # Keep gateway auth secrets server-side only (OPENCLAW_GATEWAY_TOKEN / GATEWAY_TOKEN).
72
  # Gateway client id used in websocket handshake (role=operator UI client).
73
  NEXT_PUBLIC_GATEWAY_CLIENT_ID=openclaw-control-ui
 
 
 
74
 
75
  # === Data Paths (all optional, defaults to .data/ in project root) ===
76
  # MISSION_CONTROL_DATA_DIR=.data
 
71
  # Keep gateway auth secrets server-side only (OPENCLAW_GATEWAY_TOKEN / GATEWAY_TOKEN).
72
  # Gateway client id used in websocket handshake (role=operator UI client).
73
  NEXT_PUBLIC_GATEWAY_CLIENT_ID=openclaw-control-ui
74
+ # Gateway optional mode: set to 'true' if deploying on VPS with firewall blocking WebSocket ports.
75
+ # In optional mode, Mission Control runs standalone; core CRUD features work but live gateway events do not.
76
+ # NEXT_PUBLIC_GATEWAY_OPTIONAL=false
77
 
78
  # === Data Paths (all optional, defaults to .data/ in project root) ===
79
  # MISSION_CONTROL_DATA_DIR=.data
README.md CHANGED
@@ -73,6 +73,32 @@ pnpm dev # http://localhost:3000
73
  Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run.
74
  If `AUTH_PASS` contains `#`, quote it (e.g. `AUTH_PASS="my#password"`) or use `AUTH_PASS_B64`.
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  ### Docker Hardening (Production)
77
 
78
  For production deployments, use the hardened compose overlay:
 
73
  Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run.
74
  If `AUTH_PASS` contains `#`, quote it (e.g. `AUTH_PASS="my#password"`) or use `AUTH_PASS_B64`.
75
 
76
+ ## Gateway Optional Mode (Standalone Deployment)
77
+
78
+ Mission Control can run in standalone mode without a gateway connection. This is useful when:
79
+
80
+ - Deploying on a VPS with firewall rules blocking non-standard WebSocket ports (18789/18790)
81
+ - Testing UI/core workflows without a running gateway
82
+ - Running Mission Control primarily for project/task operations
83
+
84
+ Enable with:
85
+
86
+ ```bash
87
+ NEXT_PUBLIC_GATEWAY_OPTIONAL=true
88
+ ```
89
+
90
+ When enabled, the HUD status shows `Gateway Optional (Standalone)` instead of `Disconnected`.
91
+
92
+ Works without gateway:
93
+ - Task board, projects, agents, sessions, scheduler, webhooks, alerts, activity/audit, cost tracking
94
+
95
+ Requires active gateway:
96
+ - Real-time session updates
97
+ - Agent-to-agent messaging
98
+ - Gateway log streaming
99
+
100
+ For production VPS setups, you can also proxy gateway WebSockets over 443. See `docs/deployment.md`.
101
+
102
  ### Docker Hardening (Production)
103
 
104
  For production deployments, use the hardened compose overlay:
docker-compose.yml CHANGED
@@ -21,12 +21,12 @@ services:
21
  - NET_BIND_SERVICE
22
  security_opt:
23
  - no-new-privileges:true
24
- pids_limit: 256
25
  deploy:
26
  resources:
27
  limits:
28
  memory: 512M
29
  cpus: '1.0'
 
30
  networks:
31
  - mc-net
32
  restart: unless-stopped
 
21
  - NET_BIND_SERVICE
22
  security_opt:
23
  - no-new-privileges:true
 
24
  deploy:
25
  resources:
26
  limits:
27
  memory: 512M
28
  cpus: '1.0'
29
+ pids: 256
30
  networks:
31
  - mc-net
32
  restart: unless-stopped
docs/deployment.md CHANGED
@@ -175,3 +175,36 @@ Then restart the gateway and reconnect from Mission Control.
175
 
176
  Device identity signing uses WebCrypto and requires a secure browser context.
177
  Open Mission Control over HTTPS (or localhost), then reconnect.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  Device identity signing uses WebCrypto and requires a secure browser context.
177
  Open Mission Control over HTTPS (or localhost), then reconnect.
178
+
179
+ ### "Gateway shows offline on VPS deployment"
180
+
181
+ Browser WebSocket connections to non-standard ports (like 18789/18790) are often blocked by VPS firewall/provider rules.
182
+
183
+ Quick option:
184
+
185
+ ```bash
186
+ NEXT_PUBLIC_GATEWAY_OPTIONAL=true
187
+ ```
188
+
189
+ This runs Mission Control in standalone mode (core features available, live gateway streams unavailable).
190
+
191
+ Production option: reverse-proxy gateway WebSocket over 443.
192
+
193
+ nginx example:
194
+
195
+ ```nginx
196
+ location /gateway-ws {
197
+ proxy_pass http://127.0.0.1:18789;
198
+ proxy_http_version 1.1;
199
+ proxy_set_header Upgrade $http_upgrade;
200
+ proxy_set_header Connection "upgrade";
201
+ proxy_set_header Host $host;
202
+ proxy_read_timeout 86400;
203
+ }
204
+ ```
205
+
206
+ Then point UI to:
207
+
208
+ ```bash
209
+ NEXT_PUBLIC_GATEWAY_URL=wss://your-domain.com/gateway-ws
210
+ ```
src/components/hud/connection-status.tsx CHANGED
@@ -18,10 +18,12 @@ export function ConnectionStatus({
18
  }: ConnectionStatusProps) {
19
  const { connection } = useMissionControl()
20
  const displayUrl = connection.url || 'ws://<gateway-host>:<gateway-port>'
 
21
 
22
  const getStatusColor = () => {
23
  if (isConnected) return 'bg-green-500 animate-pulse'
24
  if (connection.reconnectAttempts > 0) return 'bg-yellow-500'
 
25
  return 'bg-red-500'
26
  }
27
 
@@ -32,6 +34,9 @@ export function ConnectionStatus({
32
  if (connection.reconnectAttempts > 0) {
33
  return `Reconnecting... (${connection.reconnectAttempts}/10)`
34
  }
 
 
 
35
  return 'Disconnected'
36
  }
37
 
 
18
  }: ConnectionStatusProps) {
19
  const { connection } = useMissionControl()
20
  const displayUrl = connection.url || 'ws://<gateway-host>:<gateway-port>'
21
+ const isGatewayOptional = process.env.NEXT_PUBLIC_GATEWAY_OPTIONAL === 'true'
22
 
23
  const getStatusColor = () => {
24
  if (isConnected) return 'bg-green-500 animate-pulse'
25
  if (connection.reconnectAttempts > 0) return 'bg-yellow-500'
26
+ if (isGatewayOptional && !isConnected) return 'bg-blue-500'
27
  return 'bg-red-500'
28
  }
29
 
 
34
  if (connection.reconnectAttempts > 0) {
35
  return `Reconnecting... (${connection.reconnectAttempts}/10)`
36
  }
37
+ if (isGatewayOptional && !isConnected) {
38
+ return 'Gateway Optional (Standalone)'
39
+ }
40
  return 'Disconnected'
41
  }
42