Skip to content

Releases: night4me/pfsense-mcp-server

v1.1.0

Choose a tag to compare

@night4me night4me released this 30 Aug 08:56

Defense-in-depth and onboarding release. Public MCP tool contract is
unchanged from 1.0.0 (95 pfSense READ tools + 2 documentation guidance
tools, 0 default-reachable WRITE = 97 total) — this release is not a
capability expansion.

The headline addition is managed READ-only credential provisioning: a
dedicated, project-provisioned pfsense-mcp-readonly pfSense service
account holding exactly the 94 READ privileges this project documents —
live-LAB-verified to receive HTTP 403 AUTH_AUTHORIZATION_FAILED when used
directly against this project's own reviewed WRITE endpoint, i.e. rejected
by pfSense itself, not only by this server's tool surface. This is the
recommended path for new read_only setups via the setup wizard;
bring-your-own-key remains fully supported, unchanged by default, for
existing installations.

Added

  • Managed READ-only service-account provisioning — a fixed, dedicated
    pfsense-mcp-readonly identity, entirely separate namespace/journal/
    lock/custody state from the write_protected pfsense-mcp account.
    Reachable via pfsense-mcp-security bootstrap --target-profile read_only and pfsense-mcp-security recover --target-profile read_only.
  • Setup-wizard integration for managed READ-only provisioning. A new
    "Account" wizard step (read_only postures only) offers a dedicated
    least-privilege account [Recommended] alongside the existing
    bring-your-own-key option. The managed-vs-BYOK choice is security-bound
    into the setup plan, its digest, and the apply-time confirmation token —
    a confirmed BYOK plan can never be substituted for a managed-provisioning
    apply, or vice versa.
  • --target-profile {write_protected,read_only} flag on
    bootstrap/recover — explicit, additive; defaults to
    write_protected (unchanged behavior for every existing invocation).

Fixed

  • Recovery cross-profile isolation. Inline recovery inspection now
    threads the correct target profile explicitly from the caller that
    determined which bootstrap composition failed, instead of always
    inspecting the write_protected account's journal.
  • Operation-journal account/profile allowlist is now a closed,
    hardcoded pair set — both fields must still match together; no other
    account/profile combination can ever create a journal record.
  • docs/PFSENSE_LEAST_PRIVILEGE_MATRIX.md staleness (understated the
    verified pfSense-pkg-RESTAPI version range).

Changed

  • Verified pfSense-pkg-RESTAPI version range extended to v2.10.2
    (from v2.10.0), re-verified directly against pinned source at both
    v2.10.1 and v2.10.2.
  • MCP tool-surface architecture reviewed and retained. An empirical
    benchmark found no measurable selection-accuracy problem with the
    current 97-tool explicit surface. Two non-generic alternatives were
    designed and benchmarked but not adopted — no production architecture
    change was made.

Full detail: CHANGELOG.md.

v1.0.0

Choose a tag to compare

@night4me night4me released this 29 Aug 12:23

First stable release. Public MCP tool contract is unchanged from 0.9.0
(95 pfSense READ tools + 2 documentation guidance tools, 0
default-reachable WRITE = 97 total) — this release is a product-maturity
and correctness pass, not a capability expansion. See
docs/STABILITY.md
for the version-independent stability promise now made across the
MCP/CLI/config/persisted-state surfaces.

Fixed

  • serverInfo.version reported the installed mcp SDK's own version
    instead of this package's version
    in every MCP initialize
    response — FastMCP never forwards a version= to the low-level
    mcp.server.lowlevel.Server it constructs internally, so an unset
    .version fell back to the mcp package's own importlib.metadata
    version. Now set explicitly from one shared resolver.
  • setup write-client-config failed on a genuinely clean $HOME
    with no pre-existing ~/.codex directory, despite a valid
    confirmation token — a missing parent directory is now created
    automatically and safely (symlink-safe, ownership-checked,
    owner-only permissions).
  • Setup wizard guidance text lost whitespace at wrap boundaries on
    narrow terminals — affected paragraphs rebuilt as continuous strings
    wrapped fresh at render time.
  • TLS certificate verification failures were reported with one
    generic message
    , concealing whether the failure was an untrusted
    certificate authority or a certificate valid for a different
    hostname/IP than the one connected to. Now distinguishes CA-trust
    failure, hostname/IP identity mismatch, and expired/not-yet-valid
    certificates using structured ssl.SSLCertVerificationError
    evidence — never string-guessing, never suggesting an insecure
    bypass.
  • A successful read_only setup apply ended with an unexplained
    Doctor ready: False
    doctor's checks are exclusively about the
    optional hardware-witness ceremony, but were run and reported
    unconditionally regardless of the selected anchor. Now posture-aware.

Changed

  • SECURITY.md's "Supported versions" section, frozen at the
    v0.2.x/v0.3.0 era, replaced with a version-independent policy.
  • Corrected two client-integration-guide claims found stale by live
    verification against current vendor documentation.
  • Development Status classifier updated from 4 - Beta to
    5 - Production/Stable.

Full detail: CHANGELOG.md.

v0.9.0

Choose a tag to compare

@night4me night4me released this 28 Aug 16:38

Adds a second, structurally distinct MCP guidance tool covering the community-maintained pfREST package -- public MCP contract: 95 pfSense READ tools + 2 documentation guidance tools (was 1), 0 default-reachable WRITE = 97 total (was 96).

