feat: tunnel guide in list, push-env, VERSION file

- list and tunnel print plain-language tunnel instructions for every instance: keep it open, run it in the
  background, close it, with a notice that background/close work on Mac and Linux only, not Windows
- push-env N TARGET puts an instance's .env values into another .env file, on this server or over SSH (Mac, Linux,
  Windows): existing keys are replaced, missing keys added, commented-out lines ignored, every other line kept.
  A temporary copy of the target is compared after writing and put back on any mismatch. --dry-run shows the
  changes without writing. SSH failures are explained with how to set SSH up.
- VERSION file (1.1.0), version line in the README, `--version` prints "supaspin 1.1.0"; a test keeps the file, the
  code constant and the README in step
- remove an unused parameter of run() and an unreachable second help branch in main()

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #2.
This commit is contained in:
Igor K.
2026-09-19 12:22:13 +03:00
co-authored by Claude Sonnet 5
parent cfbc4f1612
commit 20704e128f
4 changed files with 1345 additions and 23 deletions
+86 -9
View File
@@ -1,5 +1,7 @@
# supaspin
Version 1.1.0
Create, list, show and delete throwaway [Supabase](https://supabase.com) instances on a
single-node [microk8s](https://microk8s.io) server, from a terminal menu.
@@ -87,9 +89,10 @@ Supabase Helm chart (0.8.0, Postgres 17.6.1.136) once into `/var/lib/supaspin/ch
| Menu item | What it does |
|---|---|
| Create instance | picks the lowest free number, asks whether to use the saved mail settings or enter different ones, creates the disks, installs Supabase, waits until every pod is ready, writes `<root>/<N>/.env` |
| List instances | number, hostname, pods ready, memory in use, disk use per image, `.env` path |
| List instances | number, hostname, pods ready, memory in use, disk use per image, `.env` path, and for each instance the tunnel command with plain instructions (open it, keep it in the background, close it) |
| Show instance | the `.env` (secrets hidden unless you ask), the tunnel command, pod status and recent events |
| Delete instance | asks you to type the number; removes everything (see below) |
| Copy instance values into another .env file | puts an instance's `.env` values into your app's `.env`, on this server or on another computer (see below) |
| Mail settings | view and change the saved default mail settings |
| Settings | change any first-run setting |
@@ -100,10 +103,12 @@ sudo supaspin create [--ask-mail] # --ask-mail: choose saved or different ma
sudo supaspin list
sudo supaspin show N # .env with secrets hidden, plus tunnel help
sudo supaspin env N # the full .env (contains secrets)
sudo supaspin tunnel N
sudo supaspin tunnel N # the tunnel commands, step by step
sudo supaspin push-env N TARGET [--port P] [--key FILE] [--dry-run] # see "Copying an instance's values"
sudo supaspin delete N [--yes]
sudo supaspin mail # change the saved default mail settings
sudo supaspin setup # change settings
supaspin --version # prints "supaspin 1.1.0"
```
### Connecting your app
@@ -115,15 +120,80 @@ names: `SUPABASE_URL`, `PUBLIC_SUPABASE_URL`, `API_EXTERNAL_URL`, `ANON_KEY`,
`GOTRUE_SMTP_*`. Read it with `sudo supaspin env N` and copy what your app needs.
The web API is public, so the URL and keys work from anywhere. **Postgres and Studio are not
public**; from outside the server's network use an SSH tunnel:
public.** To reach them from your own computer you open a *tunnel*: a private connection from your
computer to the server that makes the instance's database and Studio (its admin screen) appear on
your own computer, at `localhost:544N` (database) and `http://localhost:300N` (Studio), for as long
as the tunnel is open. `sudo supaspin list` prints the exact commands for every instance
(`sudo supaspin tunnel N` prints them with a few more hints). Run them in a terminal on **your**
computer, not on the server.
1. On your computer, run the command from `supaspin tunnel N` in its own terminal and leave it open:
`ssh -N -p 2222 -L 5441:192.168.10.30:5441 -L 3001:192.168.10.30:3001 you@your.server`
2. Put `DATABASE_URL=postgresql://postgres:<password>@localhost:5441/postgres` in your app's
`.env` (the password is `POSTGRES_PASSWORD` in the instance's `.env`).
3. Studio is at `http://localhost:3001`.
**Which computer you use matters.** The commands for keeping the tunnel in the background and for
closing it work on **Mac and Linux only**. They do **not** work on **Windows**, because the ssh
program that comes with Windows does not support the "control file" they need. On Windows use the
first command (keep the tunnel open in its own PowerShell window) and close it with Ctrl+C or by
closing the window.
On the server's own LAN skip the tunnel and use `<LAN IP>:5441` and `http://<LAN IP>:3001`.
- If you want to open the tunnel and keep it open in your terminal window (Mac, Linux, Windows),
run the `ssh -N ...` command. The window looks stuck and prints nothing: that is normal, it means
the tunnel is open. Press Ctrl+C to close it.
- If you want the tunnel to run in the background so your terminal stays free (Mac and Linux only),
run the `ssh -f -N -M -S /tmp/supaspin-N.sock ...` command. It asks for your password if needed,
then gives you your terminal back.
- If you no longer need the background tunnel and want to close it (Mac and Linux only), run
`ssh -S /tmp/supaspin-N.sock -O exit you@your.server`. It answers "Exit request sent."
- If you want to check whether the background tunnel is running (Mac and Linux only), run
`ssh -S /tmp/supaspin-N.sock -O check you@your.server`. It answers "Master running" if it is.
- If closing says "No such file or directory" but the tunnel still works, or starting a tunnel says
"address already in use" and the check does not say "Master running", a leftover tunnel is in the
way: run `pkill -f "[s]upaspin-N.sock"` (Mac and Linux only; the `[s]` is on purpose, it stops the command from
stopping itself) and start the tunnel again.
While the tunnel is open, put `DATABASE_URL=postgresql://postgres:<password>@localhost:5441/postgres`
in your app's `.env` (the password is `POSTGRES_PASSWORD` in the instance's `.env`) and open Studio
at `http://localhost:3001`. On the server's own LAN you do not need a tunnel: use
`<LAN IP>:5441` and `http://<LAN IP>:3001`.
### Copying an instance's values into your app's .env
If you want your app's `.env` to point at an instance, run:
```bash
sudo supaspin push-env 1 igor@192.168.10.50:/Users/igor/app/.env # a Mac or Linux computer
sudo supaspin push-env 1 "igor@192.168.10.50:C:/Users/igor/app/.env" # a Windows computer
sudo supaspin push-env 1 /home/igor/app/.env # a file on this server
sudo supaspin push-env 1 --dry-run igor@192.168.10.50:/Users/igor/app/.env # only show what would change
```
or choose "Copy instance values into another .env file" in the menu, which asks for everything
(instance, file, SSH user, port and key file). Options: `--port` (SSH port, default 22), `--key`
(key file), `--dry-run` (write nothing). If the SSH server needs a password, SSH asks for it in
your terminal, once.
What it does with the target file:
- Every `KEY=VALUE` line of the instance's `.env` is put in. If the key is already in the file
(also as `export KEY=...`), its value is replaced. If it is not, the line is added at the end.
- Lines that start with `#` are ignored completely. `# ANON_KEY=old` is left as it is, and
`ANON_KEY` counts as missing, so a new `ANON_KEY=...` line is added.
- Every other line (other keys, comments, blank lines, their order, line endings, the file's owner
and permissions) stays exactly as it was. The file must already exist; it is never created.
- It never prints values, only key names: replaced, added, already correct, and how many other
lines stayed untouched. It ends with SUCCESS or FAILED and the reason, and its exit status is
0 or 1.
Safety net: before the file is changed, an untouched copy is kept in a private temporary folder
(in memory, in `/run`). After writing, the file is read back and compared with that copy: nothing
but the instance's keys may differ, and each of them must have the instance's value. If anything is
off, the copy is written back and checked. The copy is deleted at the end. Only if even the
write-back fails is it kept, and its path is printed so that you can restore the file by hand.
The other computer can be a Mac, a Linux computer or a Windows computer. It only needs an SSH server
with file copying (SFTP) switched on and this server must be able to reach it. Files are copied with
`scp`, so nothing has to be installed there. The SSH connection is made as the person who typed
`sudo`, so your own keys and known computers apply. If the connection fails, supaspin says why and
what to do: for example how to switch SSH on (Mac: System Settings > General > Sharing > Remote
Login; Linux: `sudo apt install openssh-server`; Windows: the optional feature "OpenSSH Server"),
how to install this server's key (`ssh-copy-id`), or how to trust a new computer.
### Deleting
@@ -187,6 +257,13 @@ The tests use Python's built-in `unittest` and need no extra packages:
python3 -m unittest discover -s tests
```
### Versions
The version is written in three places that must always show the same number: the file `VERSION`, the
`VERSION` constant at the top of `supaspin`, and the line "Version x.y.z" at the top of this README. A test
fails if they differ. Every release is a git tag `vX.Y.Z` (for example `v1.1.0`) on the commit that has that
number in `VERSION`.
## License
MIT, see `LICENSE`.