landfall 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -9,7 +9,7 @@ cd my-site && lf deploy -s het # live at https://my-site.203-0-113-7.sslip.
9
9
  ```
10
10
 
11
11
  - **Servers are ssh targets.** Any `~/.ssh/config` alias works. If `ssh het` works, `lf deploy -s het` works.
12
- - **No agent on the server.** Everything is plain Docker Compose under `/opt/srv`, so you can read and fix it by hand.
12
+ - **No agent on the server.** Everything is plain Docker Compose under `/opt/landfall`, so you can read and fix it by hand.
13
13
  - **Every change is journaled** on the server and can be undone with `lf undo`.
14
14
  - **Three ways in:** plain commands for scripts, CI and agents; a web dashboard (`lf`); a terminal dashboard (`lf tui`).
15
15
 
@@ -50,16 +50,16 @@ lf <command> --help # every option of one command
50
50
  | term | meaning |
51
51
  |---|---|
52
52
  | **server** | An ssh target: a `~/.ssh/config` alias, or one landfall registered with `lf server add`/`lf setup`. Pass it as `-s <name>`. The first server you add becomes the default. |
53
- | **site** | One deployed thing on a server: a static folder, a Dockerfile app or a prebuilt image. It runs as container `srv-<name>` and Traefik routes its domains to it. |
54
- | **project file** | `./srv.json`, written by the first `lf deploy`: `{ "name", "server", "domains" }`. Commands that take `[name]` default to this site, and `-s` defaults to its server. |
53
+ | **site** | One deployed thing on a server: a static folder, a Dockerfile app or a prebuilt image. It runs as container `landfall-<name>` and Traefik routes its domains to it. |
54
+ | **project file** | `./.landfall.json`, written by the first `lf deploy`: `{ "name", "server", "domains" }`. Commands that take `[name]` default to this site, and `-s` defaults to its server. |
55
55
  | **main domain** | Optional per-server domain (`lf server domain het example.com`). Sites then default to `<site>.example.com`, and a bare name like `shop` means `shop.example.com`. Without one, sites get `<site>.<ip-with-dashes>.sslip.io` (e.g. `blog.203-0-113-7.sslip.io`), which resolves without any DNS setup. |
56
56
  | **release** | Every deploy creates one, with an id like `20260122143005`. The last 5 are kept. `lf rollback` switches between them. |
57
57
  | **run** | One landfall invocation that changed something on a server. Runs are listed by `lf history` and reverted by `lf undo`. |
58
58
 
59
59
  How defaults resolve, for every command:
60
60
 
61
- - **site** `[name]`: the argument, else `name` from `./srv.json`.
62
- - **server** `-s`: the flag, else `server` from `./srv.json`, else the default server (`lf server use`).
61
+ - **site** `[name]`: the argument, else `name` from `./.landfall.json`.
62
+ - **server** `-s`: the flag, else `server` from `./.landfall.json`, else the default server (`lf server use`).
63
63
 
64
64
  ## Using landfall from scripts and agents
65
65
 
@@ -67,7 +67,7 @@ Every dashboard action is a plain command, and both dashboards print the command
67
67
 
68
68
  - **No prompts:** `-y` accepts confirmations and takes defaults. Without a terminal, prompts are never shown. A
69
69
  command that would ask then **fails** and tells you to pass `-y`. It never silently goes ahead.
70
- - **Passwords:** pass them with `--password-stdin` or `$SRV_PASSWORD` (`setup`, `server add`). For `lf auth`, use `-p`.
70
+ - **Passwords:** pass them with `--password-stdin` or `$LANDFALL_PASSWORD` (`setup`, `server add`). For `lf auth`, use `-p`.
71
71
  - **JSON output:** `--json` works on `ls`, `info`, `server config`, `server domain`, `domains`, `firewall`,
72
72
  `updates` and `history`.
73
73
  - **Dry runs:** `lf setup <name> --check` changes nothing. `lf updates` only reports.
@@ -105,7 +105,7 @@ lf undo -s box -y # revert the last run
105
105
  | `lf server use <name>` | Sets the default server. |
106
106
  | `lf server rm <name>` | Forgets a server locally. The server is untouched. |
107
107
  | `lf ssh [name] [-- cmd]` | Opens a shell or runs a one-off command. |
108
- | `lf keygen <name>` | Creates `~/.ssh/srv_<name>` and prints the public key. |
108
+ | `lf keygen <name>` | Creates `~/.ssh/landfall_<name>` and prints the public key. |
109
109
 
110
110
  **Sites**
111
111
 
@@ -154,7 +154,7 @@ lf undo -s box -y # revert the last run
154
154
  lf setup het --host 203.0.113.7
155
155
  ```
