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 +53 -35
- package/dist/cli.mjs +186 -143
- package/dist/index.html +30 -12
- package/dist/{job-CypEf1ud.mjs → job-i5GF9h93.mjs} +2 -2
- package/dist/server-CqBtraHg.mjs +4 -0
- package/dist/tui-BDvEukfL.mjs +8 -0
- package/package.json +1 -1
- package/dist/server-CkG3c5Za.mjs +0 -4
- package/dist/tui-B0KECXvO.mjs +0 -8
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/
|
|
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 `
|
|
54
|
-
| **project file** |
|
|
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
|
|
62
|
-
- **server** `-s`: the flag, else `server` from
|
|
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 `$
|
|
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/
|
|
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/
|
|
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 `$
|
|
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
|
|
222
|
+
lf deploy # later: redeploy using ./.landfall.json
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
The first deploy writes
|
|
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
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
355
|
-
- Overwritten files are backed up as `.<name>.
|
|
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/
|
|
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 `# >>>
|
|
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.
|
|
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/
|
|
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/
|
|
405
|
-
| `~/.config/
|
|
406
|
-
| `~/.config/
|
|
407
|
-
| `~/.config/
|
|
408
|
-
| `~/.ssh/
|
|
409
|
-
| `~/.ssh/config` | `Host <name>` blocks between `# >>>
|
|
410
|
-
|
|
|
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
|
-
| `
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
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/
|
|
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
|
|
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/
|
|
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
|
-
|
|
|
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` |
|