Skip to content

SYNC plugin improvements - skipping non-reachable nodes, SYNC_BEHAVIOR#1655

Open
jokob-sk wants to merge 5 commits into
mainfrom
next_release
Open

SYNC plugin improvements - skipping non-reachable nodes, SYNC_BEHAVIOR#1655
jokob-sk wants to merge 5 commits into
mainfrom
next_release

Conversation

@jokob-sk
Copy link
Copy Markdown
Collaborator

@jokob-sk jokob-sk commented May 25, 2026

Summary by CodeRabbit

  • New Features

    • Configurable hub SYNC_BEHAVIOR (copy-new, carbon-copy, hub-defaults); hub applies received devices per selected mode and reports behavior-specific write counts.
    • Stronger validation for incoming node data with skips and logging.
  • Documentation

    • Clarified Sync API, multi-network guidance, plugin README, and admin docs to describe SYNC_BEHAVIOR and best practices.
  • Tests

    • Added node-name parsing regression and a comprehensive suite validating each sync behavior and new-device event emissions.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b3d3717c-491e-4b93-ac93-73a41b44c555

📥 Commits

Reviewing files that changed from the base of the PR and between cfea848 and d4fe94f.

📒 Files selected for processing (1)
  • front/plugins/sync/sync.py

📝 Walkthrough

Walkthrough

Adds configurable hub SYNC_BEHAVIOR (copy-new, carbon-copy, hub-defaults), updates hub sync code and test helper to honor behavior including event pre-inserts and upserts, tightens filename PUSH/PULL detection test logic, and updates frontend config and documentation.

Changes

Filename Detection Logic Refinement

Layer / File(s) Summary
PUSH/PULL detection logic update
test/plugins/test_sync_protocol.py
_node_name_from_filename now requires both a .decoded./.encoded. marker and a numeric counter segment to classify a filename as PUSH; otherwise it falls back to PULL extraction. Docstring expanded to document both supported filename shapes.
Regression test for encoded node names in PULL mode
test/plugins/test_sync_protocol.py
Added test_pull_with_encoded_in_node_name to verify that PULL filenames containing .encoded. in the node name extract correctly as PULL node names without PUSH misclassification.

Hub Sync Behavior Implementation and Tests

Layer / File(s) Summary
Test DB helper: sync_insert_devices(behavior...)
test/db_test_helpers.py
sync_insert_devices() gained a behavior parameter; it pre-inserts Events for newly discovered MACs (except hub-defaults), selects devices to write per mode, and builds schema-aware INSERT vs upsert SQL.
Hub sync device-write flow
front/plugins/sync/sync.py
Read SYNC_BEHAVIOR, log mode, optionally skip direct Devices writes for hub-defaults, optionally insert “New Device” Events for new MACs, whitelist Devices columns, and perform batch INSERT OR IGNORE (copy-new) or INSERT ... ON CONFLICT DO UPDATE (carbon-copy). Also adds input validation and base64 decode error handling in the receive loop.
Behavior test suite
test/plugins/test_sync_protocol.py
New TestSyncBehavior tests for copy-new, carbon-copy, and hub-defaults: verify insert/upsert/skip semantics, mixed-batch handling, empty input behavior, and New Device event emission and fields.

Documentation and Frontend Config

Layer / File(s) Summary
Frontend config addition
front/plugins/sync/config.json
Add BEHAVIOR setting (hub behavior) with options copy-new, carbon-copy, hub-defaults and default copy-new.
Plugin README: SYNC_BEHAVIOR docs
front/plugins/sync/README.md
Introduce and document Hub Device-Write Behavior section with mode table and per-mode semantics; reformat examples and notes.
API and auxiliary docs updates
docs/API_SYNC.md, docs/REMOTE_NETWORKS.md, docs/ADVISORY_MULTI_NETWORK.md, docs/NOTIFICATIONS.md
Add SYNC_BEHAVIOR section to API docs, insert TIP in remote networks docs referencing behavior, small tail/fmt fixes in notifications and advisory docs.

Sequence Diagram(s)

sequenceDiagram
  participant HubSync
  participant EventsTable
  participant DevicesTable
  HubSync->>EventsTable: INSERT New Device rows for genuinely new MACs (non-hub-defaults)
  HubSync->>DevicesTable: INSERT OR IGNORE (copy-new)
  HubSync->>DevicesTable: INSERT ... ON CONFLICT(devMac) DO UPDATE (carbon-copy)
Loading

Possibly related PRs

  • netalertx/NetAlertX#1654: Also modifies the sync protocol’s filename/node-name extraction logic and related tests.
  • netalertx/NetAlertX#1653: Related filename/node-name extraction adjustments in the test mirror for .decoded./.encoded. cases.