156
156
 
157
- 1. Creates `~/.ssh/srv_het` and installs it with `ssh-copy-id`. You type the root password once.
157
+ 1. Creates `~/.ssh/landfall_het` and installs it with `ssh-copy-id`. You type the root password once.
158
158
  2. Installs Docker, ufw (allowing ssh, 80 and 443), fail2ban and unattended-upgrades.
159
159
  3. Creates the user `deploy` in the docker group and disables ssh password login. Root keeps key-only access.
160
160
  4. Writes `Host het` to `~/.ssh/config`, creating the file if needed. `ssh het` now logs in as `deploy`.
@@ -189,7 +189,7 @@ with the newest release. An existing Docker is never reinstalled. In a terminal,
189
189
  |---|---|
190
190
  | `-u admin` / `-p 2222` | log in as `admin` (needs passwordless sudo) / ssh port |
191
191
  | `-i ~/.ssh/id_ed25519` | use an existing key instead of generating one |
192
- | `--password-stdin` | read the login password from stdin (or set `$SRV_PASSWORD`) |
192
+ | `--password-stdin` | read the login password from stdin (or set `$LANDFALL_PASSWORD`) |
193
193
  | `--deploy-user ""` | deploy as the login user instead of creating `deploy` |
194
194
  | `--domain example.com` | primary main domain |
195
195
  | `--staging` / `--no-staging` | use Let's Encrypt's staging CA while testing (remembered) |
@@ -219,10 +219,10 @@ lf deploy -d example.com -d www.example.com -s het # static, name = folder
219
219
  lf deploy -b "npm run build" # build locally first
220
220
  lf deploy -d app.example.com -e DATABASE_URL=postgres://… --env-file .env.production # Dockerfile app
221
221
  lf deploy -n api --image ghcr.io/me/api:1.4 --port 8080 -d api.example.com # image
222
- lf deploy # later: redeploy using ./srv.json
222
+ lf deploy # later: redeploy using ./.landfall.json
223
223
  ```
224
224
 
225
- The first deploy writes `srv.json`. Commit it:
225
+ The first deploy writes `.landfall.json`. Commit it:
226
226
 
227
227
  ```json
228
228
  { "name": "my-site", "server": "het", "domains": ["example.com", "www.example.com"] }
@@ -238,6 +238,8 @@ lf server domain het example.com # checks DNS for example.com and *.e
238
238
  lf deploy # site "blog" → blog.example.com
239
239
  lf deploy -d shop # no dot → shop.example.com
240
240
  lf domains blog --add www # www.example.com
241
+ lf deploy -d @ # @ → example.com itself
242
+ lf domains blog --add @ # blog also serves example.com
241
243
  lf server domain het --add other.org # more main domains; the first stays primary
242
244
  lf server domain het --primary other.org # switch the primary
243
245
  lf server domain het --rm other.org # drop one; --clear drops all (back to sslip.io)
@@ -246,6 +248,10 @@ lf server domain het --rm other.org # drop one; --clear drops all (back
246
248
  `server domain` prints the A records to add: one for the domain and a wildcard `*.example.com`. With those in
