Repository navigation
Manage, not view: persistence (Save), rule edit and move - #16
Merged
Merged
Conversation
The nftables backend could create rules, NAT and aliases but everything it did was runtime-only and immutable: a reboot reverted the ruleset, and the only way to change a rule was delete-and-re-add. This closes those gaps. Save (W): the tool's own table is captured with `nft list table inet tui` and installed with `install -m 644 /dev/stdin <path>` — the listing rides on stdin, never on a command line. The path is /etc/omarchy/router/tui-firewall.nft on an Omarchy router (the profile includes it on boot), /etc/nftables.d/tui-firewall.nft elsewhere, and TUI_FIREWALL_SAVE_PATH overrides it for tests. The confirm dialog shows a unified diff against the file as it stands. install(1) gets a runner of its own inside the backend, so the exec boundary stays where it was. Edit (E): the add-rule form opens pre-filled from the rule's modelled match and submits as `nft replace rule … handle H`, the same replace-by-handle rebuild the log toggle uses, with the same refusals — a rule shown only as text, a NAT rule, a source-port match or a log level the form cannot round-trip is refused before the form opens. Move (K/J): a copy written at the new position plus a delete of the old handle, always one `nft -f` transaction — staged into the atomic batch when staging is on, wrapped by the new staging.AtomicCommand when it is off — so the rule is never duplicated or missing in between. Per-rule enable/disable is deliberately left out; DESIGN.md records the persisted-spec format it waits for. The demo applies every new command in memory, so --demo behaves exactly like the real backend, and the new builders carry injection-guard and refusal tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCypLQF5AD8JD831Ea9S9q
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCypLQF5AD8JD831Ea9S9q
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The nftables backend built rules, NAT and aliases but everything was runtime-only and immutable: a reboot reverted the ruleset to whatever the router profile rendered, and the only way to change a rule was delete-and-re-add. This PR closes the three gaps.
Save (
W) — persistencenft list table inet tuiand installs it withinstall -m 644 /dev/stdin <path>. The listing rides on stdin, never on a command line, and nft's own text is a valid nft script, sonft -floads the file straight back on boot./etc/omarchy/router/tui-firewall.nfton an Omarchy router (the profile includes it on boot — the profile hook lands separately),/etc/nftables.d/tui-firewall.nfton any other host,TUI_FIREWALL_SAVE_PATHoverride for tests and labs.UnifiedDiffhelper); a file that already matches is reported without opening a dialog.install(1)gets a lazily-built runner of its own insideinternal/nftables, dispatched per command, so the exec boundary is unchanged (check-exec.shclean).Edit in place (
E)Match(newSpecForRule) and submits asnft replace rule … handle H, reusing the log toggle's rebuild machinery — same handle, same position.BuildToggleLog, made shared incheckRebuildable: a rule with an unmodeled match, a NAT rule, a non-accept/drop/reject verdict, plus edit-specific refusals for source-port matches, log levels / nflog groups, empty-verdict rules and reject-with answers the form cannot round-trip. Refused before the form opens, with the reason.Move (
K/J)nft -ftransaction: staged into the atomic batch when staging is on, wrapped by the newstaging.AtomicCommandwhen it is off. The confirm shows the whole transaction script.Deliberately left out
Per-rule enable/disable needs a persisted-spec format (v2);
DESIGN.mdrecords the approach instead of shipping a fake disable that drops rules.Evidence
go build,go vet,gofmt,make check-exec: clean.go test ./...: all green. New coverage: exact-argv builder tests, injection guards on every edited operand, refusal paths (unmodeled/NAT/sport/log-args/first/last/position-on-edit), save path guards and env override, diff rendering, demo parity (fake applies edit, atomic move and pretends the install;--demonever touches disk), and UI tests for theW/E/K/Jflows including staged-move and non-nftables refusals.🤖 Generated with Claude Code
https://claude.ai/code/session_01MCypLQF5AD8JD831Ea9S9q