Summary
Attaching an app to a server owned by another project (#167, operation 1) requires that app's CI to hold a provider credential that can see, and therefore modify and delete, every server in the project. On Hetzner there is no narrower option: Cloud API tokens are project-scoped with Read or Read & Write and no per-resource scoping. Consolidation is presented as a cost and tidiness win; it is also a credential-blast-radius change, and nothing in the model says so.
Why it is forced, not incidental
attachToComputeInfrastructure(ownerSlug, config, environment) resolves the host with this.client.listServers() and label matching. The listing comes from the attached app's own token, so the owner's box has to be visible to it, which means same project. HetznerDriver takes a single apiToken and there is no second, narrower credential anywhere in the path.
So for the consolidation in #167:
| Before | After |
|---|---|
| loghq CI token can touch loghq's box | loghq CI token can touch statushq, bughq, stacks, localtunnels |
| bughq CI token can touch bughq's box | bughq CI token can touch all of the above |
Three pipelines that previously had blast radius over one box each now each have it over the entire fleet. A compromised CI run, a bad cloud:destroy (stacksjs/stacks#2342), or a name collision now reaches production systems that have nothing to do with the app being deployed.
The reverse is equally true and worth stating in the docs: an app in its own provider project cannot be attached at all. Isolation and co-hosting are mutually exclusive under this design, and that tradeoff should be a stated, deliberate choice rather than something discovered afterwards.
What is missing
Not necessarily code. The minimum is that the model is honest about it:
- Document the tradeoff on
attachToand on the consolidation flow: attaching requires shared project scope, and therefore shared credential reach. - Surface it in the plan.
attach --dry-runshould state which other servers the credential in use can reach, so the operator sees the radius before approving. - Scope down where the provider allows it. Hetzner does not today, but AWS and others do, so the driver interface should not assume "one all-powerful token" is the only shape.
- Consider a host-side attach path that does not need the attached app's token to enumerate the owner's project at all, for example the owner registering the site and the attached app deploying to a known target. That would restore isolation without giving up consolidation.
Acceptance criteria
attachTodocuments that it requires the same provider project and what that grants- The attach plan reports the other resources the credential can reach
- The driver interface allows a narrower credential where a provider supports one
- The docs state plainly that per-project isolation and attaching are mutually exclusive
Related
- #167 — server consolidation
- #168 — port allocation on a shared host
- stacksjs/stacks#2342 — the CLI surface, including
cloud:destroy
Found while deciding whether to attach loghq to the statushq box or give it its own project, against ts-cloud 0.7.114.