247
249
  place, every new site resolves without touching DNS again. For non-primary main domains, write the full name (`-d shop.other.org`).
248
250
 
251
+ A domain belongs to one site. Giving it to another one (`lf deploy -d @`, `lf domains shop --add example.com`)
252
+ asks, then moves it: the old site keeps its other domains. A move that would leave a site with none is refused,
253
+ and so is a redeploy whose domain comes only from `.landfall.json` (pass `-d` to move it on purpose).
254
+
249
255
  ## Day-to-day
250
256
 
251
257
  ```sh
@@ -256,7 +262,7 @@ lf rollback # previous release (--to <id> for a specific
256
262
  lf env API_KEY=abc # set env (container is recreated); -u KEY removes one
257
263
  lf restart | stop | start
258
264
  lf domains # where DNS points, whether HTTPS answers
259
- lf domains --add www.example.com # also --rm, --set (updates srv.json)
265
+ lf domains --add www.example.com # also --rm, --set (updates .landfall.json)
260
266
  lf auth -u anna # login in front of the site (asks for the password, -p passes it)
261
267
  lf auth --rm-user anna | --off
262
268
  lf open
@@ -294,7 +300,7 @@ lf traefik setup --dashboard traefik # dashboard at traefik.<main doma
294
300
  lf traefik setup --host-network # bind 80/443 on the host
295
301
  ```
296
302
 
297
- landfall's Traefik is the container `traefik` from `/opt/srv/traefik/docker-compose.yml`. By default Docker
303
+ landfall's Traefik is the container `traefik` from `/opt/landfall/traefik/docker-compose.yml`. By default Docker
298
304
  publishes 80/443 to it. Sites work the same in both modes:
299
305
 
300
306
  | | ports published (default) | `--host-network` |
@@ -302,6 +308,12 @@ publishes 80/443 to it. Sites work the same in both modes:
302
308
  | client IP in logs / `X-Forwarded-For` | real IPv4; IPv6 may show Docker's gateway | always real |
303
309
  | ufw | Docker's NAT bypasses ufw for 80/443 | ufw applies |
304
310
 
311
+ Traefik only answers hosts it has a certificate for (`sniStrict`). A domain pointed at the server but served
312
+ by no site fails the TLS handshake, as if nothing listened there, instead of showing a 404 or Traefik's own
313
+ certificate; plain HTTP to it redirects to HTTPS and fails the same way. A new site's domain fails the same way
314
+ for the seconds until Let's Encrypt issues its certificate. Your own certificates and Traefik config go into
315
+ `/opt/landfall/traefik/dynamic/`: landfall rewrites only `landfall.yml` there.
316
+
305
317
  The dashboard password is generated and printed once (`--dashboard-password` sets your own). Image, network mode,
306
318
  e-mail, staging and dashboard are remembered per server.
307
319
 
@@ -314,7 +326,7 @@ lf traefik use traefik-reverseproxy-1 -s het # or name it
314
326
 
315
327
  landfall reads the network, HTTPS entrypoint and cert resolver from the container's flags. If your Traefik is
316
328
  configured by a file, pass `--network proxy --entrypoint websecure --resolver letsencrypt`. The choice is stored
317
- in `/opt/srv/proxy.env`. landfall never changes that Traefik.
329
+ in `/opt/landfall/proxy.env`. landfall never changes that Traefik.
318
330
 
319
331
  ## Firewall
320
332
 
@@ -335,7 +347,7 @@ lf firewall enable | disable # enable installs ufw if needed and all
335
347
 
336
348
  ## History and undo
337
349
 
338
- Every change landfall makes is appended to `/opt/srv/journal.log` on the server: when, which local user and
350
+ Every change landfall makes is appended to `/opt/landfall/journal.log` on the server: when, which local user and
339
351
  command, what changed and the shell that reverts it.
340
352
 
