docs: versioned documentation site, and the four pages that were missing (v0.23.28-beta)
Closes #112. Source stays `docs/*.md`, rendered by MkDocs-Material and published to GitHub Pages by Actions. Same repository on purpose: documentation drifts, and co-location is the only thing that reliably prevents it — the pull request that changes behaviour changes the description, and review sees both. A separate repo would double the ceremony and guarantee divergence. The markdown remains readable when browsing the repository, so the site renders those files rather than replacing them. **Versioned from the start**, using mike. Images are published to GHCR and this project ships betas most days, so readers are routinely running something other than the newest build; a single "latest" would confidently describe a release they do not have. Versions follow the product rather than the docs, so the site and the running image carry the same number. **Content came first, and it had to.** The docs were eleven days and sixteen releases stale — SSH keys, bulk commands, the data-cap SMS, poller health and the timezone setting appeared in exactly zero files between them. Publishing that would have given documentation missing five of the most consequential features a polished, authoritative home, and a well-presented wrong answer is worse than an obviously rough one. Four pages now cover them: - cellular.md — signal grading, carrier aggregation, the uplink anchor, observed bands, and the data-cap SMS including why it fires early and how to calibrate the margin - ssh-keys.md — leading with what installing a key costs, because RouterOS disables password SSH for that user and the database becomes load-bearing - commands.md — why waves exist, and why reading output matters when RouterOS reports failure in text rather than an exit code - scaling.md — headroom, why it is insufficient on its own, and every tunable Pull requests build the site with `--strict`, which turns warnings into errors. That is the point of running it there at all, and it earned itself immediately: the first build failed on two links to the project README, which sits outside the docs tree. They are absolute now, so they resolve both in the rendered site and when reading the markdown on GitHub. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0e364b1558
commit
98a72e1f7a
@@ -0,0 +1,74 @@
|
||||
# SSH keys
|
||||
|
||||
[← Documentation index](README.md)
|
||||
|
||||
Each device can be given its own SSH keypair, used for backups, config export and bulk
|
||||
commands instead of a stored password.
|
||||
|
||||
## Read this before installing one
|
||||
|
||||
**Installing a key disables password SSH for that user.** RouterOS binds the key to the
|
||||
account and then refuses password authentication entirely — this is RouterOS behaviour,
|
||||
not a policy choice here, and it is confirmed on hardware: removing the key restores
|
||||
password login immediately.
|
||||
|
||||
Consequences worth stating plainly:
|
||||
|
||||
- Colleagues, scripts and other tools that SSH in with a password **will stop working**.
|
||||
- The **binary API and WinBox are unaffected** and remain a way in.
|
||||
- **If this server loses its database or `ENCRYPTION_KEY`, the private key goes with it.**
|
||||
SSH would then need recovering through the API or WinBox, because the password will
|
||||
already have been refused.
|
||||
- Revoking the key restores password SSH.
|
||||
|
||||
On a shared fleet this is a change to announce, not to discover.
|
||||
|
||||
## How deployment works
|
||||
|
||||
1. A unique **ed25519** keypair is generated on the manager — one per device, never a
|
||||
fleet key. A shared key would make any single compromise fleet-wide and would make
|
||||
rotating one device impossible without touching all of them.
|
||||
2. The public half is uploaded over the existing password session and imported.
|
||||
3. A **completely fresh connection** authenticates with the new key.
|
||||
4. Only then is anything stored.
|
||||
|
||||
Step 4 is the important one. Nothing trusts a key until it has been proved, and a
|
||||
deployment that fails verification **removes the key it just installed** rather than
|
||||
leaving a device demanding a credential nobody holds.
|
||||
|
||||
## Rotation
|
||||
|
||||
Rotation authenticates with the *existing* key, because the password is no longer an
|
||||
option once a key is installed. The replacement is deployed and proved before the old key
|
||||
is removed — removing first would leave a window in which neither works.
|
||||
|
||||
Each key carries a unique comment, so retiring one cannot remove another.
|
||||
|
||||
## Recovery
|
||||
|
||||
**The binary API is the recovery path**, not the password. It is unaffected by SSH key
|
||||
state and can remove a key at any time.
|
||||
|
||||
Revoking clears *every* key this manager has installed for a device, not only the one on
|
||||
record. Orphans left by a half-failed rotation would otherwise keep password SSH disabled
|
||||
with no way back through the product.
|
||||
|
||||
If deployment fails with an authentication error and orphaned keys are present, the error
|
||||
says so.
|
||||
|
||||
## Fleet-wide deployment
|
||||
|
||||
`POST /api/devices/ssh-keys/deploy-all` keys every device that has a password but no
|
||||
verified key. It runs sequentially rather than in parallel — each deployment opens two SSH
|
||||
sessions and ends by proving the key.
|
||||
|
||||
It **refuses without explicit confirmation**, because it disables password SSH on every
|
||||
device it touches.
|
||||
|
||||
## What uses a key
|
||||
|
||||
Backups and config export prefer a verified key where one exists and fall back to a
|
||||
password otherwise. [Bulk commands](commands.md) use the same resolution.
|
||||
|
||||
Only a *verified* key is used. One that was generated or pushed but never authenticated is
|
||||
not evidence the device will accept it.
|
||||
Reference in New Issue
Block a user