Skip to content

feat: add pf port command preparation#252

Merged
munezaclovis merged 10 commits into
mainfrom
feat/pr11-pf-port-commands
Jun 4, 2026
Merged

feat: add pf port command preparation#252
munezaclovis merged 10 commits into
mainfrom
feat/pr11-pf-port-commands

Conversation

@munezaclovis

@munezaclovis munezaclovis commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add distinct persisted Gateway HTTP/HTTPS port assignments
  • render and inspect PV-owned prepared pf anchor and pf.conf reference artifacts
  • add non-privileged pv ports:status, pv ports:install, and pv ports:uninstall

Scope

  • does not write /etc/pf.conf or /etc/pf.anchors/com.prvious.pv
  • does not invoke pfctl
  • does not run sudo
  • leaves privileged installation/removal to PR 13 setup/system-integration work

Tests

  • cargo nextest run --workspace
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
  • cargo shear
  • cargo fmt --all -- --check
  • git diff --check

Summary by CodeRabbit

  • New Features
    • Added non-privileged ports commands: ports:status, ports:install, ports:uninstall; prepares/removes PF redirect artifacts and persists distinct gateway ports for HTTP/HTTPS with fallback selection.
  • Behavior/UX
    • Install prints prepared config paths and conflict guidance; uninstall removes prepared artifacts only and reports PF state.
  • Platform
    • macOS PF inspection and loopback-listener detection added.
  • Tests
    • Integration and unit tests covering port commands, PF inspection, and gateway port assignment.
  • Docs
    • Added implementation plan and design spec for PF port commands.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c6d1cdf3-15c6-4369-afdd-25a79f71cf0b

📥 Commits

Reviewing files that changed from the base of the PR and between 1460830 and 5519d9b.

