Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c7369ef
fixed some local variable definitions, empty list, and RECV_DEFAULT
Jan 14, 2026
64bf0fd
Feature/zelta test (#59)
rlogwood Jan 15, 2026
f49d563
Merge branch 'dev' of github.com:bellhyve/zelta into dev
Jan 15, 2026
2f603f1
update the README TOC
rlogwood Jan 15, 2026
b6365a5
shellspec options experiment
Jan 15, 2026
3bddf6d
feat: support multiple operand endpoints and fix top-level dataset re…
Jan 15, 2026
5d942b6
generalize backup validation test
rlogwood Jan 15, 2026
0ac745e
fix typo in backup verification
rlogwood Jan 15, 2026
f12fa71
run snapshot count on the associated server
rlogwood Jan 15, 2026
43925fc
drop some unused lines, clean explain_sync_status, better diverged me…
Jan 16, 2026
4cd49a2
doc bugs, increment version to rc1
Jan 16, 2026
fc3ecd4
docs: tighten prose and fix errors in STYLE.md
Jan 16, 2026
f94000d
test/sanity checks experiments
Jan 18, 2026
5e43196
shellspec experiments
Jan 19, 2026
6b4d5f5
docs cleanup
Jan 19, 2026
d5123e0
add testing examples
Jan 20, 2026
e9d52f3
fix local execution
Jan 21, 2026
69989a7
fix local execution
Jan 21, 2026
64964b9
fix: add missing field mappings to zelta-json.tsv for complete JSON o…
Jan 21, 2026
dc67d49
fix: add error message collection and JSON output for replication errors
Jan 21, 2026
cf0aa30
fix: properly close JSON object in stop function to fix broken output
Jan 21, 2026
bcfa950
json and output improvements
Jan 21, 2026
cc8d575
test: add installation tests and helpers
Jan 23, 2026
8c6b17e
feat: add phase parameter to uninstall.sh for selective removal
Jan 23, 2026
c2ac79b
make uninstaller require explicit 'phase' option
Jan 23, 2026
1117307
make uninstaller require explicit 'phase' option
Jan 23, 2026
e207a52
spec test improvements
Jan 23, 2026
aecb42a
spec test improvements
Jan 23, 2026
ae64432
set dryrun snapshot message to 'would snapshot'
Jan 23, 2026
a206d3e
fix dev conflict for no apparent reason
Jan 23, 2026
8f990a5
extra else in should_snapshot
Jan 23, 2026
7777a0c
fix local spec test run
Jan 23, 2026
24289a6
fix zfs get depth command order
Jan 23, 2026
5bb8f1f
style: fix typos and grammar in documentation and code
Jan 23, 2026
8a065bc
regex typo
Jan 23, 2026
a0baef5
fix spec test output
Jan 23, 2026
d6e13ed
experiment
Jan 28, 2026
aa75e81
fix zelta prune usage -x should be -X
Jan 28, 2026
fdbb61a
drop packaging (to be moved to multi-os package repo)
Feb 11, 2026
93c3708
feat: add one-shot installer script and documentation
Feb 11, 2026
0d35501
feat: Add timestamp preservation to prevent unnecessary reinstallation
Feb 11, 2026
26c5c98
feat: add non-root user install support with PATH warning
Feb 11, 2026
a2c1d0c
fix: wrap user-install prompt with TTY check and remove duplicate PAT…
Feb 11, 2026
3a8686b
feat: add auto-detection and guidance for non-root installations
Feb 11, 2026
be87d82
Feature/test refactor (#61)
rlogwood Feb 12, 2026
ca1bb12
Merge branch 'dev' of github.com:bellhyve/zelta into dev
Feb 12, 2026
ebf64b4
Merge remote-tracking branch 'origin/main' into dev
Feb 15, 2026
6fc00fc
reamde and changelog tweak
Mar 6, 2026
fa49aab
doc: clearer note about the one-shot installer
Mar 6, 2026
6273f21
Add new ShellSpec tests, CI workflow, test generation improvements
rlogwood Mar 16, 2026
d75a6f9
Multiple docs updated: fixed `--quiet` description across commands
Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/shellspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: ShellSpec Tests

on:
push:
branches: [main, dev, "feature/**"]
pull_request:
branches: [main, dev]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y zfsutils-linux man-db

- name: Install ShellSpec
run: curl -fsSL https://git.io/shellspec | sudo sh -s -- --yes --prefix /usr/local

- name: Create test user
run: |
sudo useradd -m -s /bin/bash testuser
echo 'testuser ALL=(ALL) NOPASSWD: /usr/bin/dd *, /usr/bin/rm -f /tmp/*, /usr/bin/truncate *, /usr/sbin/zpool *, /usr/sbin/zfs *' \
| sudo tee /etc/sudoers.d/testuser
sudo chown root:root /etc/sudoers.d/testuser
sudo chmod 0440 /etc/sudoers.d/testuser
sudo visudo -cf /etc/sudoers.d/testuser

- name: Copy repo for test user
run: |
sudo cp -r "$GITHUB_WORKSPACE" /home/testuser/zelta
sudo chown -R testuser:testuser /home/testuser/zelta

- name: Run ShellSpec tests
run: |
sudo -u testuser env \
SANDBOX_ZELTA_SRC_POOL=apool \
SANDBOX_ZELTA_TGT_POOL=bpool \
SANDBOX_ZELTA_SRC_DS=apool/treetop \
SANDBOX_ZELTA_TGT_DS=bpool/backups \
bash -c 'cd /home/testuser/zelta && shellspec'
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.*
!.gitignore
!.shellspec
!.github/
test/runners/test_generation/config/zelta_test_policy.conf
*.swp
*.swo
.*
doc/man?
tmp
hide.*
Expand All @@ -14,3 +16,4 @@ A*d
retired/
logs
log
ci/*
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ All notable changes to Zelta will be documented in this file.
- **Commands**: `zelta revert` for in-place rollbacks via rename and clone.
- **Commands**: `zelta rotate` for divergent version handling, evolved from original `--rotate` flag.
- **Commands**: (Experimental) `zelta prune` identifies snapshots in `zfs destroy` range syntax based on replication state and a sliding window for exclusions.
- **Installer**: (Experimental) Added a one-liner/pipe to shell installer option.
- **Uninstaller**: Added `uninstall.sh` for clean removal of Zelta installations, including legacy paths from earlier betas.
- **Core**: `zelta-args.awk` added as a separate data-driven argument preprocessor.
- **Core**: `zelta-common.awk` library for centralized string and logging functions.
- **Config**: Data-driven TSV configuration (`zelta-cmds.tsv`, `zelta-cols.tsv`, `zelta-json.tsv`, `zelta-opts.tsv`).
- **Docs**: `zelta.env` expanded with comprehensive inline documentation and examples for all major configuration categories.
- **Docs**: New man pages: `zelta-options(7)`, `zelta-revert(8)`, `zelta-rotate(8)`, `zelta-prune(8)`.
- **Docs**: Added tool to sync man pages with the zelta.space wiki.
- **Testing**: Added new advanced Shellspec-based testing suite.

### Changed
- **Architecture**: Refactored all core scripts for maintainability and simpler logic.
Expand Down
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
![Zelta Logo](https://zelta.space/index/zelta-banner.svg)

# The Zelta Backup and Recovery Suite
*Version 1.1, 2026-01-20*

---

> - **What's New:** Check [CHANGELOG.md](CHANGELOG.md) for the latest changes
> - **Found a Bug?** Please [open an issue](https://github.com/bellhyve/zelta/issues)
> - **Previous Release:** [March 2024, Zelta v1.0](https://github.com/bellhyve/zelta/tree/release/1.0)

> - **Found a Bug?** Please [open an issue](https://github.com/bell-tower/zelta/issues)
> - **Previous Release:** [March 2024, Zelta v1.0](https://github.com/bell-tower/zelta/tree/release/1.0)
>
>      ![ShellSpec Tests](https://github.com/bell-tower/zelta/actions/workflows/shellspec.yml/badge.svg)
---

[zelta.space](https://zelta.space) | [Documentation](https://zelta.space/en/home) | [GitHub](https://github.com/bellhyve/zelta)
[zelta.space](https://zelta.space) | [Documentation](https://zelta.space/en/home) | [GitHub](https://github.com/bell-tower/zelta)

**Zelta** provides bulletproof backups that meet strict compliance requirements while remaining straightforward to deploy and operate. It transforms complex backup and recovery operations into safe, auditable commands—protecting your data without requiring specialized expertise.

Expand Down Expand Up @@ -41,7 +40,7 @@ Written in portable Bourne shell and AWK, Zelta runs anywhere ZFS runs. No packa

### From Source (Recommended for Zelta 1.1)
```sh
git clone https://github.com/bellhyve/zelta.git
git clone https://github.com/bell-tower/zelta.git
cd zelta
sudo ./install.sh
# The installer will guide you through setup.
Expand All @@ -55,11 +54,16 @@ pkg install zelta
```

### Experimental: One-Shot Install

The following command clones Zelta from the `main` branch and launches the installer. Run this as a personal or backup user for a local, non-root installation. If you prefer a system-wide installation, run the command as root or via `sudo/doas`.

```sh
curl -fsSL https://raw.githubusercontent.com/bellhyve/zelta/main/contrib/install-from-git.sh | sudo sh
curl -fsSL https://raw.githubusercontent.com/bell-tower/zelta/main/contrib/install-from-git.sh | sh
```

**Note:** This is experimental. For production use, we recommend cloning the repository and reviewing `install.sh` before running it.
The installer will detect your privileges and guide you through adding the necessary environment variables to your shell profile.

*Security Note: As with any script piped from the internet, we encourage you to [inspect the installer source](https://github.com/bell-tower/zelta/blob/main/contrib/install-from-git.sh) before execution.*

---

Expand Down Expand Up @@ -182,17 +186,17 @@ We welcome contributors who are passionate about data protection and recovery. B

### Contact

We welcome questions, bug reports, and feature requests at [GitHub Issues](https://github.com/bellhyve/zelta/issues).
We welcome questions, bug reports, and feature requests at [GitHub Issues](https://github.com/bell-tower/zelta/issues).

For other inquiries including business questions, you can reach the Zelta team at Bell Tower via our [contact form](https://belltower.it/contact/).

### Conference Talks

**BSDCan 2024: Zelta: A Safe and Powerful Approach to ZFS Replication**
By Daniel J. Bell
**BSDCan 2024: Zelta: A Safe and Powerful Approach to ZFS Replication**
By Daniel J. Bell
[Watch on YouTube](https://www.youtube.com/watch?v=_nmgQTs8wgE)

**OpenZFS Summit 2025: Responsible Replication with Zelta**
**OpenZFS Summit 2025: Responsible Replication with Zelta**
[Watch on YouTube](https://www.youtube.com/watch?v=G3weooQqcXw)

### Bell Tower Services
Expand Down
2 changes: 1 addition & 1 deletion contrib/install-from-git.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Zelta One-Shot Installer
# Downloads latest Zelta from GitHub and runs install.sh
#
# Usage: curl -fsSL https://raw.githubusercontent.com/bellhyve/zelta/main/contrib/install-from-git.sh | sh
# Usage: curl -fsSL https://raw.githubusercontent.com/bell-tower/zelta/main/contrib/install-from-git.sh | sh
# Or specify branch: curl ... | sh -s -- --branch=develop

set -e
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-backup.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ _target_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-j, \--json**
: Output results in JSON format. See **zelta-options(7)** for details.
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ _target_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-n, \--dryrun, \--dry-run**
: Display `zfs` commands without executing them.
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-match.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
: Increase verbosity. Specify once for operational detail and twice (-vv) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings and twice (-qq) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (-qq) to suppress warnings.

**\--log-level**
: Specify a log level value 0-4: errors (0), warnings (1), notices (2, default), info (3, verbose), and debug (4).
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-prune.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ _target_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-n, \--dryrun, \--dry-run**
: Display `zfs` commands without executing them.
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-revert.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ _endpoint_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-n, \--dryrun, \--dry-run**
: Display `zfs` commands without executing them.
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-rotate.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ _target_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-n, \--dryrun, \--dry-run**
: Display `zfs` commands without executing them.
Expand Down
2 changes: 1 addition & 1 deletion doc/zelta-snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ _endpoint_
: Increase verbosity. Specify once for operational detail, twice (`-vv`) for debug output.

**-q, \--quiet**
: Quiet output. Specify once to suppress warnings, twice (`-qq`) to suppress errors.
: Decrease log level. Specify once to suppress notices, twice (`-qq`) to suppress warnings.

**-n, \--dryrun, \--dry-run**
: Display `zfs` commands without executing them.
Expand Down
26 changes: 15 additions & 11 deletions test/040_zelta_tests_spec.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Auto-generated ShellSpec test file
# Generated at: 2026-02-12 13:28:34 -0500
# Generated at: 2026-03-15 02:59:07 -0400
# Source: 040_zelta_tests_spec
# WARNING: This file was automatically generated. Manual edits may be lost.

output_for_match_after_divergence() {
while IFS= read -r line; do
# normalize whitespace, remove leading/trailing spaces
normalized=$(echo "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
normalized=$(printf '%s' "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
case "$normalized" in
"DS_SUFFIX MATCH SRC_LAST TGT_LAST INFO"|\
"[treetop] @start @start @start up-to-date"|\
Expand All @@ -24,7 +24,8 @@ output_for_match_after_divergence() {
"11 total datasets compared")
;;
*)
printf "Unexpected line format: %s\n" "$line" >&2
printf "Unexpected line format : %s\n" "$line" >&2
printf "Comparing to normalized: %s\n" "$normalized" >&2
return 1
;;
esac
Expand All @@ -35,16 +36,17 @@ output_for_match_after_divergence() {
output_for_rotate_after_divergence() {
while IFS= read -r line; do
# normalize whitespace, remove leading/trailing spaces
normalized=$(echo "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
normalized=$(printf '%s' "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
case "$normalized" in
"source is written; snapshotting: @zelta_"*""|\
"renaming '${SANDBOX_ZELTA_TGT_DS}' to '${SANDBOX_ZELTA_TGT_DS}_start'"|\
"to ensure target is up-to-date, run: zelta backup ${SANDBOX_ZELTA_SRC_EP} ${SANDBOX_ZELTA_TGT_EP}"|\
"no source: ${SANDBOX_ZELTA_TGT_DS}/sub1/kid"|\
"* sent, 10 streams received in * seconds")
""*" sent, 10 streams received in "*" seconds")
;;
*)
printf "Unexpected line format: %s\n" "$line" >&2
printf "Unexpected line format : %s\n" "$line" >&2
printf "Comparing to normalized: %s\n" "$normalized" >&2
return 1
;;
esac
Expand All @@ -55,7 +57,7 @@ output_for_rotate_after_divergence() {
output_for_match_after_rotate() {
while IFS= read -r line; do
# normalize whitespace, remove leading/trailing spaces
normalized=$(echo "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
normalized=$(printf '%s' "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
case "$normalized" in
"DS_SUFFIX MATCH SRC_LAST TGT_LAST INFO"|\
"[treetop] @zelta_"*" @zelta_"*" @zelta_"*" up-to-date"|\
Expand All @@ -72,7 +74,8 @@ output_for_match_after_rotate() {
"10 total datasets compared")
;;
*)
printf "Unexpected line format: %s\n" "$line" >&2
printf "Unexpected line format : %s\n" "$line" >&2
printf "Comparing to normalized: %s\n" "$normalized" >&2
return 1
;;
esac
Expand All @@ -83,14 +86,15 @@ output_for_match_after_rotate() {
output_for_backup_after_rotate() {
while IFS= read -r line; do
# normalize whitespace, remove leading/trailing spaces
normalized=$(echo "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
normalized=$(printf '%s' "$line" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
case "$normalized" in
"syncing 10 datasets"|\
"10 datasets up-to-date"|\
"* sent, 3 streams received in * seconds")
""*" sent, 3 streams received in "*" seconds")
;;
*)
printf "Unexpected line format: %s\n" "$line" >&2
printf "Unexpected line format : %s\n" "$line" >&2
printf "Comparing to normalized: %s\n" "$normalized" >&2
return 1
;;
esac
Expand Down
Loading
Loading