@wpelevator/tug 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +7 -3
  2. package/dist/backup-restore.d.ts +1 -1
  3. package/dist/backup-restore.js +3 -1
  4. package/dist/backup-scheduler.js +3 -1
  5. package/dist/cli-build.d.ts +1 -1
  6. package/dist/cli-build.js +4 -1
  7. package/dist/cli-process.d.ts +13 -0
  8. package/dist/cli-process.js +46 -0
  9. package/dist/cli-progress.d.ts +11 -0
  10. package/dist/cli-progress.js +37 -0
  11. package/dist/cli-prompt.js +5 -4
  12. package/dist/cli-selection.d.ts +13 -0
  13. package/dist/cli-selection.js +39 -0
  14. package/dist/commands/backup.js +48 -31
  15. package/dist/commands/context.d.ts +2 -5
  16. package/dist/commands/context.js +50 -28
  17. package/dist/commands/control.js +55 -42
  18. package/dist/commands/credential.js +21 -2
  19. package/dist/commands/deploy.js +97 -70
  20. package/dist/commands/hold.js +12 -4
  21. package/dist/commands/host.d.ts +2 -2
  22. package/dist/commands/host.js +13 -8
  23. package/dist/commands/node.d.ts +1 -0
  24. package/dist/commands/node.js +103 -71
  25. package/dist/commands/observe.js +7 -2
  26. package/dist/commands/restore.js +71 -50
  27. package/dist/commands/secret.js +8 -3
  28. package/dist/commands/selection.d.ts +22 -0
  29. package/dist/commands/selection.js +90 -0
  30. package/dist/commands/service.js +17 -6
  31. package/dist/commands/volume.js +3 -2
  32. package/dist/control.js +95 -47
  33. package/dist/credentials.js +5 -0
  34. package/dist/db-backup-destination.d.ts +14 -0
  35. package/dist/db-backup-destination.js +28 -0
  36. package/dist/db-locks.d.ts +10 -0
  37. package/dist/db-locks.js +23 -0
  38. package/dist/db-transaction.d.ts +3 -0
  39. package/dist/db-transaction.js +24 -0
  40. package/dist/image.d.ts +9 -0
  41. package/dist/image.js +19 -0
  42. package/dist/util.d.ts +2 -0
  43. package/dist/util.js +6 -0
  44. package/docs/access-control.md +166 -0
  45. package/docs/architecture.md +254 -0
  46. package/docs/backups.md +160 -0
  47. package/docs/cli.md +182 -0
  48. package/docs/compose.md +245 -0
  49. package/docs/configuration.md +52 -0
  50. package/docs/control-plane.md +112 -0
  51. package/docs/development.md +140 -0
  52. package/docs/getting-started.md +75 -0
  53. package/docs/healthchecks.md +51 -0
  54. package/docs/migration.md +113 -0
  55. package/docs/networking.md +150 -0
  56. package/docs/nodes.md +143 -0
  57. package/docs/observability.md +213 -0
  58. package/docs/real-host-evaluation.md +224 -0
  59. package/docs/releases.md +39 -0
  60. package/docs/roadmap.md +234 -0
  61. package/docs/security.md +198 -0
  62. package/docs/service-placement.md +72 -0
  63. package/docs/standalone-binary.md +79 -0
  64. package/docs/swarm-comparison.md +197 -0
  65. package/docs/terminology.md +74 -0
  66. package/docs/testing.md +254 -0
  67. package/docs/troubleshooting.md +33 -0
  68. package/package.json +6 -3
