Documentation
¶
Overview ¶
Package backup is the app data backup/restore core (plan §7.10, §16). The docker orchestration (throwaway RO backup containers, DB-dump sidecars) is write-plane; THIS file is the security-critical, I/O-streaming crypto + the hostile-input hardening (restore.go) + the prune denylist (denylist.go), all pure/streaming and exhaustively testable.
stream.go is the chunked AES-256-GCM pipeline: tar → gzip → THIS → sink. It never buffers the whole stream in RSS (works on a tiny box), and the chunk chaining makes tampering, REORDERING, and TRUNCATION all detectable — a silently-truncated or shuffled backup must never restore as "success".
Index ¶
- Variables
- func Decrypt(dst io.Writer, src io.Reader, key []byte) error
- func Encrypt(dst io.Writer, src io.Reader, key []byte, maxBytes int64) error
- func PruneImageSafe(digest string, s LiveState) (bool, string)
- func PruneVolumeSafe(name string, s LiveState) (bool, string)
- func RestoreTuple(plaintextSHA, ciphertextSHA string, targetVolumes, bindings []string, ...) string
- func SafeExtract(tr *tar.Reader, w FileWriter, lim ExtractLimits) error
- type ExtractLimits
- type FileWriter
- type LiveState
Constants ¶
This section is empty.
Variables ¶
var ErrCorrupt = errors.New("backup: stream is corrupt or tampered")
ErrCorrupt means the stream failed authentication (tamper / wrong key / reorder / truncation) — fail-closed, never a partial restore.
Functions ¶
func Decrypt ¶
Decrypt reads the chunked stream from src, authenticates every chunk IN ORDER, and writes the plaintext to dst. It returns ErrCorrupt on any auth failure, a missing/early final chunk (truncation), or extra data after the final chunk.
func Encrypt ¶
Encrypt reads plaintext from src and writes the chunked, authenticated stream to dst. maxBytes caps the plaintext (0 = no cap); exceeding it errors rather than writing an unbounded archive. The AAD of each chunk binds its INDEX and a final flag, so a decryptor detects a dropped, duplicated, or reordered chunk.
func PruneImageSafe ¶
PruneImageSafe reports whether an image (by resolved digest) may be pruned. It denies anything provably unsafe; everything else is the safe complement.
func PruneVolumeSafe ¶
PruneVolumeSafe reports whether a volume may be removed. A sole-data volume may be removed ONLY if it is verified present in backup_inventory ("back it up first").
func RestoreTuple ¶
func RestoreTuple(plaintextSHA, ciphertextSHA string, targetVolumes, bindings []string, sizes []int64) string
RestoreTuple binds the FULL restore operation (plan §7.10): plaintext + ciphertext digests, the resolved target volumes, the service-binding set, and the member sizes. The confirm token is hash(tuple); it is re-derived under the held write-plane lock at execute and VOIDED on any drift (not just archive bytes), so a swapped archive / re-pointed target / changed size invalidates the confirmation.
func SafeExtract ¶
func SafeExtract(tr *tar.Reader, w FileWriter, lim ExtractLimits) error
SafeExtract walks a tar stream, enforcing confinement + special-file rejection + the zip-bomb caps, and emits each confined member through w. It returns the first violation (fail-closed: a single bad member aborts the whole restore).
Types ¶
type ExtractLimits ¶
type ExtractLimits struct {
MaxTotalBytes int64 // total decompressed bytes across all members
MaxMemberBytes int64 // a single member's size
MaxMembers int // number of members
}
ExtractLimits bounds a restore extraction (fail-closed defaults via Sane()).
func SaneLimits ¶
func SaneLimits() ExtractLimits
SaneLimits returns conservative defaults for an app-volume restore.
type FileWriter ¶
type FileWriter interface {
WriteFile(relPath string, mode int64, r io.Reader) error
Mkdir(relPath string) error
}
FileWriter is how SafeExtract emits a confined file (injected so the caller owns the actual filesystem write / staging dir; tests use an in-memory sink).
type LiveState ¶
type LiveState struct {
ProtectedImages map[string]bool // child-proxy / edge / socket-proxy images
ReferencedImages map[string]bool // referenced by any app's DEPLOYED or ROLLBACK compose
InUseImages map[string]bool // images of running containers
ProtectedVolumes map[string]bool // edge/ACME/control-plane volumes
SoleDataVolumes map[string]bool // a volume that is an app's ONLY data store
BackedUpVolumes map[string]bool // verified present in backup_inventory
}
LiveState is the fresh-from-live view the prune denylist resolves against. All image identities are RESOLVED DIGESTS (never tags), so a rollback image can't be pruned just because it's currently dangling/untagged.