341
353
  ```sh
@@ -351,15 +363,15 @@ lf undo --all # everything, newest first
351
363
  compose updates.
352
364
  - **Not undoable:** `sites rm`, restarts, package upgrades, and a firewall rule that replaced an existing one.
353
365
  - Steps revert newest first, each as the user that made it. The first failure stops the rest.
354
- - Undo changes the server only. Local `~/.config/srv` and `srv.json` stay as they are.
355
- - Overwritten files are backed up as `.<name>.srv-<run>-<n>`, and the newest 10 are kept per file.
366
+ - Undo changes the server only. Local `~/.config/landfall` and `.landfall.json` stay as they are.
367
+ - Overwritten files are backed up as `.<name>.landfall-<run>-<n>`, and the newest 10 are kept per file.
356
368
 
357
369
  ## Managing servers
358
370
 
359
371
  ```sh
360
372
  lf server add het # register an existing ssh alias
361
373
  lf server add box --host 1.2.3.4 -u ubuntu -i ~/.ssh/box --default
362
- lf server add web --host 1.2.3.4 -u admin # asks the password once, installs ~/.ssh/srv_web
374
+ lf server add web --host 1.2.3.4 -u admin # asks the password once, installs ~/.ssh/landfall_web
363
375
  lf server config het # landfall entry, Traefik settings, ssh login, keys, Host block
364
376
  lf server list --all
365
377
  lf server use het
@@ -367,10 +379,10 @@ lf server rm het # forget locally
367
379
  lf ssh het -- docker ps
