Summary
Attaching a second app to an existing server (#167, operation 1) collides on ports immediately, because every app generated from the Stacks cloud template binds the same two loopback ports. Nothing allocates ports per site on a shared host, and nothing validates the collision across projects, so the failure surfaces as a service that will not start rather than as a plan-time error.
Evidence
Two independent apps, both untouched from the template:
loghq/config/cloud.ts port: 3022 (app) port: 3023 (api)
bughq/config/cloud.ts port: 3022 (app) port: 3023 (api)Identical. These are the two apps #167 names as the ones to consolidate onto the statushq box, and a third app from the same template will be the same again. The first attach works; the second cannot bind.
validateDeploymentConfig does build a portOwners map and raise deploymentCoexistenceError, but it iterates config.sites for one config. An attached app is a separate repo with its own config, so the port it wants is invisible to the owner's validation and vice versa.
What's missing
Port allocation is part of per-site isolation. #167 lists isolation as "user, directory, service unit, log stream" and stops short of the one piece of shared state that is guaranteed to conflict. Options, roughly in order of preference:
- Allocate at attach time. The host owns a port range; attaching a site claims the next free pair and records it in the host's inventory. The attached app stops declaring absolute ports at all.
- Unix sockets instead of TCP. rpx routes to a per-site socket, which removes the shared namespace rather than partitioning it.
- Detect and refuse. At minimum,
attach --dry-runreads the host's existing site ports and fails the plan with both owners named, instead of discovering it atsystemctl start.
Any of these is fine. Silently binding over another app's port is not, and neither is asking every app author to hand-pick a globally unique port.
Acceptance criteria
- Attaching a second app from the default template to an occupied host succeeds without either app editing a port by hand
- A port conflict is reported at plan time, naming both sites, not at service start
--dry-runshows the ports each site will occupy after the operation- Port assignments are recorded where the next attach can see them, so the third app is allocated correctly
Related
- #167 — server consolidation (this blocks its first operation)
- #169 — credential blast radius on a shared project
- stacksjs/stacks#2342 — the CLI surface
Found while planning a loghq deploy onto the statushq box, against ts-cloud 0.7.114.