From f1aef3d7b0578bc3029bfa64ea85094d1a242a1e Mon Sep 17 00:00:00 2001 From: usermaneGermane Date: Mon, 9 Nov 2026 22:34:37 +0200 Subject: [PATCH] Rename to SecretSpace (display names only) Owner decision 2026-11-09: another password manager is already called PassMan. Every name a reader sees is now SecretSpace: README, messages, the setup's screens, the version resource, and on Windows the installer (SecretSpace-Server-Installer.exe), its Settings -> Apps entry, the Start Menu folder and the default program folder. What a running server is made of keeps its name, so an installed server needs no change and an upgrade finds and replaces it: - Linux: service passman-sync, user passman, /opt/passman/server, /etc/passman, /var/lib/passman/store (the prod box runs these); - Windows: PassMan-Server.exe and PassMan-Server-Setup.exe (the startup task starts the first), the task and firewall group "PassMan Sync Server", %ProgramData%\PassMan Server, the installer AppId; - both: passman_server.py / passman_setup.py, the X-PassMan-* headers and the PassManSync/1.0 server id of the sync protocol. The README says so in a "Formerly PassMan" note. passman_server.py and passman_setup.py stay identical in both server repos. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011ojnyVg7ccsR3pZ64s7aRH --- README.md | 79 +++++++++++++++++++++---------------- SECURITY.md | 2 +- server/passman-sync.service | 2 +- server/passman_server.py | 4 +- server/passman_setup.py | 10 ++--- server/version.py | 4 +- tests/test_server_setup.py | 4 +- 7 files changed, 58 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 309ab14..f7127ad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PassMan sync server for Linux +# SecretSpace sync server for Linux A place to keep your encrypted vault so two PCs can stay in step. It stores one blob per user, numbers each revision, and refuses a write computed against @@ -10,23 +10,34 @@ ciphertext and no key. There is no account system, no password reset and no recovery path here, by design — the only secret that matters never leaves your PC. +**Formerly PassMan.** The project was renamed to SecretSpace in November +2026, because another password manager already uses the name PassMan. Only +the names you read changed. What a running server is made of keeps its +name, so an existing server needs no change and updates as before: the +service `passman-sync`, the user `passman`, `/opt/passman/server`, +`/etc/passman`, `/var/lib/passman/store`, the files `passman_server.py` and +`passman_setup.py`, and the `X-PassMan-*` headers of the sync protocol. A +clone made before the rename is in `~/PassMan-Server-Linux`; it keeps +working (GitHub forwards the old address), so use that folder wherever this +README says `~/SecretSpace-Server-Linux`. + Standard library only: no `pip install`, no virtualenv, nothing to keep patched beyond Python itself. -**Which apps it serves.** PassMan for Windows 2.x and PassMan for Linux -0.x sync with it. The PassMan 3 test versions (Windows 3.0.0-alpha, Linux +**Which apps it serves.** SecretSpace for Windows 2.x and SecretSpace for Linux +0.x sync with it. The SecretSpace 3 test versions (Windows 3.0.0-alpha, Linux 1.0.0-alpha) do not sync yet: they need accounts on the server, which come with server 2.0. This repository is the server for **Linux**, run by systemd. The other -parts of PassMan have their own repositories: +parts of SecretSpace have their own repositories: | Repository | What it holds | |---|---| -| [PassMan-Client-Windows](https://github.com/usermaneGermane/PassMan-Client-Windows) | the app for Windows — what your PCs run | -| [PassMan-Client-Linux](https://github.com/usermaneGermane/PassMan-Client-Linux) | the app for Linux — from source or a .deb package | -| [PassMan-Server-Windows](https://github.com/usermaneGermane/PassMan-Server-Windows) | the same server for Windows, with an installer | -| **PassMan-Server-Linux** (this one) | the server for Linux | +| [SecretSpace-Client-Windows](https://github.com/usermaneGermane/SecretSpace-Client-Windows) | the app for Windows — what your PCs run | +| [SecretSpace-Client-Linux](https://github.com/usermaneGermane/SecretSpace-Client-Linux) | the app for Linux — from source or a .deb package | +| [SecretSpace-Server-Windows](https://github.com/usermaneGermane/SecretSpace-Server-Windows) | the same server for Windows, with an installer | +| **SecretSpace-Server-Linux** (this one) | the server for Linux | ## Before you start @@ -53,13 +64,13 @@ number set to `0`: `192.168.0.3/24` means `192.168.0.0/24`. ### 1. Get the code ```bash -git clone --depth 1 https://github.com/usermaneGermane/PassMan-Server-Linux.git ~/PassMan-Server-Linux +git clone --depth 1 https://github.com/usermaneGermane/SecretSpace-Server-Linux.git ~/SecretSpace-Server-Linux ``` Or download `passman-server-linux-.tar.gz` from -[Releases](https://github.com/usermaneGermane/PassMan-Server-Linux/releases) +[Releases](https://github.com/usermaneGermane/SecretSpace-Server-Linux/releases) and unpack it; use that folder wherever these steps say -`~/PassMan-Server-Linux`. +`~/SecretSpace-Server-Linux`. This is only the source. The running service gets its own copy in step 2, so nothing you do in this folder later — pulling, switching branches, deleting @@ -70,7 +81,7 @@ it — touches the live server until you choose to update it. ```bash sudo useradd --system --no-create-home --shell /usr/sbin/nologin passman sudo mkdir -p /opt/passman/server /var/lib/passman/store /etc/passman -sudo cp ~/PassMan-Server-Linux/server/passman_server.py ~/PassMan-Server-Linux/server/passman_setup.py /opt/passman/server/ +sudo cp ~/SecretSpace-Server-Linux/server/passman_server.py ~/SecretSpace-Server-Linux/server/passman_setup.py /opt/passman/server/ sudo chown -R passman:passman /var/lib/passman sudo python3 /opt/passman/server/passman_setup.py @@ -95,7 +106,7 @@ prints the lines to run. At the end it prints the address to enter on your PCs. Run it again any time to change the address or port — see [Changing the address or port](#changing-the-address-or-port). -Why the copy into `/opt` rather than running from `~/PassMan-Server-Linux`: the service +Why the copy into `/opt` rather than running from `~/SecretSpace-Server-Linux`: the service unit sets `ProtectHome=yes`, which hides `/home` from it entirely, and on recent Ubuntu home folders are not readable by other users anyway. Run it from your home folder and it fails with "file not found". @@ -177,7 +188,7 @@ DHCP reservation stops being a nicety and becomes a requirement. ### 5. Install it as a service ```bash -sudo cp ~/PassMan-Server-Linux/server/passman-sync.service /etc/systemd/system/ +sudo cp ~/SecretSpace-Server-Linux/server/passman-sync.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now passman-sync systemctl status passman-sync @@ -191,7 +202,7 @@ The unit ships with the paths from step 2 and takes the address and port from edit. If it fails with `Failed to load environment files`, step 2's setup has not been run. -### 6. Connect PassMan +### 6. Connect SecretSpace Show the token and copy it: @@ -272,7 +283,7 @@ copied, change the master password too. When `main` has changed: ```bash -cd ~/PassMan-Server-Linux && git pull +cd ~/SecretSpace-Server-Linux && git pull sudo cp server/passman_server.py server/passman_setup.py /opt/passman/server/ sudo systemctl restart passman-sync ``` @@ -285,7 +296,7 @@ port moved into `sync.env`): the new unit file has no `--host`/`--port` and will not start without `sync.env`, so do it in this order: ```bash -cd ~/PassMan-Server-Linux && git pull +cd ~/SecretSpace-Server-Linux && git pull sudo cp server/passman_server.py server/passman_setup.py /opt/passman/server/ sudo python3 /opt/passman/server/passman_setup.py # Enter, Enter keeps 0.0.0.0 and 8787 sudo cp server/passman-sync.service /etc/systemd/system/ @@ -295,14 +306,14 @@ sudo systemctl daemon-reload && sudo systemctl restart passman-sync The setup keeps your existing token. If you had used `sed` to put your LAN address in the old unit, pick that network in the setup instead. -**If your checkout is of the old `PassMan` repository** (the one that held -the app and the server together, cloned to `~/PassMan`): it gets no more +**If your checkout is of the old `SecretSpace` repository** (the one that held +the app and the server together, cloned to `~/SecretSpace`): it gets no more server updates. Clone this repository as in [step 1](#1-get-the-code) and update from it as above — the running copy in `/opt/passman`, the token, -the settings and the vaults stay as they are. Then delete `~/PassMan` when +the settings and the vaults stay as they are. Then delete `~/SecretSpace` when you like. -**Update the server before changing a master password** in PassMan. The +**Update the server before changing a master password** in SecretSpace. The server now records how many times each vault's password has changed; an older server does not, and after a second change it can send a PC the wrong way — told to adopt a password that is really its own previous one. If `passman-sync.service` itself changed, copy it @@ -335,10 +346,10 @@ firewall. | `PASSMAN_PORT must be a port number` in the log | `sync.env` was edited by hand into something that is not a port — run the setup again | | `Cannot read the token file` in the log | `/etc/passman/token` is missing, or not owned by `passman` — redo the `chown` in step 2 | | `Address already in use` | Something else on the port — often the hand-started copy from step 3 still running | -| PassMan: "rejected the sync token" | A stray space or line break when pasting; copy it again. Or the token was replaced with `new-token` — paste the new one | -| PassMan: "Could not reach …" | Wrong address or port in Settings → Sync, the PC is on another network, or the firewall is blocking it | +| SecretSpace: "rejected the sync token" | A stray space or line break when pasting; copy it again. Or the token was replaced with `new-token` — paste the new one | +| SecretSpace: "Could not reach …" | Wrong address or port in Settings → Sync, the PC is on another network, or the firewall is blocking it | | It worked, then stopped | The server's address changed — see below | -| PassMan asks for a master password when you press Sync | Expected after a password change on another PC (or on this one while offline) — see [Changing the master password](https://github.com/usermaneGermane/PassMan-Client-Windows#changing-the-master-password-of-a-vault-you-sync) | +| SecretSpace asks for a master password when you press Sync | Expected after a password change on another PC (or on this one while offline) — see [Changing the master password](https://github.com/usermaneGermane/SecretSpace-Client-Windows#changing-the-master-password-of-a-vault-you-sync) | ## If the server's address changes @@ -386,10 +397,10 @@ To get rid of a vault for good: PCs first, server second: the other way round, a PC that syncs in between simply puts it back. -> PassMan builds from before this was fixed did the opposite — a PC syncing +> SecretSpace builds from before this was fixed did the opposite — a PC syncing > against a server whose copy had been removed **deleted every entry on the > PC**. If your PCs have not been rebuilt since, follow the order above -> strictly. To recover from it: close PassMan without saving anything else, +> strictly. To recover from it: close SecretSpace without saving anything else, > then copy `vault\.vault.enc.bak` over > `vault\.vault.enc` — the backup holds the version from just > before that sync, until the next save replaces it. @@ -404,7 +415,7 @@ simply puts it back. | `/etc/passman/sync.env` | the address and port (`PASSMAN_HOST`, `PASSMAN_PORT`) | | `/etc/passman/token` | the shared token (`600`, owned by `passman`) | | `/var/lib/passman/store/` | the encrypted vaults and their history | -| `~/PassMan-Server-Linux/` | your git checkout — only the source | +| `~/SecretSpace-Server-Linux/` | your git checkout — only the source | ## LAN or VPN @@ -456,7 +467,7 @@ stacks ignore `CN`. For a quick try-out, from the checkout: ```bash -cd ~/PassMan-Server-Linux/server +cd ~/SecretSpace-Server-Linux/server python3 -c "import secrets; print(secrets.token_urlsafe(32))" > token chmod 600 token python3 passman_server.py --host 0.0.0.0 --port 8787 @@ -504,14 +515,14 @@ Inside the store directory (`/var/lib/passman/store` with the service): /history/*.enc the last 20 revisions — encrypted ``` -One folder per PassMan username, so several vaults can share one server +One folder per SecretSpace username, so several vaults can share one server without touching each other. `vault.enc` and the history are the same AES-256-GCM files as on the PCs; the server never has the key. `meta.json` and the folder name are plain: they reveal the username, when it last synced and roughly how large the vault is — never an entry. Each history file stays encrypted with the master password in use when it was -written. When you change a master password, PassMan offers to delete them — +written. When you change a master password, SecretSpace offers to delete them — ticked by default — since they would still open with the old one. The key generation in `meta.json` counts those changes; it holds nothing secret, and lets a PC tell whether the copy here is under an older or a newer password @@ -560,12 +571,12 @@ them on Ubuntu for every pull request and every push to `main`. 127.0.0.1, with no client involved: the token, which paths exist, If-Match (including two pushes racing for one revision), malformed and oversized bodies, the history limit and the access log. It is the same file in -PassMan-Server-Windows. +SecretSpace-Server-Windows. `server/passman_server.py` and `server/passman_setup.py` are the same files -as in PassMan-Server-Windows — `passman_setup.py` holds the setup for both +as in SecretSpace-Server-Windows — `passman_setup.py` holds the setup for both systems. Make a change to either in both repositories, so the two servers -never drift apart. PassMan-Client-Windows and PassMan-Client-Linux each keep +never drift apart. SecretSpace-Client-Windows and SecretSpace-Client-Linux each keep a copy of `passman_server.py` for their end-to-end sync tests; refresh those too. @@ -576,7 +587,7 @@ Free software under the **GNU General Public License v3.0 only** share it; if you distribute a changed version, it must stay under the same licence, with its source. -The other PassMan repositories (the Windows and Linux clients and sync servers) +The other SecretSpace repositories (the Windows and Linux clients and sync servers) are free software under the same licence. Security issues: please report them privately, see [SECURITY.md](SECURITY.md). diff --git a/SECURITY.md b/SECURITY.md index 5b9e450..714b446 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security policy -PassMan keeps passwords, so security reports are welcome and taken seriously. +SecretSpace keeps passwords, so security reports are welcome and taken seriously. ## Reporting a vulnerability diff --git a/server/passman-sync.service b/server/passman-sync.service index 530b236..ea26eea 100644 --- a/server/passman-sync.service +++ b/server/passman-sync.service @@ -1,5 +1,5 @@ [Unit] -Description=PassMan sync server (encrypted vault blob store) +Description=SecretSpace sync server (encrypted vault blob store) After=network-online.target Wants=network-online.target diff --git a/server/passman_server.py b/server/passman_server.py index 4b2c557..47e7d98 100644 --- a/server/passman_server.py +++ b/server/passman_server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -PassMan sync server -- a store for encrypted vault blobs. +SecretSpace sync server -- a store for encrypted vault blobs. What it is NOT: it cannot read your passwords. The client encrypts the vault with your master password before it ever reaches the network, so this process @@ -524,7 +524,7 @@ def main(argv=None): # flush: under systemd stdout is a pipe and fully buffered, so without it # these lines never reach the journal while the server is running -- and # a silent journal after `systemctl start` reads as a failure. - print(f"PassMan sync on {scheme}://{args.host}:{args.port}") + print(f"SecretSpace sync on {scheme}://{args.host}:{args.port}") print(f"Vault store: {httpd.store.root}") if scheme == "http" and args.host not in ("127.0.0.1", "localhost"): print("NOTE: plain HTTP on a network address. Vaults are encrypted " diff --git a/server/passman_setup.py b/server/passman_setup.py index 748bf26..f099e74 100644 --- a/server/passman_setup.py +++ b/server/passman_setup.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 """ -PassMan sync server setup -- where it listens, its token, its firewall rule. +SecretSpace sync server setup -- where it listens, its token, its firewall rule. Linux (systemd): sudo python3 passman_setup.py # ask, then write the settings sudo python3 passman_setup.py new-token # replace a leaked token sudo python3 passman_setup.py show-token # print the token -Windows (PassMan-Server-Installer.exe runs the first one for you): +Windows (SecretSpace-Server-Installer.exe runs the first one for you): PassMan-Server-Setup.exe [setup] | new-token | show-token | remove `setup` asks which address to listen on (listing this machine's networks) @@ -172,7 +172,7 @@ def read_env(path): def render_env(host, port, allow=()): - text = ("# PassMan sync server -- written by passman_setup.py.\n" + text = ("# SecretSpace sync server -- written by passman_setup.py.\n" "# Change it by running the setup again.\n" f"PASSMAN_HOST={host}\n" f"PASSMAN_PORT={port}\n") @@ -447,7 +447,7 @@ def restart_task(run): def firewall_windows(run, host, port, addresses, allow=()): """ - Replace PassMan's inbound rule and say what it allows. Every network + Replace SecretSpace's inbound rule and say what it allows. Every network means LocalSubnet -- the networks this PC is on, whichever they are today -- and one network means just that range; the extra addresses are added to either. This machine only needs no rule at all. @@ -655,7 +655,7 @@ def cmd_menu(args, ask=input, say=print, run=_run): while True: current = read_env(env_path) say("") - say("PassMan sync server") + say("SecretSpace sync server") if "PASSMAN_HOST" in current and "PASSMAN_PORT" in current: say(f" Listening on {current['PASSMAN_HOST']}:" f"{current['PASSMAN_PORT']}") diff --git a/server/version.py b/server/version.py index 20242fa..3aace2e 100644 --- a/server/version.py +++ b/server/version.py @@ -4,7 +4,7 @@ release commit v. It is kept apart from passman_server.py on purpose: that file is the same -in PassMan-Server-Windows, which has its own version. +in SecretSpace-Server-Windows, which has its own version. """ -__version__ = "1.4.2" +__version__ = "1.5.0" diff --git a/tests/test_server_setup.py b/tests/test_server_setup.py index a4106d6..0da7b47 100644 --- a/tests/test_server_setup.py +++ b/tests/test_server_setup.py @@ -639,11 +639,11 @@ def broken(address, handler): def test_the_task_runs_the_server_not_the_setup_program(): """Run from PassMan-Server-Setup.exe, the task must still start PassMan-Server.exe beside it -- never the setup program itself.""" - setup = os.path.join("C:\\Program Files", "PassMan Server", + setup = os.path.join("C:\\Program Files", "SecretSpace Server", "PassMan-Server-Setup.exe") program, prefix = passman_setup.server_command(frozen=True, executable=setup) - assert program == os.path.join("C:\\Program Files", "PassMan Server", + assert program == os.path.join("C:\\Program Files", "SecretSpace Server", "PassMan-Server.exe") assert prefix == []