@@ -0,0 +1,197 @@
1
+ # Tug compared with Docker Swarm
2
+
3
+ Assessment date: 2 November 2026. Tug baseline: package version 0.9.0, checkout `355bf61` ("Improve site recovery and deployment visibility", #58). The checkout advanced during review; the final assessment reflects that revision, including ingress acknowledgement and the documented trusted-author access model. This report compares implemented behavior with Docker Engine Swarm mode, not discontinued Classic Swarm. Recommendations below are proposals, not shipped capabilities.
4
+
5
+ ## Assessment
6
+
7
+ **Tug offers a more integrated workflow for a small trusted team hosting Compose applications; Swarm offers substantially stronger cluster orchestration.** Tug's advantage is the combination of source builds, registry-free application image delivery, managed HTTPS, scoped operator access, deployment history and workload backup/restore. Swarm's advantage is continuous scheduling, stable cross-host service networking, richer update policies and an available replicated control plane.
8
+
9
+ There is considerable overlap. Tug implements its own desired-state distribution, node identities, placement, health reporting, container reconciliation, rolling replacement and rollback. Those are orchestration responsibilities, even though Tug uses ordinary Docker containers instead of Swarm services. Calling Tug merely a deployment CLI understates its scope; treating it as a Swarm-equivalent scheduler overstates its recovery behavior.
10
+
11
+ For the [target workload](roadmap.md#goal)—one to a few dozen sites on two to ten VPSes—Tug's integrated operating workflow is a credible reason to exist. The strongest case for Swarm is an application whose stateless capacity must recover automatically after a machine fails. Neither tool alone makes a database or local uploads highly available.
12
+
13
+ This is a source and documentation assessment, not a comparative benchmark or a live Swarm failure test. “Better” below means better suited to a stated requirement, not a measured performance or reliability ranking.
14
+
15
+ ## Capability overlap and differences
16
+
17
+ | Concern | Tug today | Docker Swarm mode | Assessment |
18
+ | --- | --- | --- | --- |
19
+ | Runtime | A separate Tug agent drives each node's Docker Engine API; Compose normalizes input on the operator machine. | Orchestration and worker execution are built into Docker Engine. [D1] | Swarm requires less custom runtime software; Tug controls the complete deployment workflow. |
20
+ | Application definition | Current `docker compose config` normalization, followed by Tug's explicit supported-key allowlist and `x-tug` extensions; `deploy`, top-level `configs`/`secrets` and custom networks are rejected. | `docker stack deploy` consumes legacy Compose v3, with Swarm deployment settings; it does not support the whole current Compose specification. [D2] | Neither accepts every local Compose project unchanged. |
21
+ | Desired state | Control stores revisions; agents fetch their assigned state and repair local containers while connected. | Managers maintain services through scheduled tasks. [D3] | Strong overlap; the scheduling boundary differs. |
22
+ | Placement and replicas | Named nodes or matching labels; one container per service per node; explicit `tug scale`; no capacity-aware admission. | Replicated/global services, constraints, spread preferences and CPU/memory reservations; multiple replicas can occupy one node when constraints allow. [D4] | Swarm is more flexible for capacity and placement. |
23
+ | Worker failure | Stale/unhealthy endpoints are withdrawn; existing placement is not automatically reassigned. | Replacement tasks are scheduled onto available eligible nodes. [D1] | Swarm can restore stateless replica count without an operator. |
24
+ | Global mode | Expands to a concrete list of matching nodes at deploy or scale. | Maintains a task on each eligible available node, including new nodes. [D4] | The matching name hides different lifecycle behavior. |
25
+ | Service discovery | Local Docker DNS; remote names use health-filtered `extra_hosts` entries pointing at host addresses and published ports. | Overlay connectivity with service DNS and VIP or DNS round-robin discovery. [D5] | Swarm provides a more stable cross-node application interface. |
26
+ | External traffic | Managed Caddy routes hostnames to serving endpoints and obtains HTTPS certificates. | Routing mesh exposes a service port across nodes; host publication can bypass it. [D6] | Tug bundles web ingress; Swarm needs a reverse proxy for a comparable hostname/ACME workflow. |
27
+ | Port exposure | Ports without an explicit host IP bind to the node address or loopback. | Routing-mesh publication is reachable on all node IPs; internal overlay services can remain unpublished. [D6] | Tug guards accidental public binding, but remote internal services need host ports that Swarm's overlay avoids. |
28
+ | Rolling deployment | Opt-in healthy candidate, ingress switch and drain; requires managed ingress, an `x-tug.health` check and exactly one published port. | Configurable start/stop order, batches, delays, monitoring and failure action. [D7] | Tug covers its web use case; Swarm supports broader rollout policies. |
29
+ | Rollback | New project deployment from earlier desired state; optional automatic rollback after reported deployment failure. | Manual or automatic rollback of an individual service update. [D8] | Tug groups history around projects; neither rollback reverses data changes or creates an atomic application transaction. |
30
+ | Images | Build on control or locally, deliver saved application images over mTLS; published images must resolve to a digest before publication. | Uses available images, normally distributed through a registry; tags can be resolved to digests. [D2][D4] | Tug integrates build/delivery; digest pinning itself overlaps. |
31
+ | Secrets and config | Secrets encrypted in control storage, released by placement/reference and substituted into container environment; no general managed config-file object. | Encrypted secrets mounted as in-memory files on Linux; separate file-based config objects. [D9][D10] | Swarm has the stronger runtime secret interface; Tug fits environment-based applications. |
32
+ | Identity | Private CA, node certificates with automatic renewal, key-based revocation and named operator credentials. | Node mTLS, automatic certificate renewal, role-specific join tokens and coordinated CA rotation. [D11] | mTLS and renewal overlap; Tug adds operator policy, Swarm has a more complete CA lifecycle. |
33
+ | Operator access | Built-in `read`/`deploy`/`admin`, project scope and actor-attributed deployment events; no project-specific node grants. | Docker daemon access is all-or-nothing by default; authorization plugins can add policy. [D12] | Tug has useful built-in team/CI controls, subject to its trusted-author model. |
34
+ | Control availability | One SQLite-backed control plane; backup/restore recovery; no implemented automatic standby promotion. | Replicated manager state with quorum and leader election; three managers tolerate one manager failure. [D13] | Swarm supports control-plane HA. |
35
+ | Persistent data | Node-local volumes, guarded deletion and integrated restic backup/restore; external shared storage remains operator-managed. | Volumes and drivers; scheduling does not replicate local volume contents. [D4] | Tug bundles recovery tooling; neither supplies database or storage HA. |
36
+ | One-off work | `tug exec`, service-derived `tug run`, scheduled backup commands and restore holds. | Replicated/global jobs are available through the service CLI. [D14] | Swarm has job primitives; Tug bundles application operating tasks. Neither fact implies a general built-in cron service. |
37
+ | Diagnostics | JSON or human-readable status, events, doctor and live container commands; metrics, alerting and webhooks remain planned. | Task states, service inspection/logs and Docker events. [D15][D16] | Neither core product is a complete monitoring stack. |
38
+
39
+ Tug evidence: [Architecture](architecture.md), [Compose extensions](compose.md), [Networking](networking.md), [Access control](access-control.md), [Control plane](control-plane.md), [Backups](backups.md) and [Observability](observability.md). Code references for the less obvious differences follow below. Docker references are listed under [Sources](#sources).
40
+
41
+ ## Where Tug is better suited
42
+
43
+ ### A complete path from source to an operating site
44
+
45
+ Tug can accept a source context, build the application image on control, retain it as an immutable artifact and deliver it to agents over their existing authenticated connection. Local builds are also supported. An operator does not need to set up an application registry to use that path. Published third-party images still come from their upstream registries; this is not an offline deployment system.
46
+
47
+ Swarm's stack workflow expects images to be built separately, and its documented distribution workflow uses a registry; `build` is ignored by stack deployment. [D2] A Swarm installation can add CI builds and a registry, but Tug already integrates those steps. Its corresponding cost is concentrating build execution, artifact bandwidth and storage on control. See [image delivery](control-plane.md#image-delivery-without-a-registry) and [`src/build.ts`](../src/build.ts).
48
+
49
+ Tug also refuses to publish an unresolved image tag. Swarm can warn and let workers resolve it independently; explicit digest references avoid that fallback. [D4] This gives Tug a stricter default for published-image identity, rather than a unique ability to pin images.
50
+
51
+ ### The small-site operating workflow
52
+
53
+ Tug combines hostname ownership checks, Caddy/ACME, per-project credentials, deployment audit history, scheduled restic backups, a backup catalog and guarded restores. This closely matches the work of operating WordPress sites. These are useful product integrations beyond the scheduling and service-networking primitives in Swarm's core. A fair alternative is Swarm plus a proxy, CI, backup tooling and an access layer; that assembled system can close much of the workflow gap, with more integration to own.
54
+
55
+ Backup convenience is not a consistency guarantee. Database commands and file snapshots can describe different instants; live database files need an application-appropriate backup method. See [Backups](backups.md). Likewise, a project rollback restores desired images/configuration, not database schema, uploads or external effects.
56
+
57
+ ### An outbound management channel
58
+
59
+ A Tug node needs only outbound access to the control mTLS listener for desired state, image downloads and live commands. That is a useful fit for restricted nodes and management access through NAT. Workload traffic is a separate requirement: remote ingress and inter-node services still need reachable node addresses on an operator-provided network.
60
+
61
+ Swarm requires reachable manager endpoints and, for overlay networking, peer connectivity on TCP/UDP 7946 and UDP 4789; managers use TCP 2377. [D17] Those are private-network requirements, not a reason to expose the ports publicly. Swarm can operate over a suitable VPN behind NAT, so “Swarm cannot use NAT” is too broad. The actual Tug advantage is avoiding a peer-to-peer overlay requirement for its management channel.
62
+
63
+ ### Scoped team and CI access
64
+
65
+ Tug exposes a narrow application-oriented API with named actors and built-in roles/projects. This is more convenient than granting a deployment job unrestricted manager Docker access. Swarm's default authorization can be extended with plugins, so it is inaccurate to say Swarm cannot have scoped access. [D12] Docker events also exist; Tug's distinction is an application deployment history that records the acting credential, rather than the mere existence of events. [D16]
66
+
67
+ These controls do not make Tug a hostile-tenant platform. Deployment authors must be trusted with their selectable hosts, co-located workloads and runtime secrets. See the current [access boundaries](access-control.md) and [security findings](security.md); some findings remain partial, and this report does not re-audit them.
68
+
69
+ ## Where Tug is worse or more limited
70
+
71
+ ### Scheduling is resolved at an operation, not continuously
72
+
73
+ At deploy, [`src/control.ts`](../src/control.ts) resolves label/global placement to named nodes. Label candidates are sorted by name and sliced to the requested count. `tug scale` also selects a concrete node list, using registration order. Agents reconcile that assignment; there is no controller continuously moving replicas to replace lost nodes or adding newly enrolled nodes to global services.
74
+
75
+ There are several practical consequences:
76
+
77
+ - A service on `app1` and `app2` loses capacity when `app1` disappears; withdrawing its endpoint does not create a replacement on `app3`.
78
+ - Requesting three replicas with only two eligible nodes records two. The unmet third replica does not remain as a pending demand. Scaling to zero is rejected.
79
+ - Each service gets at most one steady-state replica on a node. Repeated selection of the first candidates across different services is not cluster-wide load balancing.
80
+ - CPU/memory settings can constrain individual containers, but Tug does not reserve capacity before placement. A deployment can overload a selected node.
81
+ - `node disable` revokes management access and removes endpoints; it neither stops the existing containers nor drains them elsewhere.
82
+
83
+ See [`src/control.placement.test.ts`](../src/control.placement.test.ts), [`src/container-spec.ts`](../src/container-spec.ts) and the [CLI](cli.md). Swarm has continuing task replacement and resource-aware placement, and its drain operation moves eligible service tasks. [D3][D14][D18] These semantics matter more than both tools having a `replicas` field.
84
+
85
+ ### Remote service names are a weaker contract
86
+
87
+ Tug's remote names map to host addresses, require published ports and do not provide a service VIP or general load balancing. A changed hosts entry changes the client container definition, which can recreate or roll that client. This couples endpoint churn to otherwise unrelated application restarts.
88
+
89
+ Rolling web services are especially constrained: the active host port changes per revision, but a hosts entry carries no port. A remote client must use the ingress hostname, rather than the declared service host port. On the same node, both old and new revisions can share the service alias during overlap, allowing local name traffic to reach a candidate before it is healthy. Only the ingress path supplies the documented readiness gate. See [Networking](networking.md#rolling-services-and-service-names).
90
+
91
+ Swarm's DNS/VIP and overlay remove the need to publish an internal database port to the host network and provide a stable service endpoint across task changes. [D5] This is a substantial advantage for applications with many cross-node dependencies.
92
+
93
+ ### Web rollouts cover a narrower failure model
94
+
95
+ Tug's candidate/drain mechanism is useful for ingress-fronted stateless services, and a rejected candidate leaves the prior revision serving. Deployment success also waits for enabled managed-ingress nodes to acknowledge the current routing configuration when routes change; this verifies applied configuration, not external DNS, certificate issuance or a successful public request. Without an enabled ingress node, convergence covers service health only. See [deployment convergence](architecture.md#desired-state-and-reconciliation).
96
+
97
+ It is not a general rolling-update engine for workers, TCP-only services or services with several published ports. There is no configurable cluster-wide batch size or tolerated failure ratio. Swarm exposes those update controls. [D7]
98
+
99
+ Local reconciliation also differs: [`ensureContainer` in `src/reconciler.ts`](../src/reconciler.ts) restarts a stopped matching container but reuses a running matching container. Repeated failed health checks do not, by themselves, replace that unhealthy running container. Swarm replaces failed/unhealthy tasks. [D3] Neither approach establishes that a service is application-correct merely because a health check passes.
100
+
101
+ ### One control plane is a real availability limit
102
+
103
+ Tug intentionally has one writer. Existing containers and already-configured Caddy can keep serving during a control outage, but `pollOnce` fetches work before reconciling: without control, the agent does not keep applying an independently cached desired state. Docker restart policies can still restart configured containers. Deploys, endpoint/config updates and newly scheduled backup work wait for control to return. See [`src/agent.ts`](../src/agent.ts) and [`src/backup-scheduler.ts`](../src/backup-scheduler.ts).
104
+
105
+ Swarm can run with one manager too; three managers are required to tolerate one manager loss while retaining quorum, not to run Swarm at all. Existing tasks continue if quorum is lost, but orchestration cannot proceed. [D13] Tug's backup/restore path is disaster recovery, not an equivalent to replicated managers.
106
+
107
+ ### Secret handling and certificate recovery have gaps
108
+
109
+ Tug's secret values end up in container environments and are consequently visible through sufficiently privileged Docker/container access. Redacting a CLI inspection response does not remove them from Docker's configuration. Swarm's Linux in-memory secret files avoid routinely putting secret values in environment metadata, although host administrators and authorized applications remain trusted. [D9]
110
+
111
+ Both systems secure management traffic; neither encrypts all workload traffic by default. Swarm has opt-in encrypted overlays, including separate configuration for encrypted ingress. Tug supplies no workload encryption and relies on application TLS or the operator's network. [D5]
112
+
113
+ Swarm also has coordinated root CA rotation and optional manager autolock. [D11][D19] Tug renews leaf certificates and supports replacing node identities, but has no equivalent coordinated CA rotation workflow. Its CA/master key and workload recovery secrets need careful recovery planning. This is a difference in available mechanisms, not evidence of a particular exploitable flaw.
114
+
115
+ ## What happens when something fails?
116
+
117
+ | Event | Tug | Swarm |
118
+ | --- | --- | --- |
119
+ | A local container exits | Docker's configured restart policy may restart it; a connected agent also starts/recreates missing or stopped desired containers. | Desired service state drives replacement tasks, subject to restart policy. [D3] |
120
+ | One application host disappears | Remaining replicas may serve; control withdraws stale endpoints as other agents receive updates. Placement stays unchanged until an operation changes it. | With manager quorum, eligible capacity and accessible images/data, replacement tasks can restore stateless capacity. [D1] |
121
+ | An isolated host still runs its database | Losing its management identity or endpoint does not stop its containers. Operator fencing is required before another writer is promoted. | Scheduling a replacement is not proof the isolated original writer has stopped. Quorum is not database fencing. This is an operational inference from the scheduling and storage model. [D3][D4] |
122
+ | The control host fails | Existing traffic may continue; no new control-driven work until restart/restore. | A three-manager cluster can continue managing after one manager loss; a single-manager cluster has no such tolerance. [D13] |
123
+ | An ingress host fails | Other configured ingress nodes help only if DNS or an external traffic mechanism directs clients to them. Tug does not move the public endpoint automatically. | Routing mesh helps through another reachable node; moving a public IP, DNS or external load balancer still requires an external mechanism. [D6] |
124
+ | A database host loses its disk | Restore data and recover placement; backup frequency determines potential data loss. | Local volumes do not follow tasks. A replacement on a different host can have a new empty volume unless storage/placement is handled explicitly. [D4] |
125
+ | A deployment introduces a bad database migration | Image rollback does not reverse it; an application recovery procedure is required. | Service rollback also does not reverse database writes. |
126
+
127
+ The key availability lesson for the reference WordPress topology is that extra PHP replicas do not remove a single database, uploads server or public ingress endpoint as a failure point. Swarm improves scheduling; it does not supply the missing data architecture. Tug improves integrated recovery; it does not turn recovery into uninterrupted service.
128
+
129
+ ## Choosing between them
130
+
131
+ | Requirement | Better starting point | Reason |
132
+ | --- | --- | --- |
133
+ | A few sites, a trusted operator, existing private network, acceptable manual host recovery | Tug | Integrated build, HTTPS, backup/restore and operating commands align with the project's scope. |
134
+ | Management of restricted or NATed nodes | Tug | Agents only initiate control connections; separately establish any needed workload connectivity. |
135
+ | Many replicas, several tasks per host, continuous capacity recovery or planned host draining | Swarm | Those are core scheduler responsibilities already implemented there. |
136
+ | Frequent cross-node service communication | Swarm | Stable DNS/VIP/overlay networking avoids Tug's hosts-file and published-port constraints. |
137
+ | Continued deployments after a control machine fails | Swarm with replicated managers | Tug currently requires recovery of its single control plane. |
138
+ | A fully HA database-backed application | Either needs additional design | Database replication/fencing, shared data, traffic failover and restore testing determine the outcome. |
139
+ | Mutually untrusted customers deploying arbitrary code | Neither default setup | Tug's documented trust model and default Docker daemon authorization do not provide that product boundary. |
140
+
141
+ No measurements here support claiming Tug is faster, uses less memory, scales further or is more reliable than Swarm. Swarm's orchestrator is already in Docker Engine; Tug adds Node processes and supporting containers. Tug avoids overlay machinery, but that alone does not establish a lower total operating cost. Compare equivalent deployments, including the proxy, builds, registry, backups and access layer, before making a resource claim.
142
+
143
+ ## Recommendations for Tug
144
+
145
+ 1. **Keep the product centered on operating small applications.** Build/delivery, safe deployment history, HTTPS, backup/restore and useful CI access are its strongest differentiators. Reimplementing every Swarm scheduler feature would substantially increase scope.
146
+ 2. **Document scheduling semantics precisely.** Describe replicas/global as resolved placement, expose requested versus assigned capacity and explain that stale endpoint withdrawal is not rescheduling. Any future automatic movement should start with explicit stateless eligibility and a clear policy for isolated old containers; local-volume workloads should remain pinned unless a recovery workflow handles their data.
147
+ 3. **Improve discovery before adding more scheduling.** Avoid recreating clients merely because remote endpoints change, and provide a stable address/port contract for rolling services if cross-node clients become a priority. Keep overlay-free networking as an explicit tradeoff rather than claiming networking parity.
148
+ 4. **Prioritize recovery evidence and visibility.** Test control outage during rollout, full control restore, lost-node behavior and restore freshness; add the planned metrics/alerts. A continuously running backup service is less valuable than knowing a recent backup can actually be restored.
149
+ 5. **Borrow narrow lifecycle features where justified.** File-based secret delivery, a documented CA rotation/recovery procedure, graceful node retirement and clearer insufficient-capacity reporting would improve today's model without requiring a general scheduler. These are proposals, not commitments added by this report.
150
+
151
+ Using Swarm as a backend is technically plausible: Tug could retain builds, access policy, audit, ingress and backups while delegating services to Swarm. That would require a new scheduling/identity boundary, changes to rollout and live-command targeting, different image distribution and continued handling of node-local backups/restores. Merely enabling Swarm mode would not manage Tug's existing standalone containers, and Swarm drain explicitly excludes such containers. [D18] Treat a backend change as a separate architecture decision justified by requirements for continuous failover or service networking; this comparison alone does not justify replacing the runtime.
152
+
153
+ ## Sources
154
+
155
+ Tug implementation reviewed: [`src/control.ts`](../src/control.ts) (deploy placement, scale, access checks, convergence), [`src/agent.ts`](../src/agent.ts) (work fetch before reconciliation and live operations), [`src/reconciler.ts`](../src/reconciler.ts) (local replacement and rollout state), [`src/container-spec.ts`](../src/container-spec.ts) (resources and secret substitution), [`src/ingress.ts`](../src/ingress.ts), [`src/pki.ts`](../src/pki.ts), [`src/agent-credential.ts`](../src/agent-credential.ts), [`src/backup-scheduler.ts`](../src/backup-scheduler.ts) and [`src/backup-restore.ts`](../src/backup-restore.ts). Existing behavior coverage includes [`src/control.placement.test.ts`](../src/control.placement.test.ts), [`src/reconciler.test.ts`](../src/reconciler.test.ts), [`src/control.rollout.test.ts`](../src/control.rollout.test.ts), [`src/agent.network.test.ts`](../src/agent.network.test.ts), [`src/control.mtls.test.ts`](../src/control.mtls.test.ts) and [`tests/integration`](../tests/integration).
156
+
157
+ Docker primary documentation, consulted 2 November 2026. The service CLI is used to verify actual Swarm options; the Compose specification describes policy vocabulary and does not imply that every current Compose feature is accepted by `docker stack deploy`.
158
+
159
+ - D1: [Swarm mode overview][D1].
160
+ - D2: [Deploy a stack to a swarm][D2].
161
+ - D3: [How services work][D3].
162
+ - D4: [Deploy services: images, placement and volumes][D4].
163
+ - D5: [Swarm service networking and encryption][D5].
164
+ - D6: [Routing mesh][D6].
165
+ - D7: [Update and rollback policy vocabulary][D7].
166
+ - D8: [Service update and rollback CLI][D8].
167
+ - D9: [Docker secrets][D9].
168
+ - D10: [Docker configs][D10].
169
+ - D11: [Swarm PKI and CA rotation][D11].
170
+ - D12: [Docker authorization plugins and default authorization][D12].
171
+ - D13: [Swarm administration, quorum and recovery][D13].
172
+ - D14: [Service creation CLI, resources and job modes][D14].
173
+ - D15: [Task states][D15].
174
+ - D16: [Docker events][D16].
175
+ - D17: [Swarm network prerequisites][D17].
176
+ - D18: [Drain a node][D18].
177
+ - D19: [Manager autolock][D19].
178
+
179
+ [D1]: https://docs.docker.com/engine/swarm/
180
+ [D2]: https://docs.docker.com/engine/swarm/stack-deploy/
181
+ [D3]: https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/
182
+ [D4]: https://docs.docker.com/engine/swarm/services/
183
+ [D5]: https://docs.docker.com/engine/swarm/networking/
184
+ [D6]: https://docs.docker.com/engine/swarm/ingress/
185
+ [D7]: https://docs.docker.com/reference/compose-file/deploy/
186
+ [D8]: https://docs.docker.com/reference/cli/docker/service/update/
187
+ [D9]: https://docs.docker.com/engine/swarm/secrets/
188
+ [D10]: https://docs.docker.com/engine/swarm/configs/
189
+ [D11]: https://docs.docker.com/engine/swarm/how-swarm-mode-works/pki/
190
+ [D12]: https://docs.docker.com/engine/extend/plugins_authorization/
191
+ [D13]: https://docs.docker.com/engine/swarm/admin_guide/
192
+ [D14]: https://docs.docker.com/reference/cli/docker/service/create/
193
+ [D15]: https://docs.docker.com/engine/swarm/how-swarm-mode-works/swarm-task-states/
194
+ [D16]: https://docs.docker.com/reference/cli/docker/system/events/
195
+ [D17]: https://docs.docker.com/engine/swarm/swarm-tutorial/
196
+ [D18]: https://docs.docker.com/engine/swarm/swarm-tutorial/drain-node/
197
+ [D19]: https://docs.docker.com/engine/swarm/swarm_manager_locking/
@@ -0,0 +1,74 @@
1
+ # Terminology
2
+
3
+ This page defines Tug's vocabulary and records the naming review of 1 November 2026. Definitions describe the current implementation. The review findings below carry a status: **Implemented** items are the current interface (the [CLI reference](cli.md) is authoritative), **Partly** items are done in documentation or help text but not in names, and **Recommendation** items are proposals that do not exist yet.
4
+
5
+ ## Core concepts
6
+
7
+ Tug has a control plane and managed nodes. Each node runs an agent, which connects to the control plane and manages that node's containers. Operators use the `tug` CLI to deploy projects made up of Compose services.
8
+
9
+ | Term | Meaning in Tug | Usage guidance |
10
+ | --- | --- | --- |
11
+ | Control plane | The central software: API, state store, deployment coordination, image builds and delivery, certificate issuance and backup scheduling. | Use the full name in prose; **control API** specifically means its HTTP/WebSocket interface. Existing `control-*` commands and the `control` runtime mode abbreviate it. |
12
+ | Control name | The name control answers to, generated from its CA (`control-<hash>.tug`). | Clients derive it from the CA they pin and verify the server against it, never against the address they dial. Distinct from the control URL, which is only an address. |
13
+ | Control-plane host | The machine running the control plane. | Distinguish the machine from the software. Installing the control plane does not automatically register a workload node. |
14
+ | Node | A registered Docker host on which Tug places services, represented by a stable record with an immutable generated ID (`node_...`) beside its chosen name. | Use for placement, addresses, labels, connectivity and lifecycle operations. The record remains when its agent stops or its identity is replaced. The ID never changes and is never reused after `node remove`, even if the name is. |
15
+ | Agent | The process/container on a node that fetches work, reconciles containers through the Docker Engine API, reports status and handles operations. | Use for software behavior, logs, configuration, upgrades and connections. A node runs an agent. |
16
+ | Host | An underlying Linux machine, whether or not registered with Tug. | Use in OS, SSH, Docker and resource discussions; use **node** for a managed deployment target. Qualify hostname fields separately. |
17
+ | Project | A named application instance grouping services, secrets, deployment history and volume records. | Keep the Compose term. Different projects can represent separate installations of the same application. Project scope is not host or tenant isolation. |
18
+ | Service | A named Compose component, such as `web` or `db`, run on selected nodes. | Keep the Compose meaning; this is not a Kubernetes Service networking object. |
19
+ | Replica | One steady-state running copy of a service on a selected node. | Currently at most one steady-state copy per project/service/node; rolling replacement temporarily overlaps old and new containers. |
20
+ | Placement | The selection of nodes on which a service runs. | Names pin nodes; labels select matching nodes when deploying or scaling. Do not imply continuous rescheduling or resource-aware scheduling. |
21
+ | Deployment | A recorded attempt to publish and converge a project's desired state, with its own ID and lifecycle. | Use for the operation/history record, including a rollback deployment. |
22
+ | Desired-state revision | The immutable identity of a desired-state record. | Deploy and rollback use their deployment ID as the revision; scaling can create a separate revision. Distinguish this from a display version. |
23
+ | Display version | The supplied or generated label shown for a deployment; current option: `deploy --version`. | It can be reused; it is not an image digest or the installed Tug version. |
24
+ | Rollout | Applying a desired-state change to running services. | A deployment has a rollout; `recreate` and `rolling` are service rollout strategies. |
25
+ | Service revision | A particular rendered service configuration, identified by a configuration hash for rolling replacement. | Qualify **revision**: this differs from a project's desired-state revision. |
26
+ | Ingress | The managed HTTP reverse proxy, currently Caddy, and its hostname routes. | Introduce as **HTTP ingress (managed reverse proxy)**. An ingress node has this capability; it is not a separate machine type. |
27
+ | Node address | The node's sole inter-node `address`, used for cross-node service names, ingress upstreams and private published-port binding. | Set it to a private IP on the operator's existing network. Tug does not provide an overlay, tunnel or workload-traffic encryption; local volumes are not shared by networking. |
28
+ | Operator credential | A certificate, private key, pinned CA and default control-plane address used by a person or automation. | Created and managed with `credential create`, `credential list` and `credential revoke`; transport uses mTLS. **Operator** includes CI. |
29
+ | Node identity | The registered node principal and authorized public key; its agent holds the private key and certificate. | Certificate renewal keeps the key. Identity replacement changes it and invalidates certificates for the old key. |
30
+ | Join key | The encoded, short-lived bootstrap credential and node binding used for self-service enrollment. | Explain as an **enrollment credential** containing private-key material. Current variables: `TUG_JOIN_KEY` and `TUG_JOIN_KEY_FILE`. |
31
+ | Workload backup | A restic snapshot of a configured service command's output or a volume's files. | Distinguish target, backup run and resulting snapshot. |
32
+ | Control-plane backup | An archive of Tug's control-plane state and retained artifacts. | Current command: `control backup`. It does not capture workload volume contents. See [Control plane](control-plane.md#backup-and-restore). |
33
+
34
+ ## Review findings
35
+
36
+ Priorities describe operator confusion, not security severity. The evidence column describes the source at the time of the review. High-priority items misdescribe an operation's target, authentication or infrastructure capabilities.
37
+
38
+ | Priority | Status | Evidence at the 1 November 2026 review | Confusion | Recommendation |
39
+ | --- | --- | --- | --- | --- |
40
+ | High | **Implemented.** `credential create`, `credential list` and `credential revoke`, `/api/v1/credentials`, `operator_credentials`, `credential.*` audit events and the docs use operator credential. | `src/cli.ts`: `token-create` creates an operator credential; `token-list` lists API tokens. README and `docs/architecture.md` still describe bearer tokens. | Readers expect an API key or bearer workflow, while the implementation requires certificate credentials. | Standardize on **operator credential** for the distributed secret and **operator identity** for its named principal. Rename `token-*` to `credential-*`, updating `/api/v1/tokens`, protocol types, errors, persistence names and audit events together. Reserve token for an actual token mechanism. |
41
+ | High | **Implemented** in help, errors and docs: `doctor` reports node diagnostics through the agent, `logs` says service container logs, `node remove` says what it deletes and that containers stay. | `doctor --nodes` says “every registered agent”; `--node` says “one agent”; `logs` says “assigned node agent”. `node remove` mentions an agent token. | Machine, process and credential become interchangeable, obscuring what removal, health and repair affect. | Keep **node** and **agent** distinct. Say “diagnostics on every node” and “service container logs”; mention the agent when relevant, such as “check agent logs on node app1”. |
42
+ | High | **Implemented.** `node replace` and `node repair`; help and docs explain the SSH flow, and `node disable` is described as blocking authentication, not draining. | `node replace` installs a new key, retaining node ID, metadata and placement. `node repair` moves corrupt credential documents aside and replaces identity. | The verbs suggest replacing a VPS or repairing arbitrary host/service state. | Prefer `node replace` and `node repair`, with help explaining SSH and agent restart. Keep `node disable` for blocking authentication; explain that it withdraws service endpoints but does not stop containers or gracefully drain workloads. |
43
+ | High | **Implemented.** `node enroll`, `EnrollmentCredentialResponse`, `enrollment_credentials`; the join key remains the encoded envelope. | `node-enroll` creates a join key and prints a command/cloud-init; the agent later calls `/agent/v1/enroll`. The response is `EnrollTokenResponse`. | Preparing enrollment and enrolling a host appear to be the same action. Join key, enrollment token and bootstrap certificate appear to be separate mechanisms. | Prefer `node enroll` for the operator command. Use **enrollment credential** for its bootstrap secret and **enrollment** for the actual agent flow; rename the response accordingly. Explain join key as the current encoded envelope. |
44
+ | High | **Implemented.** `TUG_SERVICE_NAMES` and cross-node service names (hosts-file entries) throughout; "service DNS" is reserved for a possible future DNS implementation. | At the review, `docs/networking.md` and `TUG_SERVICE_DNS` called the feature service DNS; implementation injected `extra_hosts`. | The name suggests a DNS server, round-robin answers, stable service endpoints or immediate updates beyond the hosts-file contract. | Use **cross-node service names (hosts-file entries)** and `TUG_SERVICE_NAMES` for the interface. Reserve **service DNS** for an actual DNS implementation. Keep same-node Docker DNS distinct. |
45
+ | High | **Implemented** in the docs, and the runtime image and fake VPS image no longer ship Compose; the control-plane host still runs the control container with Compose. | README says agents apply Docker Compose; architecture says Docker Engine API. Compose and networking docs retain descriptions of Compose projects and `compose up` from the earlier runtime. | Compose sounds like the execution engine on nodes, obscuring installation and troubleshooting requirements. | Describe Tug as **deploying Compose applications through the Docker Engine API**. Compose normalizes input on the operator machine; agents manage containers directly. Update runtime descriptions against the implementation. |
46
+ | Medium | **Partly.** `tug-runtime` is the executable and prose says control plane and control-plane host; the `control` mode and `control-*` commands remain. | Prose alternates between control, control plane and control host; the `tug-node` executable starts either `control` or `agent`. | Component, machine and executable blur together. The shared runtime sounds specific to workload nodes. | Use **control plane** and **control-plane host** in prose. Keep `control` as an explicit command prefix/runtime mode. Prefer `tug-runtime` for the shared executable and `tug` for the CLI. |
47
+ | Medium | **Partly.** Docs qualify display version, desired-state revision and service revision. `deploy --release-label` is still a recommendation. | `version`, `revision`, `observedVersion`, `observedRevision` and `rolloutRevision` describe different identities; top-level `--version` also prints the CLI version. | A reusable display label can be mistaken for convergence identity or image pinning. Revision can mean project state or service configuration. | Qualify **display version**, **desired-state revision**, **service revision** and **image digest**. Consider `deploy --release-label` for the display label without introducing another release resource. |
48
+ | Medium | **Implemented.** Only `x-tug.global` is accepted; `x-tug.placement.global` is rejected with a pointer to it. The one-copy-per-node limit is documented. | `src/compose.ts` resolves label placement by matching labels, sorting node names and selecting nodes at deploy/scale time. It accepts both `x-tug.global` and `x-tug.placement.global`. | Replicas/global can imply several copies per host, automatic failover, resource balancing or automatic expansion onto new nodes. Two global locations add alternatives. | Keep **replicas** and **global**, documenting the one-copy-per-selected-node limit and resolution timing. Keep only `x-tug.global`, the documented location. Describe placement without promising an autonomous scheduler. |
49
+ | Medium | **Partly.** [Nodes](nodes.md#status-and-diagnostics) separates connectivity from apply results and says stale is not proof the machine is off; field names are unchanged. | Node status uses `online`/`stale`, project health uses `serving`/`skipped`/`failed`, and service `rolloutState` includes `serving`/`held`. An old rolling revision can serve after a new one fails. | Online, healthy, serving and deployment succeeded can look like one condition. | Label **node connectivity**, **project apply result**, **service rollout state** and **deployment state** separately. Explain stale as no recent agent contact, not proof the machine is off. Explain skipped as an apply result. |
50
+ | Medium | **Partly.** The former `--public-address` WireGuard endpoint was removed with the Tug-owned mesh; `--address` and the connect URL flags remain. | At the review, `--address`, `--public-address`, `--control-url`, `--agent-control-url` and ingress `host` referred to different endpoints. | Public address sounded like an application's endpoint but meant the WireGuard endpoint. A connect override can look like a TLS identity change; it is not, because clients verify the generated **control name** derived from the CA, not the address. | Name **node service address**, **control-plane URL** (`controlUrl`, the address written into credentials), **CLI connect URL**, **agent connect URL** and **ingress hostname** explicitly. Consider `--service-address` for the remaining node flag. Verification identity (the control name) is separate from routing. |
51
+ | Low | **Partly.** Docs separate node labels, container labels, access roles and the ingress capability. `TUG_MODE` is still a recommendation. | Node labels are string tags; Compose labels are container metadata. `TUG_ROLE` chooses runtime behavior, `--role` grants access, and ingress is also a role. | The same words describe different levels; Kubernetes users may expect key/value node labels. | Keep **node labels**, documenting string tags and AND matching; qualify **container labels**. Use **runtime mode**, **access role** and **ingress capability**; consider `TUG_MODE` for runtime selection. |
52
+ | Low | **Implemented.** `backup password`; docs distinguish workload backups from the control-plane backup. | `backup run`/`restore` concern workload data, `control backup` concerns platform recovery, and `backup-key` prints a repository password. | Backup can imply complete disaster recovery; key does not identify the returned secret. | Qualify **workload backup** versus **control-plane backup**. Prefer `backup password` for escrow. Keep restic's **snapshot** and **repository** terms. |
53
+
54
+ ## Industry alignment
55
+
56
+ Use Compose application vocabulary with conventional infrastructure vocabulary. These comparisons concern naming and do not imply equivalent scheduling, networking or availability guarantees.
57
+
58
+ | Primary reference | Established vocabulary | Recommendation for Tug |
59
+ | --- | --- | --- |
60
+ | [Kubernetes components](https://kubernetes.io/docs/concepts/overview/components/) | Control plane, worker nodes and kubelet as node software. | Keep the machine/process distinction. **Agent** fills the conceptual node-software role; introducing kubelet, pod or controller resource names adds unrelated concepts. |
61
+ | [Nomad glossary](https://developer.hashicorp.com/nomad/docs/glossary) | A client is an agent managing tasks; a node is a machine running a client agent. | **Agent** is an established process name. Avoid **client** for Tug nodes because the CLI is also an API client. |
62
+ | [Compose application model](https://docs.docker.com/compose/intro/compose-application-model/) | Project, service, network and volume. | Keep **project**, **service** and **volume**. Application can explain a project in plain language without becoming another resource type. |
63
+ | [Kamal servers](https://kamal-deploy.org/docs/configuration/servers/) and [proxy](https://kamal-deploy.org/docs/configuration/proxy/) | Servers grouped by role; a proxy forwarding HTTP traffic. | Explain a node as a registered server and ingress as a managed reverse proxy. Keep **node** canonical because server also means application software or the API server. |
64
+
65
+ Keep **Tug** and **tug** as product/CLI names. The tugboat explanation is optional branding; boats, captains or fleets would add vocabulary operators must learn. **Cluster** may describe one control plane and its registered nodes, but should not become another required configuration object or imply high availability or automatic failover. Avoid master/slave, a standalone worker resource, or introducing controller as another name for the control plane.
66
+
67
+ ## Recommended order of work
68
+
69
+ 1. Correct authentication and runtime descriptions first. Done: the token expiry statement in `docs/cli.md` now matches the code (maximum and default 365 days), and the bearer-token, plain-HTTP, port 8080 and Compose-on-nodes descriptions were removed.
70
+ 2. Apply this vocabulary to help, errors, status descriptions and documentation. Keep literal commands, paths and variables accurate until their implementations change. Example onboarding: “Install the control plane, add a node, then deploy your Compose project. Tug runs an agent on each node.”
71
+ 3. Rename misleading interfaces as coherent changes: credential management, node identity recovery, enrollment preparation, hosts-file service names and the runtime binary are done; the possible `--service-address` rename, `deploy --release-label` and `TUG_MODE` remain. The former WireGuard endpoint flag was removed with the mesh rather than renamed. Update API paths, protocol types, persistence names where relevant, templates, tests, docs and changesets together. This is a greenfield project; prefer direct renames over compatibility aliases or migrations.
72
+ 4. Treat command grouping as a separate usability decision. The [current convention](cli.md#cli-architecture-and-naming) is `tug <subject> <verb>`, so `credential create`, `node replace` and `node enroll` group under their subject. Grouping improves discovery, but does not itself fix ambiguous nouns or verbs.
73
+
74
+ Remaining renames should test CLI help and errors, credential/identity lifecycle flows, enrollment output, config resolution and status semantics. Avoid brittle word-ban tests: agent and control remain valid in their defined contexts.