vps-deployer 1.2.0__tar.gz

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.
@@ -0,0 +1,122 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: Jan Chimiak
6
+
7
+ Licensed Work: vps-deployer, including every version from the project's
8
+ inception on 2026-08-10 onward. The Licensed Work is
9
+ Copyright (c) 2026 Jan Chimiak.
10
+
11
+ Additional Use Grant: You may make production use of the Licensed Work, provided
12
+ your use does not include offering the Licensed Work to
13
+ third parties on a hosted or embedded basis in order to
14
+ compete with Jan Chimiak's paid version(s) of the Licensed
15
+ Work. For purposes of this license:
16
+
17
+ A "competitive offering" is a Product that is offered to
18
+ third parties on a paid basis, including through paid
19
+ support arrangements, that significantly overlaps with the
20
+ capabilities of Jan Chimiak's paid version(s) of the
21
+ Licensed Work. If your Product is not a competitive
22
+ offering when you first make it generally available, it
23
+ will not become a competitive offering later due to Jan
24
+ Chimiak releasing a new version of the Licensed Work with
25
+ additional capabilities. Products that are not provided on
26
+ a paid basis are not competitive.
27
+
28
+ "Product" means software that is offered to end users to
29
+ manage in their own environments or offered as a service on
30
+ a hosted basis.
31
+
32
+ "Embedded" means including source code or executable code
33
+ from the Licensed Work in a competitive offering. Embedded
34
+ also means packaging the competitive offering so that the
35
+ Licensed Work must be accessed or downloaded for the
36
+ competitive offering to operate.
37
+
38
+ Hosting or using the Licensed Work for internal purposes
39
+ within an organization is not considered a competitive
40
+ offering. Your organization includes all affiliates under
41
+ common control.
42
+
43
+ Change Date: Four years from the date each version of the Licensed Work
44
+ is first publicly distributed.
45
+
46
+ Change License: Mozilla Public License 2.0
47
+
48
+ For information about alternative licensing arrangements for the Licensed Work,
49
+ please contact the Licensor.
50
+
51
+ Notice
52
+
53
+ The Business Source License (this document, or the “License”) is not an Open
54
+ Source license. However, the Licensed Work will eventually be made available
55
+ under an Open Source License, as stated in this License.
56
+
57
+ Business Source License 1.1
58
+
59
+ Terms
60
+
61
+ The Licensor hereby grants you the right to copy, modify, create derivative
62
+ works, redistribute, and make non-production use of the Licensed Work. The
63
+ Licensor may make an Additional Use Grant, above, permitting limited production
64
+ use.
65
+
66
+ Effective on the Change Date, or the fourth anniversary of the first publicly
67
+ available distribution of a specific version of the Licensed Work under this
68
+ License, whichever comes first, the Licensor hereby grants you rights under the
69
+ terms of the Change License, and the rights granted in the paragraph above
70
+ terminate.
71
+
72
+ If your use of the Licensed Work does not comply with the requirements currently
73
+ in effect as described in this License, you must purchase a commercial license
74
+ from the Licensor, its affiliated entities, or authorized resellers, or you must
75
+ refrain from using the Licensed Work.
76
+
77
+ All copies of the original and modified Licensed Work, and derivative works of
78
+ the Licensed Work, are subject to this License. This License applies separately
79
+ for each version of the Licensed Work and the Change Date may vary for each
80
+ version of the Licensed Work released by Licensor.
81
+
82
+ You must conspicuously display this License on each original or modified copy of
83
+ the Licensed Work. If you receive the Licensed Work in original or modified form
84
+ from a third party, the terms and conditions set forth in this License apply to
85
+ your use of that work.
86
+
87
+ Any use of the Licensed Work in violation of this License will automatically
88
+ terminate your rights under this License for the current and all other versions
89
+ of the Licensed Work.
90
+
91
+ This License does not grant you any right in any trademark or logo of Licensor
92
+ or its affiliates (provided that you may use a trademark or logo of Licensor as
93
+ expressly required by this License).
94
+
95
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
96
+ “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
97
+ OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY,
98
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
99
+
100
+ MariaDB hereby grants you permission to use this License’s text to license your
101
+ works, and to refer to it using the trademark “Business Source License”, as long
102
+ as you comply with the Covenants of Licensor below.
103
+
104
+ Covenants of Licensor
105
+
106
+ In consideration of the right to use this License’s text and the “Business
107
+ Source License” name and trademark, Licensor covenants to MariaDB, and to all
108
+ other recipients of the licensed work to be provided by Licensor:
109
+
110
+ 1. To specify as the Change License the GPL Version 2.0 or any later version, or
111
+ a license that is compatible with GPL Version 2.0 or a later version, where
112
+ “compatible” means that software provided under the Change License can be
113
+ included in a program with software provided under GPL Version 2.0 or a later
114
+ version. Licensor may specify additional Change Licenses without limitation.
115
+
116
+ 2. To either: (a) specify an additional grant of rights to use that does not
117
+ impose any additional restriction on the right granted in this License, as
118
+ the Additional Use Grant; or (b) insert the text “None”.
119
+
120
+ 3. To specify a Change Date.
121
+
122
+ 4. Not to modify this License in any other way.
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: vps-deployer
3
+ Version: 1.2.0
4
+ Summary: Manage isolated systemd deployments on existing VPS hosts over SSH
5
+ License-Expression: BUSL-1.1
6
+ Project-URL: Repository, https://github.com/anfit/vps-deployer
7
+ Classifier: Development Status :: 5 - Production/Stable
8
+ Classifier: Environment :: Console
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: System :: Systems Administration
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: PyYAML>=6.0
18
+ Dynamic: license-file
19
+
20
+ # vps-deployer
21
+
22
+ Deploy ordinary Linux services to a host with the operational properties of a
23
+ small PaaS, without Docker or a resident agent.
24
+
25
+ `vps-deployer` packages a release locally, transfers it over OpenSSH, installs it
26
+ immutably, manages a hardened systemd service or timer, checks health, optionally
27
+ reconciles an nginx route, and rolls back failed activation. It targets operators
28
+ running roughly one to a dozen independent services on a normal Linux host.
29
+
30
+ It also supports command deployments: immutable releases exposed through a
31
+ validated `/usr/local/bin` launcher without creating a daemon.
32
+
33
+ It is deliberately:
34
+
35
+ - not configuration management;
36
+ - not a container runtime;
37
+ - not a build system;
38
+ - not a multi-host rollout orchestrator.
39
+
40
+ Applications arrive as prepared artifacts with an executable `.deployer/run.sh`.
41
+ Host packages, firewalls, TLS issuance, databases, DNS, kernel settings, and
42
+ unrelated users remain owned by host administration. The core product boundary
43
+ is a single-host deployment transaction.
44
+
45
+ ![Animated vps-deployer walkthrough: plan, apply, and verify](docs/assets/vps-deployer-demo.gif)
46
+
47
+ ```mermaid
48
+ flowchart LR
49
+ subgraph workstation["Operator workstation"]
50
+ app["Application repository<br/>code + .deployer contract"]
51
+ infra["Infrastructure repository<br/>hosts + deployment manifests"]
52
+ secrets["Operator environment<br/>secret values"]
53
+ deployer["vps-deployer"]
54
+ app --> deployer
55
+ infra --> deployer
56
+ secrets -->|"resolved locally; never printed"| deployer
57
+ end
58
+
59
+ deployer -->|"OpenSSH deployment transaction"| host["Existing Linux host"]
60
+ host --> releases["Immutable releases<br/>current + rollback target"]
61
+ host --> systemd["Hardened systemd<br/>service or timer"]
62
+ host --> nginx["Optional nginx route"]
63
+ host --> state["Persistent application data"]
64
+ ```
65
+
66
+ ## When it fits
67
+
68
+ Use it for self-contained HTTP services, workers, bots, webhooks, and scheduled
69
+ jobs that naturally run under systemd. Go/Rust binaries, packaged Java runtimes,
70
+ prepared Node/Python artifacts, and small native daemons fit especially well.
71
+
72
+ Prefer another tool for container-image-first software, multi-service products,
73
+ host provisioning, or coordinated fleet rollouts. Compared with Docker Compose,
74
+ the application runtime stays Linux-native while the deployment lifecycle is
75
+ managed. Compared with Ansible, vps-deployer owns application releases rather
76
+ than general machine state.
77
+
78
+ ## Installation
79
+
80
+ Python 3.10 or newer and an OpenSSH client are required on the operator machine.
81
+ The simplest install is an isolated tool environment:
82
+
83
+ ```console
84
+ pipx install git+https://github.com/anfit/vps-deployer
85
+ vps-deployer --version
86
+ ```
87
+
88
+ To work from a checkout instead (for development):
89
+
90
+ ```console
91
+ git clone https://github.com/anfit/vps-deployer.git
92
+ cd vps-deployer
93
+ python -m venv .venv
94
+ .venv/Scripts/python -m pip install . # Linux/macOS: .venv/bin/python
95
+ ```
96
+
97
+ The target host requirements are checked by `host inspect` and deployment
98
+ expectations; see the operations guide before onboarding a host.
99
+
100
+ ## Quick start
101
+
102
+ The bundled example validates offline, with no host required. It needs one
103
+ variable pointing at your checkout:
104
+
105
+ ```console
106
+ export VPS_DEPLOYER_DIR=$PWD # PowerShell: $env:VPS_DEPLOYER_DIR = $PWD.Path
107
+ vps-deployer --repo ./examples validate
108
+ vps-deployer --repo ./examples secrets check # reports unset secret variables by name
109
+ ```
110
+
111
+ `plan`, `apply`, and `status` connect to the host in `examples/hosts/prod.yaml`
112
+ (an SSH alias you must replace). To start your own infrastructure repository, run `vps-deployer init DIR`, or use
113
+ [service-infra-template](https://github.com/anfit/service-infra-template) for a
114
+ fuller starting point with an operator guide. To prepare an application:
115
+
116
+ ```console
117
+ vps-deployer init app ./my-service # writes .deployer/run.sh, ignore.txt, expect.yaml, .gitattributes
118
+ vps-deployer doctor ./my-service # CRLF/shebang, credential-like files, contract syntax
119
+ ```
120
+
121
+ Detailed documentation:
122
+
123
+ - [`docs/build-properties.md`](docs/build-properties.md) — application-visible build and deployment provenance contract.
124
+ - [`docs/expectations.md`](docs/expectations.md) — read-only application and infrastructure host contracts.
125
+ - [`docs/architecture.md`](docs/architecture.md) — ownership model and remote layout.
126
+ - [`docs/manifest-reference.md`](docs/manifest-reference.md) — complete host and deployment schema.
127
+ - [`docs/operations.md`](docs/operations.md) — onboarding, deployment, status, rollback, removal, and troubleshooting.
128
+
129
+ ## Codex skills
130
+
131
+ Codex discovers two repository-scoped operational skills under `.agents/skills`:
132
+
133
+ - `operate-vps-deployments` plans, applies, verifies, diagnoses, rolls back, and removes existing managed deployments.
134
+ - `onboard-vps-service` adapts a new or legacy service and adds its desired state to `service-infra`.
135
+
136
+ Invoke them explicitly as `$operate-vps-deployments` or `$onboard-vps-service`, or describe a matching task while working anywhere in this repository.
137
+
138
+ ## Repository layout
139
+
140
+ Keep host identity separate from application deployment identity. Deployment
141
+ files may be grouped recursively by application:
142
+
143
+ ```text
144
+ infra/
145
+ hosts/
146
+ prod.yaml
147
+ deployments/
148
+ invoicer/
149
+ prod.yaml
150
+ globals/ # optional; only genuinely shared non-secret values
151
+ artifacts/ # optional local release inputs
152
+ ```
153
+
154
+ See [`examples/hosts/prod.yaml`](examples/hosts/prod.yaml) and
155
+ [`examples/deployments/invoicer/prod.yaml`](examples/deployments/invoicer/prod.yaml).
156
+ Ordinary configuration should be committed directly in a deployment. Use
157
+ `from_global` only for values intentionally shared by multiple deployments, and
158
+ use `secrets.from_env` only for secret material.
159
+
160
+ Multiple deployments may target the same host. An optional `http_proxy` block
161
+ manages a deployment's nginx site, TLS certificate references, loopback upstream,
162
+ configuration validation, and reload independently from other routes on that host.
163
+
164
+ The application repository owns `.deployer/` and application provenance. The
165
+ infrastructure repository owns deployment identity, runtime configuration,
166
+ routing, storage, and secret references. Secret values remain in the operator
167
+ environment; persistent application data remains on the host.
168
+
169
+ Local artifact and include paths support explicit `${NAME}` environment roots.
170
+ Unresolved variables and `..` traversal are rejected. For example:
171
+
172
+ ```yaml
173
+ release:
174
+ source: ${PROJECTS_DIR}/my-service
175
+ include:
176
+ - source: ${SERVICE_INFRA_DIR}/configs/my-service/prod.yaml
177
+ target: config/production.yaml
178
+ ```
179
+
180
+ Directory artifacts may contain a `.deployer/ignore.txt` file with gitignore-like
181
+ glob patterns. Matching developer-local files are excluded from both the release
182
+ hash and uploaded archive; use it to prevent local credentials and helper files
183
+ from entering managed releases. `.vps-deployer-ignore` remains a compatibility
184
+ fallback for applications that have not migrated.
185
+
186
+ Applications expose their managed runtime through `.deployer/run.sh`. This is the
187
+ default `runtime.command`, so ordinary infrastructure manifests need not repeat
188
+ an application-owned path. Named entrypoints such as `.deployer/bootstrap.sh` may
189
+ be selected explicitly for a distinct service lifecycle.
190
+
191
+ Every release contains an application-facing `build.properties` file. Existing
192
+ application/build properties are preserved and vps-deployer appends reserved
193
+ `deployment.*` provenance without modifying the checkout. `status` checks the
194
+ active deployment provenance against the desired release and commit; stale or
195
+ missing metadata makes the deployment unhealthy. After a successful activation,
196
+ the active release and its immediate predecessor are retained for rollback; older
197
+ release directories are pruned. Failed deployments do not trigger cleanup.
198
+
199
+ ## Commands
200
+
201
+ ```console
202
+ vps-deployer --version
203
+ vps-deployer init [DIR] # scaffold an infra repository
204
+ vps-deployer init app [DIR] # scaffold an application's .deployer/ contract
205
+ vps-deployer doctor [APP_DIR] # offline preflight of an application
206
+ vps-deployer validate
207
+ vps-deployer list [--format markdown] # inventory from the manifests (offline)
208
+ vps-deployer secrets check [DEPLOYMENT]
209
+ vps-deployer check DEPLOYMENT
210
+ vps-deployer host inspect HOST
211
+ vps-deployer host onboard HOST
212
+ vps-deployer plan DEPLOYMENT|--all [--exit-code]
213
+ vps-deployer apply DEPLOYMENT
214
+ vps-deployer status DEPLOYMENT|--all
215
+ vps-deployer logs DEPLOYMENT
216
+ vps-deployer rollback DEPLOYMENT
217
+ vps-deployer remove DEPLOYMENT
218
+ ```
219
+
220
+ The infrastructure repository is chosen in this order: `--repo`, the current
221
+ directory when it contains `hosts/` and `deployments/`, `$VPS_DEPLOYER_REPO`,
222
+ then `$SERVICE_INFRA_DIR`. Examples:
223
+
224
+ ```console
225
+ vps-deployer --repo ./examples validate
226
+ vps-deployer --repo ./examples plan invoicer-prod
227
+ ```
228
+
229
+ `remove` stops and disables a deployment and removes its systemd unit and
230
+ environment file. It deliberately retains releases, writable storage, and service
231
+ users. Privileged deployments require `--allow-privileged` for both apply and
232
+ removal.
233
+
234
+ Hosts may set `ssh.privileged_host`, `ssh.privileged_user`, and
235
+ `ssh.privileged_identity_file` to route privileged operations through a separate
236
+ SSH identity. Use `tools/vps-deployer-ssh-gate` as that key's server-side forced
237
+ command so it cannot open an interactive root shell. The forced command must
238
+ declare the host's managed root and each storage path assigned to this key:
239
+
240
+ ```text
241
+ command="/usr/local/sbin/vps-deployer-ssh-gate --managed-root /srv/vps-deployer --storage /var/lib/example" ssh-ed25519 AAAA...
242
+ ```
243
+
244
+ The client sends named capability requests; the gate constructs fixed executable
245
+ arguments and rejects storage outside this policy. Update the forced-command
246
+ installation before using a client containing this protocol change.
247
+
248
+ Systemd units use a separate validated gate capability rather than opaque file
249
+ writes. The gate accepts only the hardened non-root unit structure rooted in the
250
+ deployment's active release and allowlisted storage. A restricted-key host cannot
251
+ apply `service.privileged: true`; root services require a separately trusted sudo
252
+ or host administration path.
253
+
254
+ Secrets are accepted only through explicitly declared process-environment
255
+ references and are never printed by planning or validation. `secrets check`
256
+ lists unset variables by name (exit status 1 when any are missing); `apply`
257
+ refuses to start with unset secrets, and `plan` warns that they are redacted.
258
+
259
+ ## Versioning and compatibility
260
+
261
+ Version 1.0 establishes the stable public contracts for deployment manifests,
262
+ the `.deployer/` application interface, generated `build.properties`, CLI
263
+ commands, and the restricted SSH gate protocol. Compatible additions are made
264
+ within the 1.x series; incompatible contract changes require a new major
265
+ version and explicit migration documentation.
266
+
267
+ The client and server-side SSH gate are one security-sensitive protocol pair.
268
+ When release notes identify a gate protocol change, install the matching gate
269
+ before using the new client against a restricted-key host.
270
+
271
+ ## License
272
+
273
+ The complete `vps-deployer` codebase, including versions from project inception,
274
+ is licensed by Jan Chimiak under the [Business Source License 1.1](LICENSE).
275
+ Production use is permitted except for paid hosted or embedded competitive
276
+ offerings. Each version converts to MPL 2.0 four years after its first public
277
+ distribution.
@@ -0,0 +1,258 @@
1
+ # vps-deployer
2
+
3
+ Deploy ordinary Linux services to a host with the operational properties of a
4
+ small PaaS, without Docker or a resident agent.
5
+
6
+ `vps-deployer` packages a release locally, transfers it over OpenSSH, installs it
7
+ immutably, manages a hardened systemd service or timer, checks health, optionally
8
+ reconciles an nginx route, and rolls back failed activation. It targets operators
9
+ running roughly one to a dozen independent services on a normal Linux host.
10
+
11
+ It also supports command deployments: immutable releases exposed through a
12
+ validated `/usr/local/bin` launcher without creating a daemon.
13
+
14
+ It is deliberately:
15
+
16
+ - not configuration management;
17
+ - not a container runtime;
18
+ - not a build system;
19
+ - not a multi-host rollout orchestrator.
20
+
21
+ Applications arrive as prepared artifacts with an executable `.deployer/run.sh`.
22
+ Host packages, firewalls, TLS issuance, databases, DNS, kernel settings, and
23
+ unrelated users remain owned by host administration. The core product boundary
24
+ is a single-host deployment transaction.
25
+
26
+ ![Animated vps-deployer walkthrough: plan, apply, and verify](docs/assets/vps-deployer-demo.gif)
27
+
28
+ ```mermaid
29
+ flowchart LR
30
+ subgraph workstation["Operator workstation"]
31
+ app["Application repository<br/>code + .deployer contract"]
32
+ infra["Infrastructure repository<br/>hosts + deployment manifests"]
33
+ secrets["Operator environment<br/>secret values"]
34
+ deployer["vps-deployer"]
35
+ app --> deployer
36
+ infra --> deployer
37
+ secrets -->|"resolved locally; never printed"| deployer
38
+ end
39
+
40
+ deployer -->|"OpenSSH deployment transaction"| host["Existing Linux host"]
41
+ host --> releases["Immutable releases<br/>current + rollback target"]
42
+ host --> systemd["Hardened systemd<br/>service or timer"]
43
+ host --> nginx["Optional nginx route"]
44
+ host --> state["Persistent application data"]
45
+ ```
46
+
47
+ ## When it fits
48
+
49
+ Use it for self-contained HTTP services, workers, bots, webhooks, and scheduled
50
+ jobs that naturally run under systemd. Go/Rust binaries, packaged Java runtimes,
51
+ prepared Node/Python artifacts, and small native daemons fit especially well.
52
+
53
+ Prefer another tool for container-image-first software, multi-service products,
54
+ host provisioning, or coordinated fleet rollouts. Compared with Docker Compose,
55
+ the application runtime stays Linux-native while the deployment lifecycle is
56
+ managed. Compared with Ansible, vps-deployer owns application releases rather
57
+ than general machine state.
58
+
59
+ ## Installation
60
+
61
+ Python 3.10 or newer and an OpenSSH client are required on the operator machine.
62
+ The simplest install is an isolated tool environment:
63
+
64
+ ```console
65
+ pipx install git+https://github.com/anfit/vps-deployer
66
+ vps-deployer --version
67
+ ```
68
+
69
+ To work from a checkout instead (for development):
70
+
71
+ ```console
72
+ git clone https://github.com/anfit/vps-deployer.git
73
+ cd vps-deployer
74
+ python -m venv .venv
75
+ .venv/Scripts/python -m pip install . # Linux/macOS: .venv/bin/python
76
+ ```
77
+
78
+ The target host requirements are checked by `host inspect` and deployment
79
+ expectations; see the operations guide before onboarding a host.
80
+
81
+ ## Quick start
82
+
83
+ The bundled example validates offline, with no host required. It needs one
84
+ variable pointing at your checkout:
85
+
86
+ ```console
87
+ export VPS_DEPLOYER_DIR=$PWD # PowerShell: $env:VPS_DEPLOYER_DIR = $PWD.Path
88
+ vps-deployer --repo ./examples validate
89
+ vps-deployer --repo ./examples secrets check # reports unset secret variables by name
90
+ ```
91
+
92
+ `plan`, `apply`, and `status` connect to the host in `examples/hosts/prod.yaml`
93
+ (an SSH alias you must replace). To start your own infrastructure repository, run `vps-deployer init DIR`, or use
94
+ [service-infra-template](https://github.com/anfit/service-infra-template) for a
95
+ fuller starting point with an operator guide. To prepare an application:
96
+
97
+ ```console
98
+ vps-deployer init app ./my-service # writes .deployer/run.sh, ignore.txt, expect.yaml, .gitattributes
99
+ vps-deployer doctor ./my-service # CRLF/shebang, credential-like files, contract syntax
100
+ ```
101
+
102
+ Detailed documentation:
103
+
104
+ - [`docs/build-properties.md`](docs/build-properties.md) — application-visible build and deployment provenance contract.
105
+ - [`docs/expectations.md`](docs/expectations.md) — read-only application and infrastructure host contracts.
106
+ - [`docs/architecture.md`](docs/architecture.md) — ownership model and remote layout.
107
+ - [`docs/manifest-reference.md`](docs/manifest-reference.md) — complete host and deployment schema.
108
+ - [`docs/operations.md`](docs/operations.md) — onboarding, deployment, status, rollback, removal, and troubleshooting.
109
+
110
+ ## Codex skills
111
+
112
+ Codex discovers two repository-scoped operational skills under `.agents/skills`:
113
+
114
+ - `operate-vps-deployments` plans, applies, verifies, diagnoses, rolls back, and removes existing managed deployments.
115
+ - `onboard-vps-service` adapts a new or legacy service and adds its desired state to `service-infra`.
116
+
117
+ Invoke them explicitly as `$operate-vps-deployments` or `$onboard-vps-service`, or describe a matching task while working anywhere in this repository.
118
+
119
+ ## Repository layout
120
+
121
+ Keep host identity separate from application deployment identity. Deployment
122
+ files may be grouped recursively by application:
123
+
124
+ ```text
125
+ infra/
126
+ hosts/
127
+ prod.yaml
128
+ deployments/
129
+ invoicer/
130
+ prod.yaml
131
+ globals/ # optional; only genuinely shared non-secret values
132
+ artifacts/ # optional local release inputs
133
+ ```
134
+
135
+ See [`examples/hosts/prod.yaml`](examples/hosts/prod.yaml) and
136
+ [`examples/deployments/invoicer/prod.yaml`](examples/deployments/invoicer/prod.yaml).
137
+ Ordinary configuration should be committed directly in a deployment. Use
138
+ `from_global` only for values intentionally shared by multiple deployments, and
139
+ use `secrets.from_env` only for secret material.
140
+
141
+ Multiple deployments may target the same host. An optional `http_proxy` block
142
+ manages a deployment's nginx site, TLS certificate references, loopback upstream,
143
+ configuration validation, and reload independently from other routes on that host.
144
+
145
+ The application repository owns `.deployer/` and application provenance. The
146
+ infrastructure repository owns deployment identity, runtime configuration,
147
+ routing, storage, and secret references. Secret values remain in the operator
148
+ environment; persistent application data remains on the host.
149
+
150
+ Local artifact and include paths support explicit `${NAME}` environment roots.
151
+ Unresolved variables and `..` traversal are rejected. For example:
152
+
153
+ ```yaml
154
+ release:
155
+ source: ${PROJECTS_DIR}/my-service
156
+ include:
157
+ - source: ${SERVICE_INFRA_DIR}/configs/my-service/prod.yaml
158
+ target: config/production.yaml
159
+ ```
160
+
161
+ Directory artifacts may contain a `.deployer/ignore.txt` file with gitignore-like
162
+ glob patterns. Matching developer-local files are excluded from both the release
163
+ hash and uploaded archive; use it to prevent local credentials and helper files
164
+ from entering managed releases. `.vps-deployer-ignore` remains a compatibility
165
+ fallback for applications that have not migrated.
166
+
167
+ Applications expose their managed runtime through `.deployer/run.sh`. This is the
168
+ default `runtime.command`, so ordinary infrastructure manifests need not repeat
169
+ an application-owned path. Named entrypoints such as `.deployer/bootstrap.sh` may
170
+ be selected explicitly for a distinct service lifecycle.
171
+
172
+ Every release contains an application-facing `build.properties` file. Existing
173
+ application/build properties are preserved and vps-deployer appends reserved
174
+ `deployment.*` provenance without modifying the checkout. `status` checks the
175
+ active deployment provenance against the desired release and commit; stale or
176
+ missing metadata makes the deployment unhealthy. After a successful activation,
177
+ the active release and its immediate predecessor are retained for rollback; older
178
+ release directories are pruned. Failed deployments do not trigger cleanup.
179
+
180
+ ## Commands
181
+
182
+ ```console
183
+ vps-deployer --version
184
+ vps-deployer init [DIR] # scaffold an infra repository
185
+ vps-deployer init app [DIR] # scaffold an application's .deployer/ contract
186
+ vps-deployer doctor [APP_DIR] # offline preflight of an application
187
+ vps-deployer validate
188
+ vps-deployer list [--format markdown] # inventory from the manifests (offline)
189
+ vps-deployer secrets check [DEPLOYMENT]
190
+ vps-deployer check DEPLOYMENT
191
+ vps-deployer host inspect HOST
192
+ vps-deployer host onboard HOST
193
+ vps-deployer plan DEPLOYMENT|--all [--exit-code]
194
+ vps-deployer apply DEPLOYMENT
195
+ vps-deployer status DEPLOYMENT|--all
196
+ vps-deployer logs DEPLOYMENT
197
+ vps-deployer rollback DEPLOYMENT
198
+ vps-deployer remove DEPLOYMENT
199
+ ```
200
+
201
+ The infrastructure repository is chosen in this order: `--repo`, the current
202
+ directory when it contains `hosts/` and `deployments/`, `$VPS_DEPLOYER_REPO`,
203
+ then `$SERVICE_INFRA_DIR`. Examples:
204
+
205
+ ```console
206
+ vps-deployer --repo ./examples validate
207
+ vps-deployer --repo ./examples plan invoicer-prod
208
+ ```
209
+
210
+ `remove` stops and disables a deployment and removes its systemd unit and
211
+ environment file. It deliberately retains releases, writable storage, and service
212
+ users. Privileged deployments require `--allow-privileged` for both apply and
213
+ removal.
214
+
215
+ Hosts may set `ssh.privileged_host`, `ssh.privileged_user`, and
216
+ `ssh.privileged_identity_file` to route privileged operations through a separate
217
+ SSH identity. Use `tools/vps-deployer-ssh-gate` as that key's server-side forced
218
+ command so it cannot open an interactive root shell. The forced command must
219
+ declare the host's managed root and each storage path assigned to this key:
220
+
221
+ ```text
222
+ command="/usr/local/sbin/vps-deployer-ssh-gate --managed-root /srv/vps-deployer --storage /var/lib/example" ssh-ed25519 AAAA...
223
+ ```
224
+
225
+ The client sends named capability requests; the gate constructs fixed executable
226
+ arguments and rejects storage outside this policy. Update the forced-command
227
+ installation before using a client containing this protocol change.
228
+
229
+ Systemd units use a separate validated gate capability rather than opaque file
230
+ writes. The gate accepts only the hardened non-root unit structure rooted in the
231
+ deployment's active release and allowlisted storage. A restricted-key host cannot
232
+ apply `service.privileged: true`; root services require a separately trusted sudo
233
+ or host administration path.
234
+
235
+ Secrets are accepted only through explicitly declared process-environment
236
+ references and are never printed by planning or validation. `secrets check`
237
+ lists unset variables by name (exit status 1 when any are missing); `apply`
238
+ refuses to start with unset secrets, and `plan` warns that they are redacted.
239
+
240
+ ## Versioning and compatibility
241
+
242
+ Version 1.0 establishes the stable public contracts for deployment manifests,
243
+ the `.deployer/` application interface, generated `build.properties`, CLI
244
+ commands, and the restricted SSH gate protocol. Compatible additions are made
245
+ within the 1.x series; incompatible contract changes require a new major
246
+ version and explicit migration documentation.
247
+
248
+ The client and server-side SSH gate are one security-sensitive protocol pair.
249
+ When release notes identify a gate protocol change, install the matching gate
250
+ before using the new client against a restricted-key host.
251
+
252
+ ## License
253
+
254
+ The complete `vps-deployer` codebase, including versions from project inception,
255
+ is licensed by Jan Chimiak under the [Business Source License 1.1](LICENSE).
256
+ Production use is permitted except for paid hosted or embedded competitive
257
+ offerings. Each version converts to MPL 2.0 four years after its first public
258
+ distribution.