Poem

A rabbit hops through sync and docs,
Counting dots and fixing flocks,
Modes declared, the hub decides,
Events queued where newness hides,
Tests attest — the garden clocks 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% 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 directly summarizes the main changes: improved node handling for non-reachable nodes and introduction of the SYNC_BEHAVIOR configuration feature.
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 next_release

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
docs/API_SYNC.md (1)

94-115: ⚡ Quick win

Deduplicate repeated processing-condition note.

The same bullet appears at Line 94 and Line 115; keep one copy to avoid future inconsistency.

🤖 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 `@docs/API_SYNC.md` around lines 94 - 115, Remove the duplicated sentence "The
data is only processed if the relevant plugins are enabled and run on the target
server." so it appears only once (keep the occurrence under Key Notes) by
deleting the second copy at the end of the "Storage Details" section; ensure
surrounding bullets/spacing remain consistent and run a quick spell/format check
to avoid leaving an extra blank line.
🤖 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 `@front/plugins/sync/README.md`:
- Line 88: Fix the typo "fileds" -> "fields" in the README sentence that
describes hub field behavior for user-customized values; locate the sentence
mentioning "source set to `USER` or `LOCKED`" and replace "fileds" with "fields"
so the line reads "...Fields customized by a user on the hub (fields with source
set to `USER` or `LOCKED`) are never overwritten."
- Around line 90-118: Update the three fenced code blocks in
front/plugins/sync/README.md to include an explicit language tag (use "text") so
markdown linting passes; specifically, change the first block containing "First
sync:  INSERT with node's full config" to start with ```text, the second block
containing "First sync:  UPSERT with node config" to start with ```text, and the
third block containing "First sync:  NEWDEV defaults applied" to start with
```text.

In `@front/plugins/sync/sync.py`:
- Around line 269-286: The comment describing the "carbon-copy" SYNC_BEHAVIOR is
incorrect; update the comment in front/plugins/sync/sync.py so it accurately
states that "carbon-copy" UPSERTs overwrite all hub fields on every sync,
including USER/LOCKED-sourced fields, and remove the claim that enforcement is
handled by update_devices_data_from_scan (since that pipeline is not invoked for
these writes); reference the SYNC_BEHAVIOR "carbon-copy" mode and the
update_devices_data_from_scan pipeline in the updated comment so readers can
find the related implementation and README contract.

In `@test/plugins/test_sync_protocol.py`:
- Line 575: Replace the unicode EN DASH characters in the comment containing
"SYNC_BEHAVIOR — three hub device-write modes (Mode 3 – RECEIVE)" with an ASCII
hyphen-minus '-' (e.g., "SYNC_BEHAVIOR - three hub device-write modes (Mode 3 -
RECEIVE)"); update both occurrences of the EN DASH so Ruff RUF003 no longer
flags them, leaving the comment text otherwise unchanged.
- Around line 768-778: The final assertion is calling cur.fetchone()["cnt"]
after already consuming the first row from the SELECT * query, causing
fetchone() to return None; update the test (in test_sync_protocol.py around the
block using cur and row) to either 1) run a separate COUNT query such as SELECT
COUNT(*) AS cnt FROM Events WHERE eveEventType='New Device' AND eveMac=? and
assert cur.fetchone()["cnt"] == 0/expected, or 2) replace the initial SELECT *
with cur.execute(...) then rows = cur.fetchall() and assert len(rows) and
inspect rows[0] (use the variable names cur and row as appropriate) so you don't
call fetchone() on an exhausted cursor.

---

Nitpick comments:
In `@docs/API_SYNC.md`:
- Around line 94-115: Remove the duplicated sentence "The data is only processed
if the relevant plugins are enabled and run on the target server." so it appears
only once (keep the occurrence under Key Notes) by deleting the second copy at
the end of the "Storage Details" section; ensure surrounding bullets/spacing
remain consistent and run a quick spell/format check to avoid leaving an extra
blank line.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c5f1742-3b9a-4957-8ac0-53a5d088a774

📥 Commits

Reviewing files that changed from the base of the PR and between c829c4c and f8f7ac3.

📒 Files selected for processing (9)
  • docs/ADVISORY_MULTI_NETWORK.md
  • docs/API_SYNC.md
  • docs/NOTIFICATIONS.md
  • docs/REMOTE_NETWORKS.md
  • front/plugins/sync/README.md
  • front/plugins/sync/config.json
  • front/plugins/sync/sync.py
  • test/db_test_helpers.py
  • test/plugins/test_sync_protocol.py