368
380
  ```
369
381
 
370
- `server add` installs nothing on the server. It writes a `Host <name>` block between `# >>> srv:<name>` markers
382
+ `server add` installs nothing on the server. It writes a `Host <name>` block between `# >>> landfall:<name>` markers
371
383
  (`--no-ssh-config` keeps the details in landfall's config instead). In a Host block you wrote yourself, landfall
372
384
  only steps in when the key does not work. It then puts the working `IdentityFile` first, drops lines whose file is
373
- gone, and keeps a backup as `~/.ssh/config.srv-bak`.
385
+ gone, and keeps a backup as `~/.ssh/config.landfall-bak`.
374
386
 
375
387
  ## Dashboards
376
388
 
@@ -378,7 +390,7 @@ gone, and keeps a backup as `~/.ssh/config.srv-bak`.
378
390
  (★ marks the default). A server tab has Sites, Updates, Firewall, History and Settings. **Deploy folder…** browses
379
391
  local folders or takes a dropped folder or `.zip`. Every action streams the output of its plain command into a side
380
392
  panel. Destructive actions ask first. Login works through the printed `/login?t=…` link. The token is kept in
381
- `~/.config/srv/ui-token`, and you can delete that file to rotate it. Requests with a foreign `Host` or `Origin`
393
+ `~/.config/landfall/ui-token`, and you can delete that file to rotate it. Requests with a foreign `Host` or `Origin`
382
394
  are refused. Running `lf` again while the dashboard is running just opens the page.
383
395
 
384
396
  **Terminal (`lf tui`).**
@@ -401,30 +413,36 @@ Keys: `↵` connect/open, `d` deploy this folder, `u` check and set up, `a` add
401
413
 
402
414
  | local file | content |
403
415
  |---|---|
404
- | `~/.config/srv/config.json` | registered servers, default server, per-server settings |
405
- | `~/.config/srv/projects.json` | folders deployed from this machine (dashboard *Recent*/*Redeploy*) |
406
- | `~/.config/srv/ui-token` | web dashboard login token |
407
- | `~/.config/srv/uploads/` | folders dropped into the dashboard, removed after a day |
408
- | `~/.ssh/srv_<name>` | generated keys |
409
- | `~/.ssh/config` | `Host <name>` blocks between `# >>> srv:<name>` markers |
410
- | `./srv.json` | per-project deploy settings |
416
+ | `~/.config/landfall/config.json` | registered servers, default server, per-server settings |
417
+ | `~/.config/landfall/projects.json` | folders deployed from this machine (dashboard *Recent*/*Redeploy*) |
418
+ | `~/.config/landfall/ui-token` | web dashboard login token |
419
+ | `~/.config/landfall/uploads/` | folders dropped into the dashboard, removed after a day |
420
+ | `~/.ssh/landfall_<name>` | generated keys |
421
+ | `~/.ssh/config` | `Host <name>` blocks between `# >>> landfall:<name>` markers |
422
+ | `./.landfall.json` | per-project deploy settings |
411
423
 
412
424
  | env var | effect |
413
425
  |---|---|
414
- | `SRV_CONFIG_DIR` | use another directory than `~/.config/srv` |
415
- | `SRV_SSH_CONFIG` | use another ssh config file (`ssh -F`) |
416
- | `SRV_PASSWORD` | login password for `setup`/`server add` |
426
+ | `LANDFALL_CONFIG_DIR` | use another directory than `~/.config/landfall` |
427
+ | `LANDFALL_SSH_CONFIG` | use another ssh config file (`ssh -F`) |
428
+ | `LANDFALL_PASSWORD` | login password for `setup`/`server add` |
429
+
430
+ **Coming from `srv`** (landfall's old name): `~/.config/srv` moves to `~/.config/landfall` on the first run, and
431
+ `srv.json`, `~/.ssh/srv_<name>`, `# >>> srv:` blocks and `$SRV_*` variables keep working. A server set up by `srv`
432
+ still has `/opt/srv`: other commands refuse it until `lf setup <name>` moves it to `/opt/landfall` and renames
433
+ the site containers. Sites keep serving, Traefik restarts once, and older changes can no longer be undone.
434
+ All of this lives in `src/legacy.ts`.
417
435
 
418
436
  On the server:
419
437
 
420
438
  ```
421
- /opt/srv/
439
+ /opt/landfall/
422
440
  traefik/docker-compose.yml Traefik, ports 80/443, Let's Encrypt (HTTP-01)
423
441
  traefik/letsencrypt/acme.json
424
442
  proxy.env only with `lf traefik use`
425
443
  sites/<name>/
426
444
  site.json metadata + release history
427
- docker-compose.yml container srv-<name> on network "web" with Traefik labels
445
+ docker-compose.yml container landfall-<name> on network "web" with Traefik labels
428
446
  nginx.conf static sites
429
447
  .env app sites (0600)
430
448
  releases/<id>/ last 5 kept
@@ -432,7 +450,7 @@ On the server:
432
450
  journal.log every change, with the shell that reverts it
433
451
  ```
434
452
 
435
- `cd /opt/srv/sites/blog && docker compose ps` works as usual.
453
+ `cd /opt/landfall/sites/blog && docker compose ps` works as usual.
436
454
 
437
455
  ## Troubleshooting
438
456
 
@@ -441,7 +459,7 @@ On the server:
441
459
  | `setup needs root or passwordless sudo` | use `-u root`, or give your user passwordless sudo |
442
460
  | certificate warning in the browser | you used `--staging`: `lf setup <name> --no-staging` |
443
461
  | no certificate at all | DNS must point at the server and 80/443 must be open; see `lf traefik logs` |
444
- | 404 from Traefik | the domain isn't in the site's list: `lf domains --add …` |
462
+ | TLS handshake fails | no site serves the domain (`lf domains --add …`), or its certificate isn't issued yet: `lf traefik logs` |
445
463
  | site container keeps restarting | `lf logs`; for apps check `--port` and env |
446
464
  | `skipping firewall/ssh hardening: …` | setup protected something running there. Fix it, or re-run with `--force` |
447
465
  | `skipping traefik: port 80 is in use by …` | free the port, then `lf traefik setup`. If it's a Traefik, `lf traefik use` |