⛔ Files ignored due to path filters (2)
  • crates/macos/tests/snapshots/resolver_config__pf_loopback_tcp_listener_ports_include_ipv4_wildcard_listener.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_loopback_tcp_listener_ports_include_ipv6_loopback_and_wildcard_listeners.snap is excluded by !**/*.snap
📒 Files selected for processing (1)
  • crates/macos/tests/resolver_config.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/macos/tests/resolver_config.rs

📝 Walkthrough

Walkthrough

Implements non-privileged macOS PF preparation plus three CLI commands (ports:status, ports:install, ports:uninstall), adds gateway HTTP/HTTPS paired port persistence, macOS PF inspection and loopback listener detection, CLI wiring and error propagation, integration tests, and supporting docs.

Changes

pf port command implementation

Layer / File(s) Summary
Gateway port persistence and prepared path contracts
crates/state/src/database.rs, crates/state/src/paths.rs, crates/state/src/lib.rs, crates/state/tests/state_foundation.rs
Adds GatewayPort enum and gateway-typed PortOwner, introduces assign_gateway_ports() to allocate HTTP+HTTPS together, adds PvPaths PF config helpers, PortRequest gateway builders, and tests for preferred reuse, fallback, and rollback.
macOS PF rendering, parsing, and inspection
crates/macos/src/lib.rs, crates/macos/tests/resolver_config.rs, crates/macos/Cargo.toml
Adds netstat dependency, PF constants, PfRedirectConfig/PfConfReference with render/parse, PfFileState classification, inspect_pf_anchor_file/inspect_pf_conf_reference, loopback listener discovery (netstat parsing + socket-table), and snapshot/unit tests.
CLI environment, errors, and command routing
crates/cli/src/environment.rs, crates/cli/src/error.rs, crates/cli/src/lib.rs, crates/cli/src/args.rs, crates/cli/src/commands/mod.rs
Extends Environment with PF path and loopback lookup defaults, adds ExecuteError::Macos, updates finish_execution error handling, registers `ports:status
Ports command implementation and integration tests
crates/cli/src/commands/ports.rs, crates/cli/tests/ports.rs
Implements status, install, and uninstall handlers with conflict detection, DB-backed gateway assignment, prepared file rendering/writing, install/uninstall guidance, optional-file deletion helpers, UTF-8 path handling, and comprehensive integration tests using an isolated TestEnvironment.
Design specification and implementation plan
docs/superpowers/specs/2026-06-03-pr-11-pf-port-commands-design.md, docs/superpowers/plans/2026-06-03-pr-11-pf-port-commands.md, IMPLEMENTATION.md
Adds a full design spec, a stepwise implementation plan spanning state/macOS/CLI verification, tests, and marks PR 11 done in IMPLEMENTATION.md.

Sequence Diagram — ports install (high-level)

sequenceDiagram
  participant CLI as ports::install
  participant Env as Environment
  participant DB as Database
  participant FS as FileSystem
  participant Inspector as inspect_pf_anchor_file
  participant Stdout as stdout

  CLI->>Env: loopback_tcp_listener_ports()
  Env-->>CLI: occupied_ports_set
  CLI->>DB: assign_gateway_ports(is_available)
  DB-->>CLI: GatewayPortAssignments
  CLI->>FS: write prepared anchor & pf.conf
  CLI->>Inspector: inspect prepared and system PF files
  Inspector-->>CLI: PfFileState results
  CLI->>Stdout: print mapping and install guidance
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • prvious/pv#239: Overlapping CLI command registration and dispatcher changes affecting crates/cli/src/args.rs and crates/cli/src/commands/mod.rs.
  • prvious/pv#240: Prior state-layer port-allocation changes that this PR extends with gateway-specific paired assignments.

"I nibble at the config tree,
I hop where ports should be,
Status, install, uninstall too,
Prepared files, tests that woo,
A rabbit cheers this ports debut!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add pf port command preparation' directly and specifically describes the main change: introducing CLI commands for pf port preparation (ports:status, ports:install, ports:uninstall) along with supporting infrastructure for gateway port management and pf artifact handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pr11-pf-port-commands

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
crates/state/tests/state_foundation.rs (1)

26-38: ⚡ Quick win

Use an insta snapshot for this new path test.

This case switches to direct string equality checks, while the surrounding tests snapshot derived path output. Keeping it in the local snapshot style makes the test easier to extend with future path additions.

♻️ Suggested update
 #[test]
 fn pv_paths_include_prepared_pf_artifacts() {
     let paths = PvPaths::for_home("/Users/alice");
 
-    assert_eq!(
-        paths.pf_anchor_config().as_str(),
-        "/Users/alice/.pv/config/pf/com.prvious.pv"
-    );
-    assert_eq!(
-        paths.pf_conf_reference_config().as_str(),
-        "/Users/alice/.pv/config/pf/pf.conf"
-    );
+    assert_debug_snapshot!((
+        paths.pf_anchor_config(),
+        paths.pf_conf_reference_config(),
+    ));
 }
As per coding guidelines, `**/*.rs`: PREFER `insta` snapshots following patterns in nearby tests over substring assertions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/state/tests/state_foundation.rs` around lines 26 - 38, Replace the
direct string equality asserts in the test
pv_paths_include_prepared_pf_artifacts with an insta snapshot assertion: call
PvPaths::for_home("/Users/alice") and produce a stable representation (e.g., a
Vec of the two path strings or Debug display of paths.pf_anchor_config() and
paths.pf_conf_reference_config()) and use insta::assert_snapshot! or
insta::assert_debug_snapshot! to capture it; update the test name if desired,
and ensure the snapshot key is generated by insta so future additions to PvPaths
are captured by the snapshot comparison instead of inline assert_eq checks.
crates/cli/tests/ports.rs (1)

342-349: ⚡ Quick win

Prefer snapshots over the substring helper here.

assert_no_privileged_guidance re-encodes output expectations that the surrounding insta snapshots already cover. Folding this into the snapshots would match the existing Rust test style and remove a second assertion path to maintain.

