Skip to content

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Changed

  • Replaced the user-facing system_objects_filtered ReportWarning in collect_change_report() with a structured DEBUG-level log entry via arlogi (src/arodonata/reports/collector.py, openspec/specs/change-reports/spec.md) so routine filtering of background Check Point "System" maintenance objects does not clutter rendered HTML/Markdown evidence reports.

v2.2.0 (2026-10-31)

Feat

  • reports: synthesize snapshot field diffs and style HTML evidence like SmartConsole (#58)

Added

  • Snapshot-based FieldDiff synthesis, pre-change bare-UID resolution, "System" maintenance noise filtering, and SmartConsole-styled inline diff rendering (arodonata.reports, arodonata.mcp.tools): collect_change_report() now synthesizes FieldDiff entries (old_value, new_value, added_items, removed_items) directly from old-object and new-object snapshots whenever explicit field-changes are absent or partial (avoiding false-positive rule diffs when an unchanged referenced group has internal member edits), recursively harvests {uid, name} dictionaries across old-object and new-object and resolves bare UUIDs appearing only in old-object or track (such as removed group.members or track.type) via CachePort and bounded show-object lookups, populates structured ObjectChangeEntry.summary_fields and enriched ObjectChangeEntry.summary (ipv6-address, subnet4/mask-length4, subnet6/mask-length6, address-range bounds, members, comments), and excludes non-deleted background Check Point "System" maintenance non-rule objects by default (RenderOptions.include_system_objects: bool = False, also exposed on the change_report MCP tool) while emitting a system_objects_filtered ReportWarning (details={"filtered_count": ...}) and preserving ChangeReport.raw_responses; apply_session_rule_placement() groups rules by (package, rulebase_type, layer) before sorting in ascending SmartConsole order; and render_change_report() renders SmartConsole-styled HTML tables (dark-blue #253245 headers, full-width pale-yellow #eee8aa access section banner rows ▾ <section_name> (<section_range>), shaded No. column, Track column in both HTML and Markdown, and Accept/Drop action indicators) with inline cell-level removed/added/before-after diffs (including composite subnet4=net/mask and range=first-last diffs) in both HTML and Markdown.

v2.1.1 (2026-10-30)

Fix

  • reports: support post-publish OwnedSession placement, cold-cache fallback, and cross-domain lookups (#56)

Security

  • Explicit server_ip trust validation and MCP staged-session boundary hardening (arodonata.adapters.checkpoint.sessions, arodonata.cpcrud.executor, arodonata.mcp.tools): LoginCoordinator.resolve_target_ips() and LoginCoordinator.create_dedicated_session() now verify that any caller-supplied server_ip belongs to the configured management host or cached DomainInfo IPs (active_ip, active_mds_ip, standby_ips, servers) before opening a socket, raising ValidationError on untrusted destinations; and MCP cpcrud_staged_session resolves target domain endpoints internally rather than accepting an unverified server_ip argument.

Fixed

  • Post-publish OwnedSession live rulebase numbering, object-only session placement short-circuit, cold-cache live published rulebase fallback, and bare layer_uid resolution (arodonata.reports): apply_session_rule_placement() now accepts a clean post-publish OwnedSession (state == "published" or published_at is not None with changes == 0, locks == 0, number-of-published-changes == 0, and matching domain) to read the published rulebase live via sid without emitting owned_session_not_used and best-effort warm CachePort.replace_domain_rulebase() alongside arodonata_domain_sync_state (numbering_source = "live_published", numbering_basis = "Numbering read live after publish at <time> UTC" derived from session_report.publish_time, skipping cache overwrite if a concurrent refresh advanced arodonata_domain_sync_state); short-circuits immediately when session_report.rule_changes is empty (numbering_source = "not_applicable", numbering_basis = "") while omitting .basis-banner and - **Numbering Basis**: in HTML and Markdown renderers; falls back to a single-fetch read-only collect_domain_rulebase_snapshot() + cache warm when the local SQL rulebase cache is cold (numbering_source = "live_published", numbering_basis = "Numbering read live from published rulebase at <time> UTC"); and resolves bare UUID layer references on rule changes via cache and bounded show-object lookups into top-level RuleChangeEntry.layer_name.
  • Cross-domain cache-first domain=None lookups and ambiguity detection (arodonata.core.errors, arodonata.client, arodonata.core.services.search_service, arodonata.mcp.tools): ArodonataClient.find_object(), SearchService.get_group_members(), and MCP show_object / get_group_members now enforce cache_mode="cache" and include_global=True when domain is omitted (preventing multi-domain network refresh storms), reject blank/whitespace targets with ValidationError, raise AmbiguousObjectError in the Python SDK when multiple domains match, and return match_type="ambiguous" with candidate objects in MCP tools.
  • LoginGate inter-login spacing without distributed lock starvation, post-revert read-only session recovery, and CPCRUD staged session takeover cleanup (arodonata.adapters.checkpoint.rate_limiter, arodonata.adapters.checkpoint.sessions, arodonata.adapters.checkpoint.client, arodonata.cpcrud.executor): LoginGate.acquire() now releases the cross-process login_gate:{mds_ip} distributed lock immediately after recording mark_login_attempt and sleeps login_attempt_spacing outside the distributed lock; is_session_expired_result() classifies show-session (without uid) returning generic_err_object_not_found after revert-to-revision as an expired session so AMgmtClient.api_call() automatically evicts the wiped SID and re-authenticates; and Executor._adopt_staged_session() passes {"uid": session_uid, "disconnect-active-session": True} to take-over-session with fallback to switch-session ({"uid": session_uid}) while logging out the initial dedicated session on exit.

v2.1.0 (2026-10-26)

Feat

  • cache,docs: add human-readable composite IDs and v2 documentation (#53)

Fix

  • docs: fix pymdownx.snippets syntax across example pages (#54)

Added

  • Human-readable composite identifiers (object_id, service_id, asset_id) and dual UID/human-readable topology links (arodonata.core.models, arodonata.adapters.storage, arodonata.core.services, arodonata.client, arodonata.mcp): non-unique indexed object_id = "{mgmt_name}:{domain_name}:{name}" on CPObject / CPObjectRecord (arodonata_objects), service_id = "{mgmt_name}:{domain_name}:{name}" on CPService / CPServiceRecord (arodonata_services), and asset_id = "{mgmt_name}:{domain_name}:{name}" on Asset / AssetRecord (arodonata_assets) alongside UID primary keys id = "{mgmt_name}:{domain_name}:{uid}"; id, object_id, and parent_object_ids on GroupMembershipNode in SearchResult.memberships; parent_id, parent_asset_id, member_asset_ids, and virtual_system_asset_ids on Asset / AssetRecord with cross-domain VSX gateway/cluster to Virtual System reconciliation across domains on the same management server; and composite identifier support across find_object(), get_group_members(), search_objects(), query_objects(), and MCP tools (show_object, list_assets, get_group_members, query_objects).
  • Complete MkDocs Material documentation site (docs/, mkdocs.yml, .github/workflows/docs.yml), portable standalone HTML guide (docs/_html/arodonata-v2-docs.html), and NotebookLM knowledge bundles (docs-notebooklm/01..04.md).

v2.0.0 (2026-10-26)

Feat

  • mcp: consolidate MCP tool surface into cache-aligned catalog (#9)
  • sdk: live integration tests (b1..b7), SDK examples (01..12), and host-app integration hardening (#8)
  • mcp: implement Streamable-HTTP MCP server, CLI, security guards, and tool surface (Milestone 6) (#7)
  • cpcrud: implement declarative Policy-as-Code CPCRUD engine (#6)
  • rulebase,reports: implement rulebase introspection, numbering, and change reports (#5)
  • sync: implement core sync/search services and ArodonataClient facade (#4)
  • checkpoint: implement native aiohttp transport, TLS verification, and session lifecycle (#3)
  • core,storage: implement milestone 1 core foundation and sql storage adapters (#2)

Security

  • Comprehensive security and specification conformance remediation (audit-remediation-261025 across arodonata.adapters, arodonata.core, arodonata.cpcrud, arodonata.rulebase, and arodonata.mcp): strict Web API command-name validation (^[a-z0-9]+(-[a-z0-9]+)*$), disabled HTTP redirects (allow_redirects=False) and ambient proxies (trust_env=False), and phase-classified TransportError (connect vs post-send) preventing post-write request resends on failover; cross-process fcntl.flock locking, 0600 permission enforcement, and IPv6 [host]:port key normalization in TrustStore; full secret and database_url password masking across ArodonataSettings/ArodonataMCPSettings and recursive secret/SID redaction across nested collections and exception chains; arodonata_ prefix on all 15 SQLModel tables with positive v1 column fingerprint checks (mdm_dmn, mgmt_dmn_key), cross-worker serialized migration locks (pg_advisory_xact_lock / BEGIN IMMEDIATE), atomic single-transaction cache + sync-state commits, dialect ON CONFLICT upserts, and owner-verified distributed lock renewal heartbeats; HMAC-SHA256 credential-bound arodonata_sid_cache keys, LoginGate self-deadlock elimination and inter-login spacing, Arodonata-only SessionCleaner filtering (changes == 0 and locks == 0), and strict prohibition of mutating commands on the shared read-only arodonata_sid_cache session; CPCRUD (object_type, operation) command allowlisting, per-plan HybridStateReader isolation, pre-publish head drift re-verification, and report-verified inverse() generation; and MCP read-only command allowlisting, HMAC-SHA256 signed cpcrud_plan verification in cpcrud_apply/cpcrud_inverse, cpcrud_staged_session tool, base64-safe 32-char minimum static token parsing with SHA-256 hmac.compare_digest, and fail-closed jwt authentication.

Added

  • Consolidated cache-aligned MCP tool surface (arodonata.mcp, arodonata.core.services, arodonata.client): 13 base tools (list_domains, list_objects, list_services, list_assets, show_object with 3-tier exact/anchored-wildcard/substring matching across objects, services, assets, and rulebase layers, search_objects, get_group_members expanding network groups, service groups, and cluster/VSX child assets, query_objects with aggregate="list" | "count" | "describe", show_rulebase, locate_rules, change_report, cache_control, and api_call) plus 5 opt-in CPCRUD tools (cpcrud_validate, cpcrud_plan, cpcrud_apply, cpcrud_inverse, cpcrud_staged_session) and multi-table ArodonataClient.find_object.
  • Host application integration hardening (arodonata.adapters.storage, arodonata.core.services, arodonata.cpcrud, arodonata.rulebase): to_naive_utc() helper and uniform naive UTC timestamps (DateTime(timezone=False), tzinfo=None) across all database tables, domain models, and Check Point timestamp parsers; ARODONATA_TABLE_NAMES namespace isolation and arodonata_assets table rename (preventing collision with host application assets tables and ignoring non-Arodonata tables in SQLModel.metadata); savepoint-wrapped (begin_nested()) multi-worker schema evolution with automatic DROP + CREATE recovery for incompatible legacy v1 cache tables (domains, sid_cache, arodonata_schema_version); exact_match: bool = False in SearchService.search_objects(), ArodonataClient.search_objects(), and MCP search_objects; staged session finalization (publish_staged_session, stream_publish_staged_session, discard_staged_session) on CPCRUDService and ArodonataClient via take-over-session (uid=session_uid); and rule_to_cpcrud_operation() with output_format="cpcrud" support in filter_rulebase() for exporting cached rules into schema-valid CPCRUD operations.
  • Live Check Point MDSM/SMS integration test harness (tests/integration/, openspec/specs/integration-testing-and-examples/spec.md), ./pytest.sh test runner (unit, int-1..int-7, int-full with -v enabled by default), and 12 progressive runnable SDK examples (examples/01..12, examples/_session_guard.py, examples/crud_example.yaml, examples/README.md, examples/README_CRUD.md): layered lab environment loader (tests/integration/lab_env.py supporting ARODONATA_LAB=<profile>), non-blocking process-wide fcntl.flock run lock (tests/integration/run_lock.py), live API readiness probe (tests/integration/lab_ready.py), crash-safe domain revision snapshot and revert-to-revision baseline recovery (tests/integration/cp_revision.py and restore_baseline.py), standalone diagnostic utilities (probe_lab_domains.py, probe_login_throttle.py, lab_build.py), 60 live integration tests across 7 buckets (b1 auth/sessions/reads/search/rate-limiting, b2 cache build & CPCRUD live operations, b3 publish/revert & 70s login throttle recovery, b4 cache mode matrix & multi-domain isolation, b5 full rebuild/120-rule pagination/rulebase session refresh/publish matrix/change reports, b6 bounded soak loop, b7 MCP server & tools), top-level re-exports (Asset, CPObject, CPService, DomainInfo, RefreshOutcome, SearchResult), ArodonataClient.cleanup_server_sessions(), and otel optional dependency group (arodonata[otel]).
  • Streamable-HTTP MCP server and standalone CLI (arodonata.mcp, arodonata[mcp], arodonata-mcp): ArodonataMCPSettings (ARODONATA_MCP_* configuration with secret-safe __repr__ and standalone startup validation refusing auth_mode="host" or "jwt" and missing pinned TLS trust material with exit code 2), create_mcp_server and create_asgi_app (ArodonataASGIApp for Starlette/FastAPI embedding), StaticTokenVerifier and MCPBearerAuthMiddleware (constant-time hmac.compare_digest verification across ARODONATA_MCP_TOKEN_VARS mapping variable names to client_id in logs and UserContext without logging token values), MCPOriginHostGuardMiddleware (DNS rebinding protection returning HTTP 421 for unlisted Host and HTTP 403 for unlisted Origin), pre-login domain validation against the cached arodonata_domains table (with single domain-list refresh on miss) and InvalidCredentialsError translation into actionable ToolFailure messages listing known domains, and MCP prompt templates (audit_session_changes, locate_ip_in_policy, plan_declarative_change).
  • Declarative Policy-as-Code CPCRUD engine (arodonata.cpcrud) wired into ArodonataClient (client.cpcrud): offline JSON Schema Draft-07 template validator (CPCRUDService.validate / validate_template over checkpoint_ops_schema.json with zero network or database calls), HybridStateReader (isolated per-plan state reader serving objects from arodonata_objects and services from arodonata_services strictly when arodonata_domain_sync_state for (scope, item_type) matches the planner's DomainStamp last_published_at and last_session_uid, with transparent live API fallback and intra-plan virtual state overlay), Planner and Differ (pre-lookup DomainStamp capture, two-axis conflict resolution NameConflictPolicy x IpConflictPolicy with NamingPrefixes auto-naming, Outcome.ERROR and Outcome.SKIPPED_DEPENDENCY on lookup failures instead of false CREATE, rule identity matching by key or traffic tuple with Check Point NAT "Original" / "Any" sentinel normalization, and cleanup-rule-aware "bottom" placement anchoring {"above": "<cleanup_rule_uid>"}), dedicated-session Executor (apply and stream_apply with Outcome.PLAN_STALE safeguard, per-domain WriteSession + SessionOverlay updates, no_publish=True, keep_session=True staging with StagedSessionInfo/StagedSessionEvent and SID redaction, post-publish invalidate_domain, and retry_remaining folding), and compensating rollback generator (CPCRUDService.inverse / build_inverse_template).
  • Rulebase introspection, SmartConsole hierarchical numbering (arodonata.rulebase), and auditable session change reports (arodonata.reports) wired into ArodonataClient: section-stitching rulebase pager (fetch_paged_rulebase merging split rulebase-section headers across 100-rule pages and recovering empty trailing sections), RulebaseRefreshService (refresh_rulebases / stream_refresh_rulebases with dirty-session refusal, full package/NAT/access/HTTPS/threat/inline/Global placeholder traversal, and atomic replace_domain_rulebase replacement), CachedRulebaseSource (get_policy_packages, get_package_rulebase, get_layer_rulebase, locate_rules, automatic domain resolution with AmbiguousLayerName, and local filter_rulebase evaluation over cached rules/objects/services), and change report pipeline (collect_change_report with SessionScope/RangeScope UTC validation, deduplication, ReportWarning partial-failure tracking, bounded 200-UID show-object lookup, read-only live OwnedSession rule numbering with full SID redaction and cached snapshot fallback, and offline render_change_report / build_change_report for self-contained HTML, Markdown, and versioned JSON).
  • Core synchronization, search services (arodonata.core.services), and unified ArodonataClient facade (arodonata.client): DomainService and DomainListRefreshTracker (MDSM show-domains discovery, Global domain synthesis, include_global filtering, active/standby failover IP re-resolution, and 1-hour TTL), DomainSyncCoordinator (parallel domain traversal per active_mds_ip at max(1, concurrent_limit - 1), per-domain distributed locks, dirty-session checks via show-session, (last_published_at, last_session_uid) staleness comparison in arodonata_domain_sync_state, and probe InvalidCredentialsError failure handling), ObjectService (paired refresh_objects / stream_refresh_objects, CacheMode "cache" | "smart" | "smart-fast" | "force", "smart-fast" incremental diff via show-changes with fallback on > max_incremental_changes, default ("host", "network", "address-range", "group", "dns-domain"), and type-scoped replacement), ServiceCacheService (arodonata_services with default ("service-tcp", "service-udp", "service-icmp", "service-group") and selective on-demand types=[...]), AssetRefreshService (ClusterRelationshipManager and VSXRelationshipManager via encapsulated generic-object queries), SearchService (search_objects with IP/CIDR/range/wildcard/UID/name classification and recursive group traversal up to max_depth, describe_objects, query_objects, and get_cache_status), and ArodonataClient (typed getters, WriteSession overlay merging, write_session, auth_context, add_object, set_object, delete_object, find_object, get_group_members, start_object_cache_warmup, and invalidate_domain).
  • Check Point native asyncio transport and session adapter (arodonata.adapters.checkpoint): TrustStore and TrustPolicy (pinned, tofu, lab-memory with atomic 0600 tls_known_hosts.json persistence and cross-IP MDS member certificate recognition), ApiTransport over aiohttp (post-handshake pre-request SHA-256 fingerprint verification, separate connect_timeout/read_timeout ApiTimeoutError phases, no automatic re-send after socket write, and VerifiedConnectionPool reusing keep-alive sockets up to 10s idle strictly for show-* commands), TaskWaiter (exponential backoff 2s..10s polling under task_timeout tolerating up to 5 transient poll errors), continuity-verified object pager (pager.py with ±10 boundary swap window recovery and single restart on discontinuity), per-MDS-member RateLimiter (database slot locks + FIFO waiter queue + coalesced keepalive sweeps) and LoginGate (70s throttle recovery and fast-fail InvalidCredentialsError), LoginCoordinator, SessionCleaner, context-bound WriteSession (SessionOverlay updates, auto-publish / discard), and AMgmtClient implementing ApiPort.
  • Core foundation (arodonata.core) and SQL storage adapter (arodonata.adapters.storage): ArodonataSettings, exception hierarchy with SID/secret redaction, @traced and span_attrs instrumentation, typed domain progress events (SyncProgressEvent, CPCRUDEvent), contextvars-bound AuthContext, UserContext, and WriteSession with SessionOverlay, Pydantic v2 domain models, hexagonal protocols (ApiPort, CachePort, LockPort, AuthProvider), DatabaseManager (SQLite + PostgreSQL with SHA-256 schema hash auto-migration via arodonata_schema_version), SqlLockAdapter, SqlCacheAdapter, and SSE boundary adapter (arodonata.adapters.sse).

Changed

  • Revised all OpenSpec capability specifications for Arodonata v2 clean-slate hexagonal architecture: native aiohttp transport (replacing cpapi), SqlCacheAdapter (replacing PostgresCacheAdapter/CacheRepository), canonical CacheMode names (cache, smart, smart-fast, force), paired coroutine and stream_* domain event APIs (removing SSEEvent from core), and unified ArodonataClient facade (replacing arodonata.helpers).

Fixed

  • Check Point R82 live compatibility and persistent cache/session reuse fixes: ArodonataClient and AMgmtClient now honor settings.database_url when engine=None, auto-create parent directories for file-backed SQLite URLs, preserve arodonata_sid_cache entries on close() for persistent databases, and skip redundant startup SessionCleaner logins when a valid system-domain SID is already cached; LoginCoordinator classifies generic R82 "Authentication to server failed." (err_login_failed) as non-retryable InvalidCredentialsError; DomainSyncCoordinator and ObjectService extract "iso-8601" from dict-formatted publish-time and unwrap task-based show-changes operations arrays; Collector passes view-published-sessions=True for published session discovery; and Planner._find_existing_nat_rule resolves inline object/service names to UIDs for idempotent NAT rule re-planning.