docs: spell out .spbk and .spde extensions

SelfPost backup / SelfPost domain export — in the guide, security notes,
architecture, and Backup/Export panel copy.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mixeme
2026-08-09 00:49:43 +03:00
parent 1dd0aa9fea
commit 18880a8286
7 changed files with 27 additions and 17 deletions
+3
View File
@@ -51,6 +51,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
### Changed ### Changed
- Document what `.spbk` and `.spde` stand for (SelfPost backup / SelfPost domain
export) in the operator guide, security notes, architecture, and the Backup /
Export panel copy.
- Docs aligned with the code: setup URL is `/setup/<token>` (README and - Docs aligned with the code: setup URL is `/setup/<token>` (README and
guide; local trial rewrites the printed `https://<hostname>/…` link to guide; local trial rewrites the printed `https://<hostname>/…` link to
`http://127.0.0.1:8080/…`); domain import uses the file extension / magic `http://127.0.0.1:8080/…`); domain import uses the file extension / magic
+4 -4
View File
@@ -290,10 +290,10 @@ restore. Stopped-container `tar` of `./data` is safe (see guide).
([internal/secretfile](../internal/secretfile/secretfile.go)): password → ([internal/secretfile](../internal/secretfile/secretfile.go)): password →
scrypt → AES-256-GCM over 64 KiB chunks, each authenticated with the header, scrypt → AES-256-GCM over 64 KiB chunks, each authenticated with the header,
its counter and an end-of-stream flag (so truncation and reordering fail to its counter and an end-of-stream flag (so truncation and reordering fail to
open). Full backup `.tar.gz``.spbk`, domain export `.json``.spde`; the open). Full backup `.tar.gz``.spbk` (SelfPost backup), domain export
plain forms remain the default. Domain import detects the envelope by magic `.json``.spde` (SelfPost domain export); the plain forms remain the
bytes; an encrypted full backup is converted back with `selfpost-backup default. Domain import detects the envelope by magic bytes; an encrypted full
-decrypt` before restore. backup is converted back with `selfpost-backup -decrypt` before restore.
--- ---
+5 -2
View File
@@ -332,8 +332,11 @@ file that comes down is an encrypted envelope instead of the plain archive:
| Artefact | Plain | Encrypted | | Artefact | Plain | Encrypted |
|----------|-------|-----------| |----------|-------|-----------|
| Full backup | `.tar.gz` | `.spbk` | | Full backup | `.tar.gz` | `.spbk` (**S**elf**P**ost **b**ac**k**up) |
| Domain export | `.json` | `.spde` | | Domain export | `.json` | `.spde` (**S**elf**P**ost **d**omain **e**xport) |
The suffixes are for the operator only — the server detects an encrypted file
by its magic bytes (`SELFPOST1`), not by the extension.
The key is derived from the password with scrypt and the contents are sealed The key is derived from the password with scrypt and the contents are sealed
with AES-256-GCM, in chunks, so a truncated or altered file fails to open rather with AES-256-GCM, in chunks, so a truncated or altered file fails to open rather
+4 -3
View File
@@ -80,9 +80,10 @@ Hardening сверх обязательного (security-заголовки, п
аутентифицирован заголовком, номером и флагом конца потока — обрезанный или аутентифицирован заголовком, номером и флагом конца потока — обрезанный или
подменённый файл не открывается вместо тихого восстановления «хвоста». подменённый файл не открывается вместо тихого восстановления «хвоста».
Формат и обёртка: [internal/secretfile](../internal/secretfile/secretfile.go). Формат и обёртка: [internal/secretfile](../internal/secretfile/secretfile.go).
- Расширения: `.spbk` (полный бэкап), `.spde` (экспорт домена); незашифрованные - Расширения: `.spbk` (**S**elf**P**ost **b**ac**k**up — полный бэкап),
остаются `.tar.gz` / `.json`. Импорт домена определяет шифрование по magic `.spde` (**S**elf**P**ost **d**omain **e**xport — экспорт домена);
файла, а не по расширению. незашифрованные остаются `.tar.gz` / `.json`. Импорт домена определяет
шифрование по magic файла, а не по расширению.
- Пароль нигде не сохраняется: восстановить файл без него нельзя. Пароль в CLI — - Пароль нигде не сохраняется: восстановить файл без него нельзя. Пароль в CLI —
только через `SELFPOST_BACKUP_PASSWORD` или `-password-file`, никогда только через `SELFPOST_BACKUP_PASSWORD` или `-password-file`, никогда
аргументом (список процессов читается любым процессом контейнера). аргументом (список процессов читается любым процессом контейнера).
+6 -4
View File
@@ -11,7 +11,8 @@
<p class="muted"><strong>The backup file is a secret</strong> (it contains <p class="muted"><strong>The backup file is a secret</strong> (it contains
private keys and credentials). Store and transfer it securely and delete it private keys and credentials). Store and transfer it securely and delete it
once the restore succeeds. Encrypting it below is the simplest way to do that: once the restore succeeds. Encrypting it below is the simplest way to do that:
the download is then a <code>.spbk</code> file that only the password opens.</p> the download is then a <code>.spbk</code> file (SelfPost backup) that only
the password opens.</p>
{{if .BackupErr}}<p class="error">{{.BackupErr}}</p>{{end}} {{if .BackupErr}}<p class="error">{{.BackupErr}}</p>{{end}}
<form method="post" action="/backup"> <form method="post" action="/backup">
{{template "encryptfields" .}} {{template "encryptfields" .}}
@@ -22,9 +23,10 @@
<div class="card"> <div class="card">
<h2>Import a domain</h2> <h2>Import a domain</h2>
<p class="muted">Move a single domain here from another SelfPost instance using <p class="muted">Move a single domain here from another SelfPost instance using
a domain export file (from that domain's page). Its DKIM key and application a domain export file (from that domain's page) — plain <code>.json</code> or
passwords come across, so the published DNS record needs no change. The export encrypted <code>.spde</code> (SelfPost domain export). Its DKIM key and
file is a secret, like a full backup.</p> application passwords come across, so the published DNS record needs no
change. The export file is a secret, like a full backup.</p>
{{if .ImportErr}}<p class="error">{{.ImportErr}}</p>{{end}} {{if .ImportErr}}<p class="error">{{.ImportErr}}</p>{{end}}
<form method="post" action="/domains/import" enctype="multipart/form-data"> <form method="post" action="/domains/import" enctype="multipart/form-data">
<label for="importfile">Domain export file</label> <label for="importfile">Domain export file</label>
+1 -1
View File
@@ -299,7 +299,7 @@
<p class="muted"><strong>The export file is a secret</strong> — it contains the <p class="muted"><strong>The export file is a secret</strong> — it contains the
private DKIM key and application passwords. Transfer it securely and delete it private DKIM key and application passwords. Transfer it securely and delete it
after the import, or encrypt it below and move a <code>.spde</code> file after the import, or encrypt it below and move a <code>.spde</code> file
instead; the import form asks for the password.</p> (SelfPost domain export) instead; the import form asks for the password.</p>
{{if .ExportErr}}<p class="error">{{.ExportErr}}</p>{{end}} {{if .ExportErr}}<p class="error">{{.ExportErr}}</p>{{end}}
<form method="post" action="/domains/{{.Domain.ID}}/export"> <form method="post" action="/domains/{{.Domain.ID}}/export">
{{template "encryptfields" .}} {{template "encryptfields" .}}
+4 -3
View File
@@ -1,9 +1,10 @@
{{/* Password fields shared by the full-backup and domain-export forms. The {{/* Password fields shared by the full-backup and domain-export forms. The
checkbox is the switch: unticked, the download keeps its historic plain checkbox is the switch: unticked, the download keeps its historic plain
form (.tar.gz / .json); ticked, the file is sealed in a password-encrypted form (.tar.gz / .json); ticked, the file is sealed in a password-encrypted
envelope (.spbk / .spde). The fields start hidden and are revealed by envelope (.spbk = SelfPost backup, .spde = SelfPost domain export). The
panel.js — with JavaScript blocked they are simply always visible, and the fields start hidden and are revealed by panel.js — with JavaScript blocked
server still decides from the checkbox alone. */}} they are simply always visible, and the server still decides from the
checkbox alone. */}}
{{define "encryptfields"}} {{define "encryptfields"}}
<div class="encrypt"> <div class="encrypt">
<label class="check"> <label class="check">