As per coding guidelines, **/*.rs: PREFER insta snapshots following patterns in nearby tests over substring assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/tests/ports.rs` around lines 342 - 349, The helper function
assert_no_privileged_guidance is redundant with the existing insta snapshots;
remove or stop calling assert_no_privileged_guidance and instead extend the
relevant insta snapshot(s) to include the expectations about not containing
privileged guidance (e.g., ensure the captured output snapshot for the test
covering ports does not include "sudo", "pfctl", "sudo rm", or "sudo install").
Locate usages of assert_no_privileged_guidance in the ports tests and replace
them by asserting/recording the full output with insta (e.g.,
assert_snapshot!/assert_display_snapshot!) so the absence of those substrings is
validated via the snapshot.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/macos/src/lib.rs`:
- Around line 267-281: The current anchor detection (has_anchor_name) is too
loose and matches comments or unrelated text; update it to only consider active,
non-comment pf.conf directives by trimming lines, ignoring lines that start with
'#' and matching exact directive patterns such as anchor "com.prvious.pv" or
load anchor "com.prvious.pv" from "/etc/pf.anchors/com.prvious.pv" (or their
unquoted variants) instead of simple substring contains. Adjust the logic that
computes has_anchor_name (and keep PV_MARKER and the
PfFileState::Conflict/PfFileState::Missing branches) so it only returns Conflict
when a real anchor/load directive is present in a non-comment line.
- Around line 288-303: loopback_tcp_listener_ports currently only requests
netstat::AddressFamilyFlags::IPV4 and filters IpAddr::V4, so IPv6 loopback
listeners (::1 or dual-stack) are missed; update the function to query both IPV4
and IPV6 (or IPV6 only) and include a branch that matches
netstat::ProtocolSocketInfo::Tcp(tcp) where tcp.local_addr is IpAddr::V6(addr)
and addr.is_loopback() to insert tcp.local_port. Also tighten
inspect_pf_conf_reference PfFileState::Conflict detection by replacing fragile
substring checks of PV_MARKER (e.g., line.contains("com.prvious.pv")) with exact
directive parsing—call PfConfReference::parse_block(&content) or perform exact
anchor/directive matching to determine presence of the PV anchor marker so
commented or unrelated lines are not treated as conflicts.

---

Nitpick comments:
In `@crates/cli/tests/ports.rs`:
- Around line 342-349: The helper function assert_no_privileged_guidance is
redundant with the existing insta snapshots; remove or stop calling
assert_no_privileged_guidance and instead extend the relevant insta snapshot(s)
to include the expectations about not containing privileged guidance (e.g.,
ensure the captured output snapshot for the test covering ports does not include
"sudo", "pfctl", "sudo rm", or "sudo install"). Locate usages of
assert_no_privileged_guidance in the ports tests and replace them by
asserting/recording the full output with insta (e.g.,
assert_snapshot!/assert_display_snapshot!) so the absence of those substrings is
validated via the snapshot.

In `@crates/state/tests/state_foundation.rs`:
- Around line 26-38: Replace the direct string equality asserts in the test
pv_paths_include_prepared_pf_artifacts with an insta snapshot assertion: call
PvPaths::for_home("/Users/alice") and produce a stable representation (e.g., a
Vec of the two path strings or Debug display of paths.pf_anchor_config() and
paths.pf_conf_reference_config()) and use insta::assert_snapshot! or
insta::assert_debug_snapshot! to capture it; update the test name if desired,
and ensure the snapshot key is generated by insta so future additions to PvPaths
are captured by the snapshot comparison instead of inline assert_eq checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 41a884ed-cc95-4913-8e23-09855ac2169d

📥 Commits

Reviewing files that changed from the base of the PR and between b8475ab and ca1279d.

⛔ Files ignored due to path filters (13)
  • Cargo.lock is excluded by !**/*.lock
  • crates/cli/tests/snapshots/ports__ports_install_fails_on_low_port_conflict_before_writing_prepared_artifacts.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/ports__ports_install_prepares_pf_artifacts_without_touching_system_paths.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/ports__ports_status_reports_prepared_and_system_pf_states_without_mutating_state.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/ports__ports_uninstall_removes_prepared_artifacts_and_defers_system_removal.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_anchor_inspection_reports_missing_current_stale_conflict_and_unreadable.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_conf_reference_inspection_reports_missing_current_stale_conflict_and_unreadable.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_config_renders_pv_owned_anchor_and_pf_conf_reference.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__gateway_port_allocator_persists_distinct_http_and_https_assignments.snap is excluded by !**/*.snap
  • it/snapshots/cli__completions_generate_bash_script.snap is excluded by !**/*.snap
  • it/snapshots/cli__completions_generate_fish_script.snap is excluded by !**/*.snap
  • it/snapshots/cli__completions_generate_zsh_script.snap is excluded by !**/*.snap
  • it/snapshots/cli__daemon_run_is_hidden_from_top_level_help.snap is excluded by !**/*.snap
📒 Files selected for processing (18)
  • Cargo.toml
  • IMPLEMENTATION.md
  • crates/cli/src/args.rs
  • crates/cli/src/commands/mod.rs
  • crates/cli/src/commands/ports.rs
  • crates/cli/src/environment.rs
  • crates/cli/src/error.rs
  • crates/cli/src/lib.rs
  • crates/cli/tests/ports.rs
  • crates/macos/Cargo.toml
  • crates/macos/src/lib.rs
  • crates/macos/tests/resolver_config.rs
  • crates/state/src/database.rs
  • crates/state/src/lib.rs
  • crates/state/src/paths.rs
  • crates/state/tests/state_foundation.rs
  • docs/superpowers/plans/2026-06-03-pr-11-pf-port-commands.md
  • docs/superpowers/specs/2026-06-03-pr-11-pf-port-commands-design.md

Comment thread crates/macos/src/lib.rs
Comment thread crates/macos/src/lib.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/macos/tests/resolver_config.rs (1)

196-228: 💤 Low value

Consider snapshot pattern consistency.

These tests verify runtime listener detection using assert! with membership checks, while all other tests in this file use assert_debug_snapshot!. The deviation appears justified because the ephemeral port assignment makes traditional snapshots impractical. However, given the file's established pattern, consider whether there's value in supplementing these runtime tests with snapshot-based tests for the underlying parsing logic (e.g., testing parse_netstat_tcp_listener_ports directly with fixed inputs).

The current approach is sound for integration-level verification, but a hybrid approach might better align with the file's snapshot convention while preserving runtime coverage.

Based on learnings: PREFER insta snapshots following patterns in nearby tests over substring assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/macos/tests/resolver_config.rs` around lines 196 - 228, The tests use
runtime membership asserts for loopback_tcp_listener_ports() but the file
prefers insta snapshots; add supplemental snapshot tests that exercise the
underlying parsing logic (e.g., parse_netstat_tcp_listener_ports) with
deterministic, fixed netstat output fixtures and assert_debug_snapshot! on the
parsed port set, while keeping the existing integration-style runtime tests
(loopback_tcp_listener_ports and its IPv6 variant) for ephemeral-port coverage;
locate parse_netstat_tcp_listener_ports and write one or more unit tests that
feed fixed input strings and call assert_debug_snapshot! on the resulting
Vec/HashSet so the parsing behavior is snapshot-covered and consistent with
nearby tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/macos/tests/resolver_config.rs`:
- Around line 196-228: The tests use runtime membership asserts for
loopback_tcp_listener_ports() but the file prefers insta snapshots; add
supplemental snapshot tests that exercise the underlying parsing logic (e.g.,
parse_netstat_tcp_listener_ports) with deterministic, fixed netstat output
fixtures and assert_debug_snapshot! on the parsed port set, while keeping the
existing integration-style runtime tests (loopback_tcp_listener_ports and its
IPv6 variant) for ephemeral-port coverage; locate
parse_netstat_tcp_listener_ports and write one or more unit tests that feed
fixed input strings and call assert_debug_snapshot! on the resulting Vec/HashSet
so the parsing behavior is snapshot-covered and consistent with nearby tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bfb69be1-e319-47e4-8d99-a17c18c40c7b

📥 Commits

Reviewing files that changed from the base of the PR and between ca1279d and d3ed1e5.

⛔ Files ignored due to path filters (3)
  • crates/cli/tests/snapshots/ports__ports_install_uses_fallback_gateway_ports_when_preferred_ports_are_busy.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_anchor_inspection_reports_missing_current_stale_conflict_and_unreadable.snap is excluded by !**/*.snap
  • crates/macos/tests/snapshots/resolver_config__pf_conf_reference_inspection_reports_missing_current_stale_conflict_and_unreadable.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • crates/cli/tests/ports.rs
  • crates/macos/src/lib.rs
  • crates/macos/tests/resolver_config.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/macos/src/lib.rs
  • crates/cli/tests/ports.rs

@munezaclovis
munezaclovis merged commit cacf3c6 into main Jun 4, 2026
2 checks passed
@munezaclovis
munezaclovis deleted the feat/pr11-pf-port-commands branch June 4, 2026 00:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant