diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl
index 2edb51c..bbea524 100644
--- a/.beads/issues.jsonl
+++ b/.beads/issues.jsonl
@@ -10,6 +10,8 @@
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"_type":"issue","id":"islandflow-2db","title":"Manually remove stale islandflow local-infra containers from VPS","description":"The live VPS still has an older compose project named islandflow created from the repo-root docker-compose.yml. Inspection shows it is separate from the supported islandflow-vps deployment stack and exposes NATS, ClickHouse, and Redis on host ports. Container removal commands currently hang when run as the delta user through Docker, so cleanup likely needs a focused maintenance window and possibly host-level intervention or a Docker daemon restart.","notes":"The duplicate islandflow compose project on the VPS was confirmed live during inspection. Nginx Proxy Manager routes public traffic only to islandflow-vps web/api by Docker name, so the stale islandflow project appears to be stray local-infra state rather than part of the supported production path. Attempts to remove the stale containers with docker compose down and docker rm -f as the delta user hung and timed out, so manual cleanup likely needs a maintenance window and possibly Docker daemon intervention.","status":"open","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:27:27Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:28:59Z","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"_type":"issue","id":"islandflow-c87","title":"Clean up duplicate Islandflow Docker infra on VPS","description":"The live VPS is currently running both the production-style islandflow-vps Docker stack and an older root-level islandflow infra stack that publishes NATS, ClickHouse, and Redis on host ports. Investigate whether the older stack is unused, remove it safely if so, and update docs/deploy guidance so the server topology is clearer.","notes":"Inspected the live VPS and confirmed the duplicate compose project: islandflow-vps is the supported deployment stack, while a separate islandflow project from the repo-root docker-compose.yml still runs exposed NATS/ClickHouse/Redis containers. Verified Nginx Proxy Manager routes only to islandflow-vps web/api by Docker name. Attempted cleanup via docker compose down and docker rm -f on the stale islandflow containers, but those commands hung for the delta user and timed out. Added repo guardrails and docs so deploy warns when the duplicate project exists, and opened islandflow-2db for manual host-level cleanup during a maintenance window.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:16:05Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:28:07Z","started_at":"2026-05-16T01:16:09Z","closed_at":"2026-05-16T01:28:07Z","close_reason":"Completed the repo-side investigation and guardrails. Actual server-side container removal is blocked by hanging Docker operations and is tracked separately in islandflow-2db for a maintenance window.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-4gj","title":"Clarify Docker-first deploy workflow and mark native runtime experimental","description":"After inspecting the live VPS, native deployment is not ready for routine use: Nginx Proxy Manager routes to Docker container names, Bun is not installed on the host, sudo systemctl is not passwordless, and no Islandflow units exist. Update deploy messaging and docs so Docker remains the clearly recommended deployment path and native runtime is labeled experimental/future-facing with server prerequisites called out.","notes":"Updated deploy messaging and docs after live VPS inspection. scripts/deploy.ts now marks Docker as the default and recommended runtime, labels native as experimental, switches native systemctl default to sudo -n systemctl, and prints explicit native precheck failures for missing Bun/systemctl access/units. Updated README.md, deployment/docker/README.md, and deployment/native/README.md to reflect the current Docker + Nginx Proxy Manager topology. Validation: ./deploy --help, ./deploy main --runtime native --no-build (fails fast with Bun-missing message), bun run check:docker-workspace.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:10:11Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:12:39Z","started_at":"2026-05-16T01:10:14Z","closed_at":"2026-05-16T01:12:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-7p2","title":"Fix deploy wrapper argument forwarding for runtime flags","description":"The repo-root deploy wrapper currently invokes bun run without a -- separator, so flags like --runtime native are treated as Bun CLI flags instead of script arguments. Update the wrapper so ./deploy main --runtime native forwards arguments correctly to scripts/deploy.ts.","notes":"Cherry-picked the dual-runtime deploy workflow onto main and fixed the repo-root deploy wrapper to call Bun with a -- separator so flags like --runtime native are forwarded to scripts/deploy.ts correctly. Validation: ./deploy --help, ./deploy main --runtime native --force-recreate guard, bun run check:docker-workspace.","status":"closed","priority":2,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T00:51:05Z","created_by":"dirtydishes","updated_at":"2026-05-16T00:52:34Z","started_at":"2026-05-16T00:51:10Z","closed_at":"2026-05-16T00:52:34Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-qh7","title":"Implement dual-runtime deploy workflow with partial deploys","description":"Implement the planned refactor of the root deploy script and scripts/deploy.ts so deployment can target Docker and host-native runtimes during a transition period. Preserve local dev as Docker infra plus native Bun services/web, add explicit runtime selection, runtime-specific prechecks/rollout/verification, and support partial deploy scopes such as web-only or services-only. Update operator documentation for the new workflow.","notes":"Implemented dual-runtime deploy workflow. scripts/deploy.ts now supports --runtime docker|native, scope flags (--web-only, --api-only, --services-only), and --no-build. Docker verification now uses docker compose exec instead of hardcoded container names. Added deployment/native/README.md and updated README.md plus deployment/docker/README.md for the new workflow. Validation: bun run scripts/deploy.ts --help, bun run check:docker-workspace, guard checks for invalid flag combinations.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-15T23:38:31Z","created_by":"dirtydishes","updated_at":"2026-05-15T23:46:17Z","started_at":"2026-05-15T23:40:13Z","closed_at":"2026-05-15T23:46:17Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
diff --git a/README.md b/README.md
index f6d0085..50063d9 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,7 @@ This keeps Docker in the local workflow where it helps most (NATS, ClickHouse, R
## Deployment Workflow
- `./deploy main` keeps the current VPS Docker rollout path as the default and recommended path.
+- Do not run the repo-root `docker-compose.yml` on the VPS. That file is for local infra only and can create duplicate exposed NATS, ClickHouse, and Redis containers on the server.
- `./deploy main --runtime native` targets an experimental host-native Bun + systemd deployment.
- `./deploy current-branch` and `./deploy current-branch --runtime native` keep branch deploys available during the transition, but Docker remains the supported path for the current VPS.
- Partial deploys are supported with `--web-only`, `--api-only`, `--services-only`, and `--no-build`.
diff --git a/deployment/docker/README.md b/deployment/docker/README.md
index a6cc1d5..7c4f03b 100644
--- a/deployment/docker/README.md
+++ b/deployment/docker/README.md
@@ -11,6 +11,8 @@ The repo no longer ships or supports a separate `deployment/npm` stack. If you w
It is separate from the repo-root `docker-compose.yml`, which remains the lightweight local infra stack for development.
+Do not run the repo-root `docker-compose.yml` on the VPS. On the live server that creates a second compose project with host-published NATS, ClickHouse, and Redis ports that are not part of the supported production topology.
+
## What this stack does
- Builds and runs the full Islandflow stack with Docker Compose.
@@ -192,6 +194,8 @@ docker compose build web
The current live VPS uses Nginx Proxy Manager on the shared Docker network and routes public traffic to the Docker `web` and `api` containers by container name. Because of that, this Docker path remains the operationally correct default for the live server today.
+The deploy helper also warns if it detects a second compose project named `islandflow` on the server, because that usually means the repo-root local-infra stack was started on the VPS by mistake.
+
The checked-in deploy helper is meant to run from your local repo checkout, not from the VPS shell. It always targets:
- SSH host: `delta@152.53.80.229`
diff --git a/docs/turns/2026-05-15-add-duplicate-vps-compose-warning.html b/docs/turns/2026-05-15-add-duplicate-vps-compose-warning.html
new file mode 100644
index 0000000..c9a2ffe
--- /dev/null
+++ b/docs/turns/2026-05-15-add-duplicate-vps-compose-warning.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+ 2026-05-15: Warn about duplicate VPS compose stacks
+
+
+
+
+
+ Turn document
+ 2026-05-15
+ Issues: islandflow-c87, islandflow-2db
+
+ Warn about duplicate VPS compose stacks
+
+ Investigated the live VPS, confirmed that a second compose project from the repo-root local-infra stack is still running there, attempted cleanup, and added deploy/docs guardrails so this state is easier to spot and less likely to be recreated accidentally.
+
+
+
+ Summary
+
+ The live server currently has both islandflow-vps and an older islandflow compose project. The supported production traffic path uses islandflow-vps. I added a deploy-time warning and documentation updates so Docker remains the intended VPS path and the repo-root docker-compose.yml is clearly marked as local-only. I also attempted to remove the stale islandflow containers on the VPS, but Docker operations against them hung and timed out, so manual cleanup is tracked separately.
+
+
+
+
+ Changes Made
+
+ - Updated
scripts/deploy.ts so Docker runtime prechecks warn when the server also has a compose project named islandflow.
+ - Updated
README.md to explicitly say the repo-root docker-compose.yml is for local infra only and should not be run on the VPS.
+ - Updated
deployment/docker/README.md with the same warning and a note about the duplicate-project detector.
+ - Inspected the live VPS and confirmed that Nginx Proxy Manager routes public traffic to
islandflow-vps container names on the shared Docker network.
+ - Attempted
docker compose down and forced container removal for the stale islandflow project, but those operations timed out when run as the normal deploy user.
+
+
+
+
+ Context
+
+ The repo has two Docker entry points with different purposes. The root docker-compose.yml is a local development infra stack that publishes NATS, ClickHouse, and Redis on host ports. The supported VPS deployment lives under deployment/docker/ and uses an islandflow-vps compose project, internal service-name routing, and Nginx Proxy Manager on the shared Docker network. Running both on the VPS creates duplicate infra and can make host-level debugging confusing.
+
+
+
+
+ Important Implementation Details
+
+ - The new warning is advisory only. It does not block Docker deploys, because the current live server still has the duplicate project and production deploys must keep working.
+ - The detection looks for containers whose compose project label is
islandflow, which matches the repo-root stack on the VPS.
+ - The stale containers are still present as of this turn. Removal is blocked by hanging Docker operations and likely needs a maintenance window or host-level intervention.
+
+ [deploy] Warning: found an additional compose project named "islandflow" on the server.
+[deploy] The live VPS should normally use only the deployment/docker stack (compose project "islandflow-vps").
+[deploy] The repo-root docker-compose.yml is for local infra and can create duplicate exposed NATS, ClickHouse, and Redis services on the VPS.
+
+
+
+ Validation
+
+ - Passed:
./deploy --help
+ - Passed:
bun run check:docker-workspace
+ - Passed: live VPS inspection confirming duplicate compose containers still exist
+ - Passed: public app health check after the cleanup attempt,
https://flow.deltaisland.io still returned HTTP 200
+
+ ssh di 'docker ps --format "{{.Names}} {{.Label \"com.docker.compose.project\"}}" | grep "^islandflow-.* islandflow$" || true'
+curl -I -fksS https://flow.deltaisland.io
+
+
+
+ Issues, Limitations, and Mitigations
+
+ - The stale VPS containers were not removed in this turn. Mitigation: tracked manual cleanup in
islandflow-2db.
+ - Docker commands targeting those old containers hung and timed out. Mitigation: avoided risky broader actions that could impact the live
islandflow-vps stack.
+ - The deploy warning is non-blocking. That is intentional so normal Docker deploys continue to work while the duplicate stack is still present.
+
+
+
+
+ Follow-up Work
+
+ - Open follow-up:
islandflow-2db, manually remove the stale islandflow local-infra containers from the VPS during a maintenance window.
+
+
+
+
+
diff --git a/scripts/deploy.ts b/scripts/deploy.ts
index 183f833..1ec3e6c 100644
--- a/scripts/deploy.ts
+++ b/scripts/deploy.ts
@@ -495,6 +495,12 @@ cd ${shellEscape(REMOTE_DOCKER_DEPLOYMENT)}
command -v docker >/dev/null 2>&1
docker compose version >/dev/null
+
+if docker ps --format '{{.Names}} {{.Label "com.docker.compose.project"}}' | grep -q '^islandflow-.* islandflow$'; then
+ echo '[deploy] Warning: found an additional compose project named "islandflow" on the server.' >&2
+ echo '[deploy] The live VPS should normally use only the deployment/docker stack (compose project "islandflow-vps").' >&2
+ echo '[deploy] The repo-root docker-compose.yml is for local infra and can create duplicate exposed NATS, ClickHouse, and Redis services on the VPS.' >&2
+fi
`
);
return;