💤 Files with no reviewable changes (1)
  • docs/NOTIFICATIONS.md
✅ Files skipped from review due to trivial changes (1)
  • docs/REMOTE_NETWORKS.md

Comment thread front/plugins/sync/README.md Outdated
Comment on lines +90 to +118
```
First sync: INSERT with node's full config
Next syncs: empty fields updated only (name, vendor) via scan pipeline
User edits: protected — never overwritten
```

#### `carbon-copy`

All received devices are upserted on every sync. The node is treated as fully authoritative: its values overwrite **all** hub fields on every sync cycle, including fields with `USER` or `LOCKED` source.

> ⚠️ Do not customize devices on the hub when using `carbon-copy`. Any hub-side changes will be overwritten on the next sync.

```
First sync: UPSERT with node config
Next syncs: UPSERT — node values win (all fields, no exceptions)
User edits: overwritten on next sync
```

#### `hub-defaults`

**The hub is the source of truth.** Nodes contribute only presence data (MAC, IP, vendor from scans). All device configuration — name, alerts, notes, group — should be set on the hub. Node-side values for those fields are ignored.

Use this mode when you want the hub to behave as a fully independent instance — it receives presence data from nodes but manages its own device configuration.

```
First sync: NEWDEV defaults applied
Next syncs: empty fields updated only via scan pipeline
User edits: set and maintained on the hub — never overwritten
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language tags to fenced code blocks.

The three example blocks should use an explicit language (e.g., text) to satisfy markdown lint rules.

Suggested patch
-```
+```text
 First sync:  INSERT with node's full config
 Next syncs:  empty fields updated only (name, vendor) via scan pipeline
 User edits:  protected — never overwritten

@@
- +text
First sync: UPSERT with node config
Next syncs: UPSERT — node values win (all fields, no exceptions)
User edits: overwritten on next sync

@@
-```
+```text
First sync:  NEWDEV defaults applied
Next syncs:  empty fields updated only via scan pipeline
User edits:  set and maintained on the hub — never overwritten
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 90-90: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

[warning] 102-102: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

[warning] 114-114: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @front/plugins/sync/README.md around lines 90 - 118, Update the three fenced
code blocks in front/plugins/sync/README.md to include an explicit language tag
(use "text") so markdown linting passes; specifically, change the first block
containing "First sync: INSERT with node's full config" to start with ```text,
the second block containing "First sync: UPSERT with node config" to start with