Added

  • pfsense_get_api_guidance -- a new guidance tool covering pfSense-pkg-RESTAPI (documented at pfrest.org), never Netgate product documentation, never blended with the existing pfsense_get_official_guidance. Four bounded query modes -- tool, endpoint, model, topic -- returning cross-source evidence explicitly labeled by provenance: PROJECT_AUTHORED (this project's own tool interpretation), PFREST_UPSTREAM (pfrest.org's live documentation, fetched through a narrow, allowlisted, HTTPS-only, GET-only fetcher with a hard response-size cap and a bounded cache), and LIVE_APPLIANCE_SCHEMA (your connected appliance's own /api/v2/schema/openapi, fetched through the same authenticated transport every READ tool already uses). When sources disagree, the disagreement is stated explicitly -- never silently merged, never silently dropped.
  • Project-authored tool guidance (pfsense_mcp.guidance.tool_guidance) is now wired into pfsense_get_api_guidance's tool query mode.
  • make pfrest-privilege-crosscheck -- an offline, advisory, maintainer-facing script comparing what pfrest.org and (if configured) your connected appliance each declare as a READ tool's required pfSense privilege. Not part of the MCP surface; never grants a privilege or modifies configuration.
  • make pfrest-schema-diff -- an offline, advisory, maintainer-facing semantic (not byte-level) OpenAPI comparison across twelve dimensions (paths/methods, operationIds, parameters, schemas, fields, enums, default values, required packages, auth metadata, allowed privileges, applies-immediately metadata, extensions, version metadata), classifying differences without assuming a cause. Not part of the MCP surface.

Safety

Documentation and guidance content -- from any of the four provenance sources -- is evidence, never authorization. It cannot select, influence, or execute any action this server takes; it cannot reach a WRITE capability; and no source can masquerade as another. This is enforced structurally (isolation tests, no authorization-shaped output fields) and verified adversarially, including live against a real appliance, before this release.

Not included in this release: a persistent pfREST documentation cache, conditional-GET revalidation, a model-name discovery tool, or a production CE-vs-Plus schema comparison (the tooling to run one exists offline, but no comparison was performed against production).

Full detail: CHANGELOG.md, docs/adr/ADR-035-pfrest-live-guidance-layer.md, and docs/ACCEPTANCE_v0.9.0.md.

v0.8.0

Choose a tag to compare

@night4me night4me released this 27 Aug 00:38

CLI-only expansion of the pfsense-mcp-security operator tooling (ADR-021/ADR-033). No MCP capability change: the public MCP tool contract remains byte-identical to v0.7.2 -- still 95 pfSense READ tools + 1 official-guidance tool, 0 default-reachable WRITE. None of this release's new functionality is reachable from, or wired into, pfsense_mcp.server/the MCP tool registry/normal application startup; it is exclusively the separate pfsense-mcp-security CLI entry point.

Added

  • pfsense-mcp-security setup -- a guided, non-mutating discovery + plan-only wizard (interactive by default, or --non-interactive for deterministic automation), plus setup apply, a wholly separate, explicit command that can apply a generated plan: read_only performs one read-only connectivity check; write_protected composes the existing bootstrap orchestration to provision the one fixed ADR-033 service account. Includes inline RECOVERY_REQUIRED delegation, PFSENSE_SETUP_APPLY_CONFIRM_TOKEN for CI-friendly non-interactive confirmation, and setup write-client-config (merge-only MCP client config writing, gated behind its own explicit --confirm).
  • pfsense-mcp-security recover -- standalone ADR-033 recovery-execution orchestration. Read-only inspection by default; execution requires both --execute <ACTION> and the exact incident-bound confirmation token.

Fixed

  • bootstrap (and setup apply --capability-posture write_protected) restart classification -- a prior operation journal is no longer unconditionally treated as requiring recovery attention on every subsequent run. One fresh, read-only, GET-only live observation is now attempted first; only an exact match against every expected binding field resolves to a clean, already-complete restart. A strictly fail-closed correctness improvement, not a relaxation.

Full detail: CHANGELOG.md and docs/ACCEPTANCE_v0.8.0.md.

v0.7.2

Choose a tag to compare

@night4me night4me released this 23 Aug 13:23

Tier 1 correctness fix and validation-pipeline improvement. No MCP
capability change.
Public contract byte-identical to v0.7.1: 95
pfSense READ tools + 1 official-guidance tool, 0 default-reachable
WRITE
, confirmed by a tests/contracts/mcp_public_contract_v0.7.2.json
snapshot that diffs to zero against v0.7.1's own snapshot.

Fixed

  • MutationExecutor real-wall-clock expiry checkexecute()
    called RecoveryContract.is_expired() with no explicit now=
    argument, so it silently fell through to real
    datetime.now(timezone.utc) regardless of any deterministic clock
    the surrounding test/store already used. Under a long-running test
    suite this occasionally caused a 4-minute contract TTL to expire
    before the two real-executor tests in
    test_alias_description_execution.py reached it, producing an
    intermittent ContractConflictError unrelated to any real
    authorization problem. Fixed with the same constructor-injectable
    Clock seam already used by SqliteRecoveryContractStore and
    SqliteAuthorizationConsumptionStoreMutationExecutor now
    defaults to real UTC wall-clock time exactly as before (the only
    production construction site, production_runtime.py, does not
    inject a clock) and fails closed on a naive or non-UTC value. No
    expiry semantics, TTL values, authorization logic, or state-machine
    transitions changed.
  • README.md's Mermaid diagrams rendering as raw source text on
    PyPI
    — GitHub renders fenced ```mermaid blocks natively;
    PyPI's readme_renderer does not, so the live PyPI project page
    showed the raw diagram source as a code block. Both diagrams
    (READ trust path, WRITE authorization path) replaced with
    pre-rendered SVG images (assets/diagrams/*.svg), referenced by an absolute
    raw.githubusercontent.com URL since PyPI has no file tree for a
    relative path to resolve against. The Mermaid source remains the
    maintained source of truth in assets/diagrams/*.mmd. See
    docs/adr/ADR-034-mermaid-pypi-compatibility.md.
  • GitHub Pages documentation staleness — the published docs site
    was last deployed from a commit 92 commits behind main (still
    describing a 42-tool, v0.3.x-era state). Redeployed from current
    main. Added a read-only staleness detector
    (scripts/docs_pages_freshness_check.py, weekly + on-docs-push CI
    check) that cannot itself deploy anything — automatic deployment on
    every docs change remains a separate, explicit owner decision.

Added

  • pfsense-mcp-security bootstrap CLI subcommand (ADR-033 CLI
    Integration Slice 3) — a journal-aware, locking bootstrap-
    orchestration layer composing the already-implemented ADR-033
    security-bootstrap primitives into one operator-facing command. This
    is a CLI-only administrative tool, invoked outside the MCP server
    process; it registers no MCP tool, adds nothing to the public MCP
    contract, and does not change READ or WRITE reachability.

Changed

  • Validation-pipeline performance: the full offline pytest suite
    now runs under pytest-xdist (-n 6 --dist=loadscope), cutting
    local wall-clock time from ~146s to ~65s (~55%) with zero test
    coverage loss — two tests that cannot safely collect under xdist
    (collection-time random parametrize values; a test asserting a
    fresh, untouched sys.modules state) run in a small serial pass
    instead. make quick and make validate are correspondingly
    faster (~75s and ~77s). Also removed a genuine redundancy from CI:
    the test job ran the full suite twice in the same job/SHA (a
    standalone pytest -q step immediately followed by make quick's
    own pytest stage) — the standalone step was removed.

v0.7.1

Choose a tag to compare

@night4me night4me released this 23 Aug 07:36

Documentation/packaging presentation correction. NO functional MCP/API/
security change.
Public contract byte-identical to v0.7.0: 95
pfSense READ tools + 1 official-guidance tool, 0 default-reachable
WRITE
, confirmed by a tests/contracts/mcp_public_contract_v0.7.1.json
snapshot that diffs to zero against v0.7.0's own snapshot.

Fixes a documentation/release-integrity defect found after v0.7.0
published: README.md's Quick start still instructed
pip install 'pfsense-mcp-server==0.5.1' — stale across two releases,
and, because hatchling embeds README.md verbatim as the wheel/sdist
long_description at build time, permanently frozen into the
already-published, immutable v0.7.0 PyPI artifact (PyPI's rendered
project page cannot be edited in place — only a new release changes
what it shows). This release exists solely to publish the correction.

Fixed

  • README.md's Quick start install command — replaced the stale
    pinned ==0.5.1 with an unpinned pip install --upgrade pfsense-mcp-server. No other install instruction in this repository
    pins an exact release version, and this exact line had already gone
    stale twice in a row (through v0.6.0 and v0.7.0) — evidence a
    pinned Quick start version is a recurring maintenance liability, not
    a deliberate convention.
  • Stale current-state documentation, found during the same
    repository-wide sweep: docs/ROADMAP.md's "Current baseline" section
    still named v0.5.1/84 tools as current (three releases stale);
    docs/index.md's two tool-count lines omitted the guidance tool;
    three spots in docs/THREAT_MODEL.md (an assets-table row, a section
    heading, and its intro sentence) still described the official-
    guidance layer as inert/consumerless, missed by v0.7.0's own
    release-readiness audit. Fixed narrowly; historical CHANGELOG,
    ACCEPTANCE_v0.*.md, dated compatibility-evidence rows, and other
    release-immutable records were left untouched.
  • Regression protection added: tests/test_readme_install_version.py
    asserts any pinned version in README.md's pip install line for this
    package matches pyproject.toml's current version, so a stale pin
    cannot silently recur.

v0.7.0

Choose a tag to compare

@night4me night4me released this 22 Aug 20:47

First official-guidance MCP tool. Public contract: 95 pfSense READ
tools (unchanged) + 1 new official-guidance tool
(pfsense_get_official_guidance), 0 default-reachable WRITE (unchanged)
.
The guidance tool is not a 96th pfSense READ capability — it is a
separate, structurally distinct tool class (tool_class: "guidance" in
the public contract, capability: null), not gated by the
Capability/privilege/profile system that governs appliance access, and
does not add to, widen, or reinterpret the 95-tool READ surface, the 94
distinct READ privileges, or the 1 implemented-but-default-unreachable
WRITE tool — all three are byte-identical to v0.6.0.

pfsense_get_official_guidance(capability) returns project-authored
summaries of official Netgate/pfSense documentation for a given
pfsense-mcp-server capability, from a deterministic, Git-tracked,
PR-reviewed bundled registry (ADR-017/ADR-018) — never a live web
fetch, never a runtime documentation retrieval of any kind. Every
returned entry carries structural provenance: a canonical Netgate
source URL, an evidence level, and an applicability state
(APPLICABLE/PARTIALLY_APPLICABLE/VERSION_UNCONFIRMED/
EDITION_MISMATCH/STALE/NO_OFFICIAL_GUIDANCE_FOUND) computed from
the pfSense appliance's own observed edition/version — resolved by the
tool itself through the exact same already-authenticated read path
every other READ tool uses (GET /api/v2/system/version), never
supplied by the caller and never guessed. Guidance output is fixed-schema
data only: it carries no field of type capability, endpoint, method, or
confirmation token, cannot invoke a tool, alter a capability, or
authorize any action, and is returned as inert content even when a
registry entry is adversarial. A fixed, non-overridable disclaimer field
on every response states plainly that guidance is documentation, not
observed live appliance state, and confers no authorization.

Fixed

  • Server-startup failure-coupling, found during this release's own
    release-readiness audit.
    An eager import in
    pfsense_mcp/guidance/__init__.py meant that importing
    GuidanceReference alone (needed for the new tool's own Pydantic
    schema) was enough to trigger the guidance registry's load-time
    integrity self-check on the server-startup path for every profile
    with any capability granted — a corrupted guidance registry entry
    could have crashed the entire MCP server, taking all 95 READ tools
    down with it. Fixed with a PEP 562 lazy __getattr__, verified by a
    fresh-subprocess test; a corrupted registry entry now fails only that
    one tool's own calls, never server startup or any other tool.
  • Stale "guidance layer unwired"/"95 tools" claims, found during the
    same audit, across README.md, docs/API.md,
    docs/CONFIGURATION.md, docs/SECURITY_MODEL.md,
    docs/THREAT_MODEL.md (scope line, TB9, the guidance adversarial-paths
    table, and the A7 threat entry), docs/OFFICIAL_GUIDANCE_LAYER.md,
    docs/VERSION_AWARE_GUIDANCE.md, docs/adr/ADR-017,
    docs/adr/ADR-018, the ADR index, and all 6 client example guides.
    Fixed narrowly and additively; historical CHANGELOG/v0.6.0-specific
    records were left untouched.

v0.6.0

Choose a tag to compare

@night4me night4me released this 22 Aug 11:52

READ-capability expansion. Public contract: 84 → 95 READ tools
(0 default WRITE, unchanged). Useful READ coverage: 84/105 (80.0%) →
95/105 (90.5%) against this project's own capability-audit denominator
(unchanged — every candidate added this release was already counted in
that original 105, none legitimately expands it). 84 → 94 distinct READ
privileges; 85 → 95 write-protected combined privileges. Every new tool
was exercised against a real pfSense instance (LAB, this project's
disposable CE 2.9.0 appliance) before public registration — 10 of the
11 with an exact field-level key-set match against their typed model
(FIELD_MODEL_LIVE_VERIFIED); the remaining one (WireGuard tunnel
addresses) confirmed reachable and correctly authorized but returned an
empty list on this LAB (ENDPOINT_VERIFIED), so its field shape rests
on independent schema/security review rather than an observed
populated item. No package was installed to support this release; the
one package-conditional addition (WireGuard tunnel addresses) uses a
package already present on the LAB from prior, separately authorized
work.

Added

  • Schema field-drift regression protection (v0.6.0 Phase B, Batch A).
    scripts/lib/schema_drift.py provides a general mechanism,
    independently designed (not derived from the comparison project
    investigated in the v0.6.0 competitive audit), that asserts every
    field a pinned upstream OpenAPI schema component declares is either a
    field this project's Pydantic response model already declares, or is
    present in an explicit, reviewed intentional_exclusions allowlist
    (e.g. WireGuardPeerStatus.preshared_key, deliberately never
    modeled). This closes a real, previously-unguarded gap: a future
    pfREST release adding a field to an already-modeled response object
    would otherwise go completely unnoticed, since a Pydantic model
    silently ignores unknown upstream keys by construction.
    tests/test_schema_field_drift.py registers four already-shipped
    models (ConfigHistoryRevision, SystemTimezone,
    WireGuardTunnelStatus, WireGuardPeerStatus) against a small,
    explicitly curated fixture (tests/fixtures/pinned_response_schemas.json)
    and proves the mechanism itself fires correctly against synthetic
    ordinary-field, secret-like-field, nested-model, exclusion-allowlist,
    stale-exclusion, and schema-evolution (nullable) cases. No MCP tool,
    capability, or public contract change. Public contract remains 84
    READ / 0 default WRITE.

  • pfsense_get_diagnostics_config_history_revisions (v0.6.0 Phase B,
    Batch B).
    Lists configuration-history (backup) revisions: change
    timestamp, pfSense's own system-generated audit description, the
    pfSense version at the time, and the backup file size. Metadata only
    — the v0.6.0 Phase A qualification independently confirmed, against
    the upstream ConfigHistoryRevision.inc Model source (not just the
    OpenAPI schema), that this endpoint's response never includes the
    backup's actual configuration content, only filesystem-level metadata.
    The underlying client method, typed model, and Endpoints entry
    already existed (added 2026-08-16 for internal ADR-026 evidence
    gathering, verified=True from that session's real LAB call); this
    release adds only the public MCP tool registration. A fresh
    confirmatory LAB call was attempted this session but could not be
    completed (the read-only LAB service account's privilege scope did
    not yet include this endpoint, and granting it required admin LAB
    access not available in this session) — promotion rests on the
    pre-existing 2026-08-16 evidence, disclosed explicitly rather than
    overstated; see docs/PFSENSE_LEAST_PRIVILEGE_MATRIX.md. Public
    contract: 84 → 85 READ tools (0 default WRITE, unchanged).

  • LogSettings model + client method, implemented and offline-tested
    (v0.6.0 Phase B, Batch C — not yet registered).
    New
    src/pfsense_mcp/models/log_settings.py (34 fields, all
    boolean/string/integer, no writeOnly/secret-shaped field anywhere —
    re-verified directly against the pinned schema immediately before
    implementation, independent of the v0.6.0 Phase A finding) and
    PfSenseClient.get_status_logs_settings(). LAB verification could not
    be completed this session: the read-only LAB service account's
    privilege scope covers only already-registered tools, and granting
    api-v2-status-logs-settings-get required admin LAB access that did
    not authenticate successfully. Endpoints.STATUS_LOGS_SETTINGS remains
    verified=False; no tools/read/ file, Capability enum member, or
    registry wiring was added, matching this project's established
    WireGuard-package-blocker precedent. Public contract unchanged at 85
    READ / 0 default WRITE. Registered in the Batch A schema-drift
    registry.

  • Apply-status sweep, implemented and offline-tested (v0.6.0 Phase B,
    Batch D — not yet registered).
    Eight new models/client methods for
    firewall/virtual_ip/apply, interface/apply, routing/apply,
    services/dhcp_server/apply, services/dns_forwarder/apply,
    services/dns_resolver/apply, vpn/ipsec/apply, vpn/wireguard/apply
    — each independently re-verified against the pinned schema (all
    trivial {"applied": bool} shapes; interface/apply additionally
    has a flat pending_interfaces: list[str]; no secret material in
    any). Mirrors the already-shipped FirewallApplyStatus pattern.
    applied/pending_interfaces are modeled | None (schema-declared
    nullable: true), deliberately not assumed non-null the way the
    pre-existing FirewallApplyStatus is, since no live call has
    confirmed any of these eight endpoints' actual behavior yet. None
    registered as public tools — same LAB-access blocker as Batch C.
    Public contract unchanged at 85 READ / 0 default WRITE.

  • WireGuard tunnel addresses, implemented and offline-tested (v0.6.0
    Phase B, Batch E — not yet registered).
    New
    WireGuardTunnelAddress model/client method for
    vpn/wireguard/tunnel/addresses (address/mask/descr, none
    writeOnly, no secret material — address/mask redacted by
    default, matching RoutingStaticRoute's established convention).
    Independently re-confirmed NOT redundant with the already-shipped
    WireGuardTunnelStatus, which has no address/CIDR field at all —
    unlike WireGuard peer allowed-IPs, deliberately not implemented
    since it is already nested as WireGuardPeerStatus.allowed_ips.
    pfSense-pkg-WireGuard is already installed on the LAB (prior,
    separately authorized action), so package availability is not this
    batch's blocker — the same read-only LAB service-account privilege
    scope issue as Batches C/D is. Not registered as a public tool. Public
    contract unchanged at 85 READ / 0 default WRITE.

  • v0.6.0 Phase B completion: LAB-verified and registered all 10
    previously-pending Batch C/D/E candidates.
    The read-only LAB
    service account (pfsense-mcp, id=2) was synced from a stale
    42-privilege snapshot (dating to its original 2026-08-19 ADR-033
    provisioning) to the current full requirement, after reconciling —
    with direct evidence from AI_CONTEXT.md's own provisioning
    checkpoint, not assumption — that this account is the project's
    single, intentionally-provisioned write_protected-profile service
    account (hence its pre-existing api-v2-firewall-alias-patch
    privilege, preserved throughout the sync). All 10 candidates were
    then live-verified in one pass:

    • pfsense_get_status_logs_settingsFIELD_MODEL_LIVE_VERIFIED.
      Fixed a real defect found by the live call, not assumed correct:
      sourceip needed the same nullable: falseOptional widening
      already applied to 17 other fields, missed in the earlier partial
      fix — a second live parse attempt (not a first-pass assumption)
      caught it.
    • pfsense_get_firewall_virtual_ip_apply_status,
      pfsense_get_interface_apply_status,
      pfsense_get_routing_apply_status,
      pfsense_get_dhcp_server_apply_status,
      pfsense_get_dns_forwarder_apply_status,
      pfsense_get_dns_resolver_apply_status,
      pfsense_get_ipsec_apply_status,
      pfsense_get_wireguard_apply_status — all 8
      FIELD_MODEL_LIVE_VERIFIED, exact key-set match, independently
      confirmed per-endpoint (not inferred from one passing).
    • pfsense_get_vpn_wireguard_tunnel_addresses
      ENDPOINT_VERIFIED (200, {"data": []} — no tunnel addresses
      configured on this LAB; field safety rests on the Phase A
      schema/security review, not an observed populated item).

    Public contract: 85 → 95 READ tools (0 default WRITE, unchanged).
    Useful READ coverage: 95/105 (unchanged historical denominator, per
    the Phase A correction). tests/fixtures/pfsense_openapi_schema_trimmed.json
    extended with all 10 endpoints' real captured descriptions (needed
    for the security-bootstrap/privilege-derivation test suite, which
    resolves privileges from this fixture). Fixed the resulting
    hardcoded-count cascade across test files, docs/API.md, and
    docs/PFSENSE_LEAST_PRIVILEGE_MATRIX.md.

Fixed

  • Stale tool-count/coverage claims across README, docs, and examples,
    found during this release's own release-readiness audit: the 84→95
    increase above was reflected in docs/API.md and
    docs/PFSENSE_LEAST_PRIVILEGE_MATRIX.md (updated as part of the
    READ-capability work itself) but not yet in README.md (Key facts,
    architecture diagram labels, capability-overview table and per-category
    counts, package-conditional-tools note), docs/ARCHITECTURE_DIAGRAMS.md,
    docs/API_SURFACE_ARCHITECTURE.md, docs/CONFIGURATION.md,
    docs/PYPI_RELEASE.md, docs/SECURITY_MODEL.md, docs/THREAT_MODEL.md,
    docs/index.md, or any of the 7 client-setup guides under examples/.
    Evidence-tier distinctions were preserved rather than strengthened:
    README's Plus 26.07 PRODUCTION VERIFIED row keeps its exact "82 of
    84" historical cla...
Read more

v0.5.1

Choose a tag to compare

@night4me night4me released this 21 Aug 22:49

Documentation-accuracy and security-communication patch. NO MCP
capability change. NO runtime security-semantic change. Public contract
remains exactly 84 READ / 0 default WRITE
, byte-identical to v0.5.0
— confirmed by an unchanged tests/contracts/mcp_public_contract_v0.5.1.json
snapshot relative to v0.5.0's. Every finding below is documentation or
presentation only.

Fixed

  • Post-publication documentation correction (2026-08-21): incorrect
    pfSense Plus REST API packaging claim.
    README's compatibility
    section (and the matching table in docs/ACCEPTANCE_v0.5.0.md)
    claimed the REST API "ships as a built-in platform component" on
    pfSense Plus rather than a separately versioned package, inferred
    from its absence in the general installed-package listing
    (pfsense_get_system_packages) during the v0.5.0 release audit. This
    was a genuine error: a direct, targeted follow-up call to
    pfsense_get_system_restapi_version — the tool actually built for
    this exact question, not consulted at the time — confirms the REST
    API package's own self-reported version (current_version) is
    v2.10 on both pfSense CE (re-confirmed live on the CE 2.9.0 LAB)
    and pfSense Plus 26.07 production
    , identical to the CE baseline
    already documented. Further investigation found the package does
    not appear as a discrete entry in the general installed-package
    listing on either edition — re-confirmed directly on the CE 2.9.0
    LAB (which lists only the one other package genuinely installed
    there) — so this was never a CE-vs-Plus difference at all, only a
    characteristic of that one endpoint on every platform tested. README
    and docs/ACCEPTANCE_v0.5.0.md corrected to state only
    independently-verified facts, distinguishing pfSense platform
    version, edition, REST API package version, and schema/API
    compatibility as the separate facts they are. v0.5.0 itself
    (already tagged, released, and published to PyPI before this error
    was found) still carries the original incorrect text in its
    immutable tag/Release/PyPI artifacts — per this project's own
    release policy, that historical record is not altered; this fix
    applies to main and every release from here forward. No public
    contract, security, or compatibility-verification-result change: the
    underlying evidence (schema match, tool-count regression results)
    was always correct — only the packaging-mechanism inference was
    wrong.
  • Package-dependency documentation was incomplete. README only
    documented the two WireGuard status tools as package-conditional.
    Re-derived every one of the 84 registered endpoints' schema-declared
    Required packages metadata directly (not assumed): four more tools
    (pfsense_get_acme_settings, pfsense_get_bind_settings,
    pfsense_get_cron_jobs, pfsense_get_freeradius_eap) reference a
    package in the schema's own metadata (pfSense-pkg-acme,
    pfSense-pkg-bind, pfSense-pkg-Cron, pfSense-pkg-freeradius3
    respectively) but were directly confirmed, by invoking them against
    systems genuinely lacking those packages (the CE 2.9.0 LAB for all
    four; the Plus 26.07 production appliance for three of the four),
    to succeed regardless — these read as stored
    configuration/default-settings structures, not genuinely
    package-gated runtime state, unlike the WireGuard status pair (which
    do 404 with MODEL_MISSING_REQUIRED_PACKAGE when absent). README
    now documents this distinction precisely instead of implying only
    WireGuard has any package reference at all.
  • Evidence-tier terminology overlapped. README's compatibility
    matrix previously used LIVE VERIFIED to mean "LAB or production,"
    which overlapped with the separate LAB VERIFIED tier and made the
    two indistinguishable for a reader. Replaced LIVE VERIFIED with
    PRODUCTION VERIFIED (production only) so all four tiers
    (PRODUCTION VERIFIED / LAB VERIFIED / SUPPORTED / COMPATIBLE /
    EXPECTED COMPATIBLE / UNVERIFIED) are mutually exclusive. No
    evidence changed — pfSense Plus 26.07's row is unaffected in
    substance, only relabeled from LIVE VERIFIED to the more precise
    PRODUCTION VERIFIED.
  • pfSense Plus 25.11's classification was too strong for its
    evidence.
    Re-evaluated rather than preserved as previously written:
    the evidence behind the prior SUPPORTED / COMPATIBLE classification
    was entirely adjacent (FreeBSD-generation similarity via Netgate's
    own 25.11 release notes, plus pfREST v2.10 behavior observed on
    other releases) — nothing this project has directly exercised
    touches a 25.11 instance in any way. Downgraded to
    EXPECTED COMPATIBLE / UNVERIFIED, the tier this project's own newly
    mutually-exclusive definitions assign to exactly this evidence
    profile.
  • An unqualified "verified before promotion" claim was too
    universal.
    README's "Key facts" bullet stated every tool is
    "verified against a real pfSense instance before public
    registration" without distinguishing depth. Reworded to state
    precisely what is true: every one of the 84 tools was exercised at
    least once and confirmed to match its typed model, but some have so
    far only been observed against a valid empty/default envelope on
    every system tested, not populated real data — the two are not the
    same claim and the README no longer conflates them.
  • docs/TIER1_ARCHITECTURE.md and docs/ARCHITECTURE_DIAGRAMS.md
    were stale, describing the pre-ADR-026 v0.3.0-era state
    ("no
    mutation executor exists yet," "adapter implementation remains
    blocked," "these diagrams describe the immutable v0.3.0 production
    baseline") despite the first WRITE capability having been built and
    independently live-verified since 2026-08-16. Added a dated
    historical note to TIER1_ARCHITECTURE.md (matching
    SECURITY_MODEL.md's own established correction pattern) rather than
    rewriting its still-accurate generic reusable-framework content, and
    updated ARCHITECTURE_DIAGRAMS.md's framing and its
    "Inert Tier 1 framework and future execution path" section — which
    claimed "no executor, endpoint, capability, or tool is active" — to
    describe the real, current architecture instead. This inaccuracy was
    independent of, and unrelated to, the pfREST packaging finding above;
    found during this release's own re-reading of the authoritative
    architecture sources before drafting new diagrams.

Added

  • Three new Mermaid architecture diagrams, derived directly from
    current source (tools/registry.py, capabilities.py, profiles.py,
    tier1/execution_coordinator.py, tier1/executor.py,
    tier1/state_machine.py) and accepted architecture
    (ADR-026, SECURITY_MODEL.md), not from this changelog entry's own
    prose:
    • READ security path — the exact path every one of the 84 tools
      takes, compact version in README near "Why this server," full
      version in docs/ARCHITECTURE_DIAGRAMS.md.
    • Protected WRITE authorization path — the gate-by-gate ADR-026
      flow (off-host signature → 6 fail-closed pre-execution gates →
      RecoveryContract → sealed MutationExecutor → read-back →
      verified/reconciliation), compact version in README's "Protected
      WRITE architecture" section, full version with all six gates named
      individually in docs/ARCHITECTURE_DIAGRAMS.md. Explicitly
      distinguishes IMPLEMENTED / VERIFIED / DEFAULT-REACHABLE as
      three different claims, since the one capability that exists is
      the first two but never the third.
    • Defense in depth / trust boundaries — a single high-level
      diagram in docs/ARCHITECTURE_DIAGRAMS.md showing which failure
      class each layer actually stops, limits, constrains, or detects
      (deliberately not a blanket "secure" label on any layer). Corrects
      a self-caught drafting error: the TPM witness was initially
      labeled "optional," which contradicts SECURITY_MODEL.md's own
      statement that production WRITE activation requires it (a
      software-only anchor alternative is modeled but has no implemented
      backend) — fixed before this diagram was ever committed.
    • All four diagrams (three new plus the existing set) independently
      validated with mermaid's own parser (mermaid.parse() via a
      headless DOM shim) before commit; full visual/browser rendering
      was not available in this environment (missing
      chrome-headless-shell system dependency), so parser-level syntax
      validation is this release's evidence tier for "renders correctly"
      rather than a rendered-image comparison.

v0.5.0

Choose a tag to compare

@night4me night4me released this 21 Aug 21:33

Major READ capability expansion — no WRITE change, no security-model
change.
Public MCP contract grows from 42 to 84 READ tools (exactly a
100% increase over the last published baseline, v0.4.2), covering
roughly 80% of the useful READ capability universe identified by this
project's own capability discovery audit (267 OpenAPI paths / 243 GET
operations reviewed; every GET given exactly one disposition — up from
roughly 40% coverage at the v0.4.2 baseline). Every tool below was
independently re-verified for secret-bearing fields before
implementation, then verified against a real pfSense instance (LAB or,
where explicitly owner-authorized, production) before public
registration — never assumed from schema alone. Closes with an
independent, adversarial release-readiness audit (security regression
sweep, CE/Plus compatibility verification including a live production
Plus 26.07 pass, packaging/fresh-install/upgrade-path verification, and
a full README restructure) — see "Security" and "Changed" below for its
findings.

Added

  • 2 new READ tools (production live-verified, pre-dating the P0
    backlog below), public MCP contract 42 → 44 (0 WRITE, unchanged):
    • pfsense_get_firewall_nat_outbound_mappings — outbound NAT
      mappings (source/destination/target address/alias fields
      redacted by default, matching FirewallNatPortForward's
      established convention; source_hash_key is a hash seed, not a
      credential, and stays visible).
    • pfsense_get_firewall_nat_one_to_one_mappings — 1:1 NAT mappings
      (external/source/destination redacted the same way).
    • Owner-authorized, narrowly-scoped live production READ
      verification (not LAB) of exactly these two endpoints: both typed
      GETs succeeded with zero configured mappings in either category;
      field-level type/nullability compatibility confirmed via an exact,
      byte-for-byte match between the live OpenAPI schema and the pinned
      v2.10 reference the models were derived from.
  • 7 new READ tools, public MCP contract 44 → 51 (0 WRITE, unchanged):
    • pfsense_get_interface_vlans — 802.1Q VLAN interfaces.
    • pfsense_get_routing_static_routes — static routes (network/gateway
      redacted by default).
    • pfsense_get_interface_groups — interface group membership, useful
      for interpreting firewall rules that target a group.
    • pfsense_get_firewall_schedules — time-based firewall schedules.
    • pfsense_get_system_restapi_version — installed REST API package
      version and update availability.
    • pfsense_get_firewall_virtual_ips — CARP/IP-alias/proxy-ARP virtual
      IPs (address fields redacted by default; the CARP shared secret is
      never modeled at all — see "Security" below).
    • pfsense_get_system_certificate_authorities — trusted CA inventory
      (the CA private key is never modeled at all — see "Security" below).
    • Two of the seven (system_restapi_version,
      system_certificate_authorities) were verified against real,
      populated LAB objects, not just an empty envelope — the latter
      against the LAB's own internal CA.
  • 2 more READ tools (P1 Batch A, partial), public MCP contract 51 →
    53 (0 WRITE, unchanged):
    • pfsense_get_status_ipsec_sas — live IPsec SA/tunnel status,
      including nested child SAs.
    • pfsense_get_status_ipsec_child_sas — live IPsec child SA status.
    • IPsecSaStatus.child_sas embeds full IPsecChildSaStatus objects
      (schema-confirmed $ref) and is constructed through that model's
      own parser for every nested item, not passed through as a raw dict.
    • status/wireguard/tunnels/status/wireguard/peers were also
      implemented and offline-tested this batch but remained unregistered
      at first: this LAB did not have pfSense-pkg-WireGuard installed,
      so live verification was blocked — see the next entry for how this
      was resolved.
  • 2 more READ tools (P1 Batch A completion), public MCP contract 53 →
    55 (0 WRITE, unchanged):
    owner explicitly authorized installing
    pfSense-pkg-WireGuard on the LAB for non-production READ
    verification only. Preflight: reconfirmed LAB identity distinct from
    production, identified the LAB as the sole pfSense-named VM in its
    Proxmox cluster, and took a fresh rollback snapshot before any
    change. Post-install: confirmed pfSense/pfREST healthy, re-ran a
    52-tool regression subset with zero regressions, then live-verified
    both endpoints (HTTP 200, correct envelope, zero configured tunnels/
    peers; raw responses inspected directly for unexpected fields).
    • pfsense_get_status_wireguard_tunnels — live WireGuard tunnel
      status, including nested peer status.
    • pfsense_get_status_wireguard_peers — live WireGuard peer status.
    • The package installation was a one-off authenticated LAB
      administrative call, made outside and independent of this
      project's own WriteApiClient/WriteEndpoints allow-list
      mechanism, which remains untouched and still empty except
      FIREWALL_ALIAS_DESCRIPTION — not a WRITE-capability expansion of
      the shipped server.
  • 4 more READ tools (P1 Batch B), public MCP contract 55 → 59 (0
    WRITE, unchanged):
    • pfsense_get_status_openvpn_servers — live OpenVPN server status,
      including nested connection/route status.
    • pfsense_get_status_openvpn_clients — live OpenVPN client status.
    • pfsense_get_status_openvpn_server_connections — flat, all-servers
      OpenVPN client connection status.
    • pfsense_get_status_openvpn_server_routes — flat, all-servers
      OpenVPN client route status.
    • Resolved the standing open question of whether the standalone
      connection/route endpoints duplicate OpenVpnServerStatus's own
      nested conns/routes fields using the pinned schema's own
      Parent model declaration (both standalone endpoints declare
      Parent model: OpenVPNServerStatus) — the identical structural
      relationship already established as non-redundant between
      IPsecSaStatus/IPsecChildSaStatus, so all four were implemented
      as genuinely independent capabilities rather than assumed
      duplicates.
    • OpenVpnServerStatus.conns/.routes embed full
      OpenVpnServerConnectionStatus/OpenVpnServerRouteStatus objects
      (schema-confirmed $ref) and are constructed through those
      models' own parsers for every nested item, not passed through as
      raw dicts.
  • 3 more READ tools (P1 Batch C), public MCP contract 59 → 62 (0
    WRITE, unchanged):
    • pfsense_get_dns_forwarder_host_overrides — dnsmasq (DNS Forwarder)
      host overrides: addresses, aliases, and descriptions.
    • pfsense_get_dns_resolver_domain_overrides — Unbound (DNS Resolver)
      domain overrides: forwarding target address and DNS-over-TLS
      settings.
    • pfsense_get_dns_resolver_access_lists — Unbound (DNS Resolver)
      access lists: allow/deny action and the network ranges each list
      applies to.
    • All three re-checked against the pinned schema for secrets (none
      found) and modeled following the existing shipped
      DnsResolverHostOverride precedent: full field visibility, no
      include_identifying_metadata redaction, since address/network
      data is the core content of this capability class (the same
      rationale already documented for DhcpServer).
    • All three LAB-verified live: HTTP 200, zero configured objects
      (ENDPOINT_VERIFIED); no package required (base
      pfSense/dnsmasq/Unbound features).
  • 3 more READ tools (P1 Batch D, interface extras), public MCP
    contract 62 → 65 (0 WRITE, unchanged):
    • pfsense_get_interface_available_interfaces — all interfaces
      available for assignment (not just already-assigned ones):
      identifier, in-use status, hardware boot message.
    • pfsense_get_interface_gres — GRE tunnel interfaces.
    • pfsense_get_interface_laggs — LAGG (link aggregation) interfaces.
    • All three re-checked against the pinned schema for secrets (none
      found). mac (AvailableInterface) and 7 of InterfaceGRE's 11
      fields (tunnel-endpoint addresses) are redacted by default,
      matching InterfaceStatus.macaddr and RoutingStaticRoute's
      established conventions; InterfaceLAGG's members/laggif stay
      visible, matching InterfaceBridge's established no-redaction
      precedent. InterfaceLAGG's proto-conditional fields
      (lacptimeout/lagghash/failovermaster) use .get() with the
      schema's own declared default, matching the install_version
      precedent for a field that can be legitimately absent rather than
      merely null.
    • interface/available_interfaces LAB-verified with
      FIELD_MODEL_LIVE_VERIFIED: 2 real populated objects (the LAB's
      actual vtnet0/vtnet1 WAN/LAN interfaces), with redaction
      confirmed against real data. interface/gres and
      interface/laggs both LAB-verified ENDPOINT_VERIFIED (zero
      configured objects); no package required for any of the three
      (base pfSense features).
    • The models/client methods/Endpoints entries were implemented and
      offline-tested one commit before registration, deliberately
      unregistered in the interim, matching the established
      "implemented, offline-tested, blocked" precedent from P1 Batch A's
      WireGuard pair.
  • 5 more READ tools (P1 Batch E, routing + DHCP extras), public MCP
    contract 65 → 70 (0 WRITE, unchanged):
    • pfsense_get_routing_gateway_groups — gateway groups: name,
      failover trigger, description, prioritized member gateways.
    • pfsense_get_routing_gateway_default — current default IPv4/IPv6
      gateway assignment.
    • pfsense_get_dhcp_relay — DHCP Relay configuration.
    • pfsense_get_dhcp_server_address_pools — additional DHCP scopes
      across all configured DHCP servers.
    • pfsense_get_dhcp_server_custom_options — DHCP custom options
      across all configured DHCP servers.
    • RoutingGatewayGroupPriority.gateway/.virtual_ip and
      DefaultGateway.defaultgw4/.defaultgw6 (gateway name references)
      ...
Read more