to start with ```text.

Comment thread front/plugins/sync/sync.py
Comment thread test/plugins/test_sync_protocol.py Outdated
Comment thread test/plugins/test_sync_protocol.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
front/plugins/sync/README.md (1)

157-182: ⚡ Quick win

Add the standard plugin ### Other info footer section.

This README is missing the standard metadata footer used across plugin docs.

📄 Proposed patch
 ---
 
 #### How to Set It Up
@@
 7. Check **Sync Devices**.
 
+### Other info
+
+- Version: 0.1
+- Author: [jokob-sk](https://github.com/jokob-sk)
+- Release Date: 26-May-2026

Based on learnings: For each front/plugins/**/README.md, include a ### Other info section with standardized fields and labels.

🤖 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 `@front/plugins/sync/README.md` around lines 157 - 182, The README's missing
the standard plugin metadata footer—append a "### Other info" section at the end
of the document (after the "How to Set It Up" content) and populate it with the
standardized fields used across plugin docs: e.g., "Supported versions",
"Maintainers", "License", "Configuration keys", and "Troubleshooting" (use the
exact heading "### Other info" and consistent labels for each field so it
matches other front/plugins/**/README.md files).
🤖 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 `@front/plugins/sync/README.md`:
- Around line 157-182: The README's missing the standard plugin metadata
footer—append a "### Other info" section at the end of the document (after the
"How to Set It Up" content) and populate it with the standardized fields used
across plugin docs: e.g., "Supported versions", "Maintainers", "License",
"Configuration keys", and "Troubleshooting" (use the exact heading "### Other
info" and consistent labels for each field so it matches other
front/plugins/**/README.md files).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f2641fb-d40d-415a-b0a2-92cb284027f7

📥 Commits

Reviewing files that changed from the base of the PR and between f8f7ac3 and 5ea999f.

📒 Files selected for processing (3)
  • front/plugins/sync/README.md
  • front/plugins/sync/sync.py
  • test/plugins/test_sync_protocol.py

@jokob-sk jokob-sk changed the title Enhance node name extraction logic to accurately classify file format… SYNC plugin improvements - skipping non-reachable nodes, SYNC_BEHAVIOR May 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
front/plugins/sync/sync.py (2)

294-337: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate SYNC_BEHAVIOR before branching.

Any value other than copy-new, carbon-copy, or hub-defaults currently falls into an undocumented path: Line 314 processes all devices, then Lines 323-337 use INSERT OR IGNORE. That silently creates a fourth behavior instead of honoring the config contract or falling back to the default.

Suggested fix
             sync_behavior = get_setting_value('SYNC_BEHAVIOR') or 'copy-new'
+            valid_behaviors = {'copy-new', 'carbon-copy', 'hub-defaults'}
+            if sync_behavior not in valid_behaviors:
+                mylog('none', [f'[{pluginName}] Invalid SYNC_BEHAVIOR "{sync_behavior}", defaulting to "copy-new"'])
+                sync_behavior = 'copy-new'
             mylog('verbose', [f'[{pluginName}] SYNC_BEHAVIOR: "{sync_behavior}"'])
🤖 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 `@front/plugins/sync/sync.py` around lines 294 - 337, sync_behavior returned by
get_setting_value can be any string and unrecognized values silently fall into
the "else" path (treated like copy-new) causing an undocumented fourth behavior;
before the existing branching (around the use of sync_behavior,
devices_to_write, and the carbon-copy/insert logic), validate sync_behavior
against the allowed set {'copy-new','carbon-copy','hub-defaults'} and if it's
not one of those, log a warning via mylog (include pluginName and the bad value)
and set sync_behavior = 'copy-new' (or otherwise explicitly enforce a known
default) so the subsequent branches (hub-defaults check, carbon-copy handling,
and INSERT OR IGNORE) only execute for known modes.

137-149: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard base64.b64decode in hub PULL so one bad node can’t abort the loop.

In front/plugins/sync/sync.py, decoded_data = base64.b64decode(data_base64) is unguarded; invalid/incorrectly padded (or non-string/None) data_base64 will raise and stop processing for remaining pull_nodes. Decode errors should be caught and that node skipped.

Suggested fix
-            # Extract node_name and base64 data
-            node_name = response_json.get('node_name', 'unknown_node')
-            data_base64 = response_json.get('data_base64', '')
-
-            # Decode base64 data
-            decoded_data = base64.b64decode(data_base64)
+            # Extract node_name and base64 data
+            node_name = response_json.get('node_name', 'unknown_node')
+            data_base64 = response_json.get('data_base64', '')
+
+            try:
+                decoded_data = base64.b64decode(data_base64)
+            except Exception:
+                mylog('none', [f'[{pluginName}] Skipping node "{node_url}" due to invalid payload encoding'])
+                continue
🤖 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 `@front/plugins/sync/sync.py` around lines 137 - 149, Wrap the base64 decoding
in a try/except around base64.b64decode(data_base64) (inside the code handling
the hub PULL loop that processes each node) so a malformed or non-string
data_base64 for a given node_name does not raise and abort the loop; catch
binascii.Error, ValueError and TypeError, log the failure (including node_name
and the problematic data_base64) and skip writing the log_file_name to LOG_PATH
for that node, allowing the loop to continue to the next pull node.
🤖 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.

Outside diff comments:
In `@front/plugins/sync/sync.py`:
- Around line 294-337: sync_behavior returned by get_setting_value can be any
string and unrecognized values silently fall into the "else" path (treated like
copy-new) causing an undocumented fourth behavior; before the existing branching
(around the use of sync_behavior, devices_to_write, and the carbon-copy/insert
logic), validate sync_behavior against the allowed set
{'copy-new','carbon-copy','hub-defaults'} and if it's not one of those, log a
warning via mylog (include pluginName and the bad value) and set sync_behavior =
'copy-new' (or otherwise explicitly enforce a known default) so the subsequent
branches (hub-defaults check, carbon-copy handling, and INSERT OR IGNORE) only
execute for known modes.
- Around line 137-149: Wrap the base64 decoding in a try/except around
base64.b64decode(data_base64) (inside the code handling the hub PULL loop that
processes each node) so a malformed or non-string data_base64 for a given
node_name does not raise and abort the loop; catch binascii.Error, ValueError
and TypeError, log the failure (including node_name and the problematic
data_base64) and skip writing the log_file_name to LOG_PATH for that node,
allowing the loop to continue to the next pull node.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ff23f107-5698-465a-9f8c-b2872782269a

📥 Commits

Reviewing files that changed from the base of the PR and between 5ea999f and cfea848.

📒 Files selected for processing (1)
  • front/plugins/sync/sync.py

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