What's new

MerVLAN v0.52.1 (dev 0.52.7) Simple and Powerful VLAN Management **BETA**

First i have some questions:
Did you get an "unsupported" popup when reloading in 0.52.5?

The new feature affecting you makes easier to track these kind of bugs from now on. The ui bug should be fixed now and this is probably a profile problem.

You can test this easily. (in 0.52.5)

Run
Code:
nano /tmp/var/wwwext/mervlan/settings/settings.json

Scroll down to the hardware section.
First check if it says:
"PRODUCTID": "Unsupported"
Then test to change MAX LAN to something else, like 8.
Press ctrl+x, y, then enter to save.

Then reload the UI and see if this changes the amount of ports.

If so, this is only that the model wasnt loaded and an easy fix.

Also, everytime you update the addon a backup is made. You can access and restore these by either running:

These two commands:
Code:
cd /jffs/addons/mervlan/functions

./update_mervlan restore
Or this command:
Code:
(sh /jffs/addons/mervlan/functions/update_mervlan.sh restore)
Make sure to include the ( ) if you use the oneliner.

Just follow the instructions and choose one of up to three saved backups.
@r80xcore

Hi!

Good guess, effectively, i attempted an update from my franken v0.52.4 from the gui and get back to v0.52.5.

As you guessed, there was someting wrong in the hardware detection:

Code:
"Hardware": {
"_description": "Detected hardware profile (auto-generated by hw_probe.sh; do not edit)",
"MODEL": "UNSUPPORTED",
"PRODUCTID": "RT-AC86U",
"MAX_SSIDS": "3",
"RADIOS": ["2.4", "5g-1"],
"GUEST_SLOTS": "3",
"ETH_PORTS": ["eth1", "eth2", "eth3"],
"LAN_PORT_LABELS": ["LAN1", "LAN2", "LAN3"],
"WAN_IF": "eth0",
"MAX_ETH_PORTS": "3"
}

This effectively makes the gui having just 3 ssids and 3 lan ports

Previously with v0.52.4, i had someting like this:

Code:
"Hardware": {
"_description": "Detected hardware profile (auto-generated by hw_probe.sh; do not edit)",
"MODEL": "AC86U",
"PRODUCTID": "RT-AC86U",
"MAX_SSIDS": "8",
"RADIOS": ["2.4", "5g-1"],
"GUEST_SLOTS": "3",
"ETH_PORTS": ["eth4", "eth3", "eth2", "eth1"],
"LAN_PORT_LABELS": ["LAN1", "LAN2", "LAN3", "LAN4"],
"WAN_IF": "eth0",
"MAX_ETH_PORTS": "4"
}

It looks like v0.52.5 cannot detect my router correctly anymore, like the previous version can...
I have just manually edited the hardware version with what was detected in previous version and after a reboot, the patched v0.52.5 now displays everything correctly.
It looks basically that it works, but v0.52.5 has some additionnal glitches i didn't get with v0.52.4 (at least maybe i need to "torture" v0.52.4 more). The timing with the save/apply vlan operations is much slowlier now than before (guessing of multiple ap support, but i am only editing in the 1st one single acces point setup). And sometimes there seems to have 2 page refresh after and operation which could be very delayed from each others. I got things like the vlan for a lan port was 222, just changed it to 333, save it. Waits a while for the refresh, apply the vlan. Did not change, and a refresh later, the gui revert to the previous vlan port by itself 222. (and brctl show previous vlan config) So i still think v0.52.4 is more stable. But i will continue testing the previous version to make sure that was not a coincidence...

FYI: i never got any unsupported popup at all (at least i did not see it if it was there)...
 
@r80xcore

Hi!

Good guess, effectively, i attempted an update from my franken v0.52.4 from the gui and get back to v0.52.5.

As you guessed, there was someting wrong in the hardware detection:

Code:
"Hardware": {
"_description": "Detected hardware profile (auto-generated by hw_probe.sh; do not edit)",
"MODEL": "UNSUPPORTED",
"PRODUCTID": "RT-AC86U",
"MAX_SSIDS": "3",
"RADIOS": ["2.4", "5g-1"],
"GUEST_SLOTS": "3",
"ETH_PORTS": ["eth1", "eth2", "eth3"],
"LAN_PORT_LABELS": ["LAN1", "LAN2", "LAN3"],
"WAN_IF": "eth0",
"MAX_ETH_PORTS": "3"
}

This effectively makes the gui having just 3 ssids and 3 lan ports

Previously with v0.52.4, i had someting like this:

Code:
"Hardware": {
"_description": "Detected hardware profile (auto-generated by hw_probe.sh; do not edit)",
"MODEL": "AC86U",
"PRODUCTID": "RT-AC86U",
"MAX_SSIDS": "8",
"RADIOS": ["2.4", "5g-1"],
"GUEST_SLOTS": "3",
"ETH_PORTS": ["eth4", "eth3", "eth2", "eth1"],
"LAN_PORT_LABELS": ["LAN1", "LAN2", "LAN3", "LAN4"],
"WAN_IF": "eth0",
"MAX_ETH_PORTS": "4"
}

It looks like v0.52.5 cannot detect my router correctly anymore, like the previous version can...
I have just manually edited the hardware version with what was detected in previous version and after a reboot, the patched v0.52.5 now displays everything correctly.
It looks basically that it works, but v0.52.5 has some additionnal glitches i didn't get with v0.52.4 (at least maybe i need to "torture" v0.52.4 more). The timing with the save/apply vlan operations is much slowlier now than before (guessing of multiple ap support, but i am only editing in the 1st one single acces point setup). And sometimes there seems to have 2 page refresh after and operation which could be very delayed from each others. I got things like the vlan for a lan port was 222, just changed it to 333, save it. Waits a while for the refresh, apply the vlan. Did not change, and a refresh later, the gui revert to the previous vlan port by itself 222. (and brctl show previous vlan config) So i still think v0.52.4 is more stable. But i will continue testing the previous version to make sure that was not a coincidence...

FYI: i never got any unsupported popup at all (at least i did not see it if it was there)...
I cant thank you enough for testing this and trying to debug it. It really helps me!

I have found one of the problem and its coming from the actual update. When i wrote this (quite long ago) the settings.json was quite barebone. Now when the addon has expanded it contains a lot more sections and keys. The update copies the json and then restores it afterwards. That wasnt a problem before but is now as it essentially removes key needed for the addon to function properly.

Im currently working on making the update function merge the keys instead.
After this i am going to take a look at why your model reports as unsupported. Though this is good in a way, as it meant that we actually detect a unsupported model, i must have missed a character or something in your model, making it miss it.

I'll keep you posted!
 
@r80xcore

"It looks basically that it works, but v0.52.5 has some additionnal glitches i didn't get with v0.52.4 (at least maybe i need to "torture" v0.52.4 more)."

After reverting with my v0.52.4 backup using your procedure (much more easier to do this way!) i tortured it a lot more. Yes it has a couple similar glitches than v0.52.5 but maybe not so easy to get them (because of the dynamic changes in the newer version to deal will mult AP)? As v0.52.4 looks more responsive to operations than v0.52.5 (illusion?)

Thanks
 
@r80xcore
Once fully uninstalled, is there any way to reinstall offline using a locally backup of a specific version (i assume there is one). I did not inspect all of your scripts, i just ask if i could simply extract a couple of scripts from the backup, and then force a full install from that backup (after completly restarting the router from zero and default configuration, after any problematic situation with the router), reinstalling offline would be usefull.

Thanks
 
@r80xcore
Hi!

By testing with v0.52.4 (i guess it could be similar with the newer one). If you create a vlan, apply it on the network manually, reboot etc, nothing looks strange. But once you start to enable the service so it automatically configures on next reboot, this will cause some side effect to the web interface (there seems not to be there when the service is not enable and you play with it manually for the current active session only): after a few minutes (i didn't get a chrono to measure exactly how many), the router web gui interface is forced to logoff of its current active session (even if i set the gui to automatically logoff only after a long period, 30mn for example)! As soon as i disable the service from mervlan, this thing never appears again (so it should be triggered with something related to the service activity)...

I don't understand yet using "Persistent: On" option: what will this change in mervlan operations (except that i understand that this will save some information in the router nvram)?

Thanks
 
@r80xcore
Hi!

By testing with v0.52.4 (i guess it could be similar with the newer one). If you create a vlan, apply it on the network manually, reboot etc, nothing looks strange. But once you start to enable the service so it automatically configures on next reboot, this will cause some side effect to the web interface (there seems not to be there when the service is not enable and you play with it manually for the current active session only): after a few minutes (i didn't get a chrono to measure exactly how many), the router web gui interface is forced to logoff of its current active session (even if i set the gui to automatically logoff only after a long period, 30mn for example)! As soon as i disable the service from mervlan, this thing never appears again (so it should be triggered with something related to the service activity)...

I don't understand yet using "Persistent: On" option: what will this change in mervlan operations (except that i understand that this will save some information in the router nvram)?

Thanks
Right now Persistent doesn't do anything. If you set it to "on" the mervlan_manager.sh will translate that off. There is code for using this for AP and some other stuff but after much testing it turned out to pose problems on some users devices. I chose to ditch it (temporarily at least) until I have the time to test it more thoroughly. At the current state, the health-service takes care of any drops anyway. I have not decided yet if i want to venture down the road on that feature or skip it.
@r80xcore
Once fully uninstalled, is there any way to reinstall offline using a locally backup of a specific version (i assume there is one). I did not inspect all of your scripts, i just ask if i could simply extract a couple of scripts from the backup, and then force a full install from that backup (after completly restarting the router from zero and default configuration, after any problematic situation with the router), reinstalling offline would be usefull.

Thanks
No, not right now. A full uninstall will and should nuke mervlan from the router.
@r80xcore

"It looks basically that it works, but v0.52.5 has some additionnal glitches i didn't get with v0.52.4 (at least maybe i need to "torture" v0.52.4 more)."

After reverting with my v0.52.4 backup using your procedure (much more easier to do this way!) i tortured it a lot more. Yes it has a couple similar glitches than v0.52.5 but maybe not so easy to get them (because of the dynamic changes in the newer version to deal will mult AP)? As v0.52.4 looks more responsive to operations than v0.52.5 (illusion?)

Thanks
I tracked down the reason for your LAN and SSIDs disappearing and it turned out that i had accidentally removed the profile for your model. This explain why you have had that "bug" (my bad). It will be back in the next update.

I have also tracked down some other bugs connected to the updater and have patched it to merge the settings.json instead of copy it. This preservers the settings while also allowing the json to gradually grow. For the next update i will provide a transition command that will update the router to 0.52.6 without trashing the json. After that the updates should work as intended. The missing json values is probably the reason for you buggy UI. The UI is heavily json driven.

The next update will focus a lot on the SSH part of the addon. It will centralize all SSH commands and also have a much sturdier build around that to make sure the addon doesn't stall if a node/AP is offline or improperly configured. What this means is that future changes to the SSH functions can be changed on one place instead of in all scripts.
The execute_nodes.sh will also use parallel execution with a local guard on the nodes/AP to tell the main when to continue.
 
@mistermoonlight1
mervlan v0.52.6
IMPORTANT: TRANSITION UPDATE FOR EXISTING INSTALLATIONS
========================================================
If you are updating from v0.52.5 or earlier, you MUST use one of the
following commands to update. This is required because the update process
now includes settings.json migration logic that older versions do not have.

DO NOT use the GUI update button for this version. Run one of these in SSH:

OPTION 1 - Download dependencies first, then update (recommended):
Code:
/usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/dev/settings/lib_json.sh" -o "/jffs/addons/mervlan/settings/lib_json.sh" && \
/usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/dev/settings/lib_ssh.sh" -o "/jffs/addons/mervlan/settings/lib_ssh.sh" && \
/usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/dev/settings/var_settings.sh" -o "/jffs/addons/mervlan/settings/var_settings.sh" && \
/usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/dev/settings/log_settings.sh" -o "/jffs/addons/mervlan/settings/log_settings.sh" && \
/usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/dev/functions/update_mervlan.sh" -o "/jffs/addons/mervlan/functions/update_mervlan.sh" && \
chmod 0644 /jffs/addons/mervlan/settings/lib_json.sh /jffs/addons/mervlan/settings/lib_ssh.sh /jffs/addons/mervlan/settings/var_settings.sh /jffs/addons/mervlan/settings/log_settings.sh && \
chmod 0755 /jffs/addons/mervlan/functions/update_mervlan.sh && \
/jffs/addons/mervlan/functions/update_mervlan.sh update dev
OPTION 2 - Run update twice (if OPTION 1 should fail, second run uses new updater):
Code:
/jffs/addons/mervlan/functions/update_mervlan.sh update dev && /jffs/addons/mervlan/functions/update_mervlan.sh update dev
The first run downloads the new files, the second run uses the new updater
which properly migrates your settings.json to the new format.

After this update, future updates can use the normal GUI update button.

If you used the GUI update button to update to the newest dev update, manually update via the CLI once.
Code:
/jffs/addons/mervlan/functions/update_mervlan.sh update dev
========================================================
This update focuses on stability improvements, fixing event loop issues,
adding interface detection for the VLAN client viewer, improving the
SSH wrapper for more reliable node communication, save verification
reliability, UI layout fixes, and proper hardware value detection.

This update also addresses the intermittent auto-logout issue that could
occur after saving settings. The fix ensures save actions use a sandboxed
iframe target that bypasses ASUS firmware's automatic page refresh behavior.


UPDATED FILES:

index.html
- Added interface badges in VLAN client viewer showing SSIDs, access ports, and trunks.
- Updated clients panel to expand and fill available viewport space.
- Added friendly node name display using aliases (e.g., "Living Room AP (192.168.1.100)").
- Replaced fixed 5500ms save verification timer with intelligent polling.
- New waitForSettingsToMatch() polls settings.json until managed keys match expected values.
- Added no-cache fetch headers (cache: "no-store", Cache-Control, Pragma) to prevent stale reads.
- Added helper functions: normalizeToFlatSettings, extractManagedKeys, shallowEqual, fetchSettingsObjNoStore.
- loadSettings() now calls refreshHwSettingsFromCache() to detect hardware value changes.
- If MAX_SSIDS, MAX_ETH_PORTS, or node MAX_ETH_PORTS changed, tables regenerate automatically.
- Moved trunk toggle checkbox to appear after "LAN X" label (prevents overlap with PRODUCTID badge).
- LAN row template now orders: PRODUCTID badge → LAN label → Trunk toggle.

vlan_index_style.css
- Added interface badge styles (.iface-ssid, .iface-access, .iface-trunk, etc.).
- Updated cli-shell and clients-panel to support dynamic height expansion.

vlan_form_style.css
- Trunk toggle now positioned on right side of LAN label cell (right:0 instead of left:0).
- Added padding-right to .lan-label-wrap to accommodate right-aligned trunk toggle.
- Changed .lan-trunk-inline to flex-direction:column for vertical stacking of T/U fields.
- Trunk config now expands row height instead of overflowing horizontally.

mervlan.asp
- Added loading overlay guard to prevent ASUS firmware from dismissing loading screen early.
- New window._mvmHoldLoadingFor(ms) blocks hideLoading() calls until minimum time passes.
- Fixed showLoadingSafe() to pass duration hint (seconds) to ASUS showLoading().
- MVM_exec now calls _mvmHoldLoadingFor() before showing loading overlay.
- Added save_vlanmgr, executenodes_vlanmgr, executenodesonly_vlanmgr to MVM_NO_REFRESH set.

collect_clients.sh
- Fixed background job wait logic that could fail on BusyBox non-interactive shells.
- Replaced unreliable 'jobs -p' with /proc-based PID tracking for parallel node collection.
- Added explicit wait calls to collect exit codes from background jobs.

collect_local_clients.sh
- Added interface detection for each VLAN bridge (SSIDs, access ports, trunk ports).
- New functions to classify interfaces and detect trunk port configurations.
- Filters out internal switch fabric interfaces (eth0) from display.
- Added cleanup trap for temporary files.

execute_nodes.sh
- Migrated all SSH calls to use the new merv_ssh_exec wrapper with retries and timeouts.
- Added parallel execution of VLAN manager on all nodes and main router.
- Added completion marker verification to confirm node execution succeeded.
- Separated collection phase to run after all nodes complete.

heal_event.sh
- Removed httpd from healable events to prevent GUI logoff loops.
- Added self-restart marker check with expiry timestamp to prevent event loops.
- Changed mismatch detection to require persistence across 3 checks (10s window).

hw_probe.sh
- Fixed model naming to use full product IDs (RT-AX86U instead of AX86U).
- Added RT-AC86U support entry.

mervlan_boot.sh
- Migrated all SSH calls to use merv_ssh_exec wrapper.
- Updated node IP parsing to return node_id and ip pairs.
- Improved logging with node ID prefixes.

mervlan_manager.sh
- Added --no-collect flag for parallel execution mode.
- Added node detection and completion marker creation for nodes.
- Added stale self-restart marker cleanup at startup.
- Creates self-restart marker before service restarts to prevent heal loops.

service-event-handler.sh
- Excluded httpd events from wildcard patterns to prevent event floods.
- Added secondary filter to catch httpd events that slip through *restart* pattern.
- Added handler-level debounce (5s) for system events.

sync_nodes.sh
- Migrated all SSH calls to use merv_ssh_exec wrapper with retries and timeouts.
- Improved logging with node ID prefixes (NODE1, NODE2, etc.).
- Fixed enable_jffs_and_reboot to handle connection drop during reboot.

update_mervlan.sh
- Added settings.json merge helper to preserve user values during updates.
- Skips Hardware section during merge to allow fresh hardware detection.
- Improved remote cleanup with proper SSH wrapper usage.

save_settings.sh
- Now reads MAX_ETH_PORTS from nested Hardware section using json_get_hw_int().
- Fixed MAX_ETH_PORTS variable assignment so it's properly set before logging.
- MAX_TRUNK_PORTS correctly inherits from MAX_ETH_PORTS when not explicitly set.

lib_json.sh
- Added json_set_section_value() for updating keys inside nested sections.
- Added json_set_section2_value() for two-level nested section updates.
- Added json_get_hw_value() for reading keys from nested Hardware section.
- Added json_get_hw_int() for reading integer values from Hardware section with validation.
- Both new getters use json_extract_hardware_section() to parse nested structure.

lib_ssh.sh
- Added comprehensive SSH wrapper functions with 3 retries and hard timeouts.
- New merv_ssh_exec() handles precheck, retries, timeout, and error classification.
- New merv_ssh_precheck() validates IP, keys, and ping before SSH attempt.
- New merv_ssh_test() and merv_ssh_skip_log() helper functions.
- Added _merv_timeout_run() for BusyBox timeout support.
 
@r80xcore
Hi!

I made some tests with the dev branch,1 commit after v0.52.6

1-It is now repaired and my AP (RT-AC86U) is now recognized by the new script

2-I have made some tests with enabling the service feature, and i can still get a web interface logoff (even if my web interface timeout is every long). Every few minutes, when vlanmgr is running. In the router system log, i can see something like this (part of it at the end before the logoff):

Code:
Feb  2 22:10:02 kernel: device eth6 left promiscuous mode

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered disabled state

Feb  2 22:10:02 kernel: device eth6 entered promiscuous mode

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered forwarding state

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered forwarding state

Feb  2 22:10:03 rc_service: service 20214:notify_rc restart_httpd

Feb  2 22:10:03 custom_script: Running /jffs/scripts/service-event (args: restart httpd)

Feb  2 22:10:03 VLANMgr: handler: RAW='restart_httpd' TYPE='restart' EVENT='httpd' (args: 'restart' 'httpd' '')

Feb  2 22:10:03 VLANMgr: handler: skipping httpd event restart_httpd (excluded)
Feb  2 22:10:03 RT-AC86U: start https:xxx
Feb  2 22:10:03 RT-AC86U: start httpd:xxx

Exactly at the end of this trace (the trace with the "skipping httpd", where it ends with an exit 0 in your event script), the router log off (i guess).

3-i have made an experiment and locally add 2 more install modes to your script with the purpose to split in 2 different steps the download and the full install process. This way i can fully install/uninstall and fully reinstall the same version the following days from scratch (from clean) by using a local copy of the tar file, without suffering from a new commit in the dev branch when tested from the dev branch (like install from a local snapshot copy of the main or dev branch). Something like this:

Code:
# Typical split installation downloaded from web dev branch head (for test purpose)

mkdir -p /jffs/addons/mervlan && /usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/refs/heads/dev/install.sh" -o "/jffs/addons/mervlan/install.sh" && chmod 0755 /jffs/addons/mervlan/install.sh && /jffs/addons/mervlan/install.sh dev downloadonly

# the tar file in /tmp/mervlan_tmp can be locally copied and recreate there later...

# then when there is a need to do a full install from a local tar copy without downloading anything

/jffs/addons/mervlan/install.sh installonly



Attached the modified "install.sh" (with rename txt extension to allow attachement):

Thanks
 

Attachments

@r80xcore
Hi!

I made some tests with the dev branch,1 commit after v0.52.6

1-It is now repaired and my AP (RT-AC86U) is now recognized by the new script

2-I have made some tests with enabling the service feature, and i can still get a web interface logoff (even if my web interface timeout is every long). Every few minutes, when vlanmgr is running. In the router system log, i can see something like this (part of it at the end before the logoff):

Code:
Feb  2 22:10:02 kernel: device eth6 left promiscuous mode

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered disabled state

Feb  2 22:10:02 kernel: device eth6 entered promiscuous mode

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered forwarding state

Feb  2 22:10:02 kernel: br0: port 6(eth6) entered forwarding state

Feb  2 22:10:03 rc_service: service 20214:notify_rc restart_httpd

Feb  2 22:10:03 custom_script: Running /jffs/scripts/service-event (args: restart httpd)

Feb  2 22:10:03 VLANMgr: handler: RAW='restart_httpd' TYPE='restart' EVENT='httpd' (args: 'restart' 'httpd' '')

Feb  2 22:10:03 VLANMgr: handler: skipping httpd event restart_httpd (excluded)
Feb  2 22:10:03 RT-AC86U: start https:xxx
Feb  2 22:10:03 RT-AC86U: start httpd:xxx

Exactly at the end of this trace (the trace with the "skipping httpd", where it ends with an exit 0 in your event script), the router log off (i guess).

3-i have made an experiment and locally add 2 more install modes to your script with the purpose to split in 2 different steps the download and the full install process. This way i can fully install/uninstall and fully reinstall the same version the following days from scratch (from clean) by using a local copy of the tar file, without suffering from a new commit in the dev branch when tested from the dev branch (like install from a local snapshot copy of the main or dev branch). Something like this:

Code:
# Typical split installation downloaded from web dev branch head (for test purpose)

mkdir -p /jffs/addons/mervlan && /usr/sbin/curl -fsL --retry 3 "https://raw.githubusercontent.com/r80xcore/mervlan/refs/heads/dev/install.sh" -o "/jffs/addons/mervlan/install.sh" && chmod 0755 /jffs/addons/mervlan/install.sh && /jffs/addons/mervlan/install.sh dev downloadonly

# the tar file in /tmp/mervlan_tmp can be locally copied and recreate there later...

# then when there is a need to do a full install from a local tar copy without downloading anything

/jffs/addons/mervlan/install.sh installonly



Attached the modified "install.sh" (with rename txt extension to allow attachement):

Thanks
Thank you, I will take a look at that tonight but right now I'm working on solving that logout bug once and for all. With the service enabled can you try to trigger a couple of logout events and after that run this command to capture those logs?
Code:
cat /tmp/syslog.log-1 /tmp/syslog.log | \
grep -Ei -C 5 'restart_httpd|rc_service.*httpd|httpd:' | \
tail -n 100

Could you also run these two and give me the output please:

Code:
cat /jffs/scripts/service-event

Code:
cat /jffs/scripts/services-start


All of this can be quite a lot of text so just paste it in a txt file and attach it here.

The thing is that I have had that problem a couple of times but not as much as you have. So we need to make sure to replicate it and record it so we can figure out exactly what triggers it differently for us. I have a solid idea of what I think it is and I just need to confirm it.

Thank you!
 
@mistermoonlight1

mervlan v0.52.7

This update resolves critical UI logout issues, enhances node sync reliability,
improves VLAN healing with faster monitoring and escalation logic, plus major
improvements to the installation system with interactive branch selection,
tarball management, and version tracking.

NOTABLE FIXES:

UI Logout Issue Resolution:
- Removed httpd restart from mervlan_manager.sh completely.
- Users can now save VLAN settings without being logged out.
- Web interface remains active throughout entire configuration process.

Node Sync Reliability:
- Fixed false "size mismatch" errors during settings.json sync to nodes.
- Improved verification focuses on JSON structure validity instead of byte count.
- Better Hardware section preservation prevents corruption of node-specific values.
- Eliminated stdin corruption issues with cleaner SSH communication patterns.

UPDATED FILES:

mervlan_manager.sh
- Removed httpd restart call to eliminate UI logout issues during VLAN saves.
- Previous behavior caused immediate logout after saving settings via web UI.
- VLAN changes now apply without disrupting active web sessions.
- GUI remains accessible throughout the entire save operation.

execute_nodes.sh
- Fixed settings.json sync verification that reported false size mismatches.
- Replaced byte-count comparison with semantic JSON structure validation.
- New verify_settings_conf_on_node() checks:
* File existence and non-empty content.
* Valid JSON structure (opening/closing braces).
* Minimum key count (ensures complete structure).
* Critical sections present (General, VLAN, Hardware).
- Improved JSON merge logic in copy_settings_conf_to_node():
* Preserves only Hardware section from node's existing settings.json.
* Main router settings copied cleanly to node (VLAN config, Pool, etc.).
* IS_NODE and NODE_ID flags set separately via set_node_flags_remote().
- Eliminated stdin piping through SSH that caused JSON corruption.
- Fixed multi-line JSON parsing issues with hardware values.
- More reliable node synchronization with proper error detection.
- Added automatic trunk configuration reset when pushing settings.json to nodes.
- Prevents trunk VLANs from being applied on nodes (main router only).
- All TRUNKx values set to "0", TAGGED/UNTAGGED_TRUNKx set to "none" on nodes.
- Ensures trunk capability remains exclusive to main router unit.

heal_event.sh
- Fixed syntax error (duplicate closing brace) in check_vlan_config().
- Fixed expected_vlans_from_settings() to properly parse trunk VLAN configurations.
- Now correctly reads comma-separated TAGGED_TRUNKx values (e.g., "187,188,189").
- Added trunk enable check - only reads VLAN config when TRUNKx is enabled (1 or positive).
- Prevents constant heal loops when trunk VLANs are configured.
- Each VLAN ID in trunk config is now properly extracted and validated.
- Fixed bridge_has_members() to detect trunk VLAN bridges correctly.
- Now recognizes eth1.X, eth2.X, etc. as valid edge members (trunk ports).
- Distinguishes between uplink (eth0/eth0.*) and LAN-side trunk interfaces.
- Trunk VLANs (e.g., br70, br100) now properly detected during heal checks.
- Optimized monitoring: 10 checks @ 3s intervals (27s window, down from 25s).
- Heal trigger: 6 seconds on persistent issues (3 consecutive mismatches).
- Implemented escalator pattern for cron events:
Phase 1: Fast sensor check (single-pass validation).
Phase 2: Full validation (10 passes over 27s) only if fast check fails.
- Prevents unnecessary 27s delays when VLANs are healthy during cron runs.
- Quick healing (6s) on confirmed persistent issues remains unchanged.
- Added --cron-test mode for testing full escalation cycle.
- Injects fake VLAN (9999) to trigger escalation from fast check to full validation.
- Allows testing of escalation logic without actual VLAN damage.
- Skips actual heal execution in test mode (shows what would happen).

sync_nodes.sh
- Added automatic trunk configuration reset when syncing settings.json to nodes.
- Detects settings.json during file copy and applies trunk reset transformation.
- Prevents trunk VLANs from being applied on nodes (main router only).
- All TRUNKx values set to "0", TAGGED/UNTAGGED_TRUNKx set to "none" on nodes.
- Ensures trunk capability remains exclusive to main router unit.
- Fallback to original file if trunk reset fails (with warning logged).

lib_json.sh
- Added json_reset_trunks_section() function for node trunk configuration safety.
Replaces entire Trunks section with default disabled values.
Uses same AWK-based pattern as json_replace_hardware_section() for consistency.
Called automatically by execute_nodes.sh and sync_nodes.sh when pushing settings.

mervlan_trunk.sh
- Fixed unterminated quoted string syntax errors (lines 210 and 536).
Removed corrupted leftover code from ensure_vlan_bridge() function.
Fixed multi-line printf statement that caused shell parsing errors.

index.html
- Fixed trunk UI visibility to properly hide on node tabs (Node1-5).
Trunk toggle and configuration now only appear on main router tab.
- Added CURRENT_LAN_TARGET check in updateExperimentalVisibility().
- Modified updateSingleTrunkRow() to force regular VLAN input on node tabs.
- Regular VLAN input (.lan-main-vlan) now always shows on node views.
Trunk configuration (.lan-trunk-config) now always hidden on node views.
- Ensures nodes never display trunk UI regardless of experimental mode state.
Prevents confusion by showing only standard VLAN inputs on node configuration.

install.sh
- Added interactive branch selection during download mode.
- Prompts user to choose between main (stable) or dev (development) branch.
- Automatically extracts version from changelog.txt inside downloaded tarball.
- Renames tarballs to mervlan-{branch}-{version}.tar.gz for easy identification.
- Added interactive tarball selection menu (similar to update_mervlan.sh restore UI).
- Shows all available mervlan-*.tar.gz files with branch, version, and size.
- Menu options: [1-N] select tarball, [d] delete tarball, [q] quit/abort.
- Delete functionality with confirmation prompt for removing unwanted downloads.
- Added support for './install.sh full dev' to install fresh from dev branch.
- Fixed EXIT trap cleanup in all early return paths (prevents "[: bad number" errors).
- Added TMP_DIR validation for download/tarball modes.
- Added SELECTED_TARBALL safety check before extraction.
- Enhanced mode documentation with comprehensive usage examples.
- All modes properly abortable with appropriate exit codes.

SUPPORTED INSTALL MODES:

./install.sh
- Standard upgrade install (uses existing /jffs/addons/mervlan).
- No download, no SSH credential prompts.

./install.sh full
- Fresh install from main branch.
- Creates directories, downloads, and installs.
- Prompts for SSH credentials.

./install.sh full dev
- Fresh install from dev branch.
- Creates directories, downloads, and installs.
- Shows development version warning.
- Prompts for SSH credentials.

./install.sh download
- Interactive download session.
- Prompts for branch selection (main/dev).
- Downloads and renames to mervlan-{branch}-{version}.tar.gz.
- Exits after download (does not install).

./install.sh tarball
- Interactive tarball selection menu.
- Shows all mervlan-*.tar.gz files in TMP_DIR.
- Allows selection, deletion, or abort (q).
- Prompts for SSH credentials if installing.
- Fully abortable at any confirmation prompt.
 
@r80xcore

Hi,

i am still actively testing v 0.52.6 and trying to get all the information you asked before i have just seen your new release not tested yet. But there was something weird that i have seen when looking at the logs (maybe it has nothing to do with the current problem?):

1-the script that is ran to collect local clients (collect_local_clients.sh) information does not find any of the 4 tools it wanted to use, but when i am manually calling all of these tools in an ssh terminal window, i can run all of them (the 4 tools exist in the system and can be called, so i don't know why the script cannot find them (some are inside busybox i guess))??

Code:
Feb  3 18:15:17 VLANMgr: collect_local_clients: PATH=/sbin:/bin:/usr/sbin:/usr/bin
Feb  3 18:15:17 VLANMgr: collect_local_clients: missing cmd ip
Feb  3 18:15:17 VLANMgr: collect_local_clients: missing cmd brctl
Feb  3 18:15:17 VLANMgr: collect_local_clients: missing cmd awk
Feb  3 18:15:17 VLANMgr: collect_local_clients: missing cmd grep

Thanks
 
Last edited:
@r80xcore

Hi!

The temptation to stop debugging with v0.52.6 and try v0.52.7 was too big, so i gave it a try...
Here are my preliminary results (from the dev branch):

1-The "collect_local_clients.sh" bug reported in previous post is still there, but maybe this bug is just visual traces in syslog and has no consequence at all...

and now with the good news...

2-I think i can safely say that the webui logoff bug is gone forever: i am still logged in on the web gui for 36mn since the router was rebooted with the service active (and the simple vlan i created for the test is still there)! I don't see any syslog traces of these "VLANMgr" events i got previoulsy before the webui logoff. Very good!

3-the improvement you made with the install process suggestion i made yesterday is much better than what i experimented myself. So now we can easily test with the dev branch and not being forced to download while testing each time.

I can say that i am impressed of all the changes you made in a short time. I won't turn back to any version before v0.52.7 as it is the first version on my system that can do simple things (create a single vlan) and looks "stable" without any glitch here and there.

I guess now i am at another level of playing with it for the 1st time, probably seeing maybe new problems of things i never experimented yet...

Very good job!

Thanks
 
@r80xcore

Hi!

Using v0.52.7. I got a new issue. There is an intermittent boot issue. While yesterday after booting, it was ok. Now with a new reboot, the vlan was not recreated. But if i check the service status, i got this (so service is enabled):

Code:
2026-02-04 19:59:34 [INFO] Status:
2026-02-04 19:59:34 [INFO] <--- Main Unit --->
2026-02-04 19:59:34 [INFO] RT-AC86U boot=enabled addon=active service-event=active cron=absent is_node=no

So i manually "Appy Vlan" after a while (10-15mn) and the vlan was reconfigured correctly.

After another reboot, vlan was still not enabled after booting.

So i disable the service, then enable it again then after another reboot, the vlan was recreated correctly this time.

When it is not working, in the gui, i can see the vlan configuration, status is enable, but when i look at the gui mervlan log, there was no active vlan. And i cannot see watchdog script checking vlan configuration i guess at that time in an attempt to repair it (but i didn't verify the cron job if it was there or not at that time)...

After multiple reboots, i can say that half the time it is working: 2 good reboots, 2 bad reboots. Investigating further, i can see that when i have a bad reboot, the watchdog health cron job is not installed at all!
And when there is a bad reboot, the log in mervlan is very short:

Code:
2023-12-31 19:00:26 [INFO] Installed service-event with MERV_BASE=/jffs/addons/mervlan (setup-only)
2023-12-31 19:00:27 [INFO] Skipping node propagation for 'setupenable' (MERV_SKIP_NODE_SYNC=1)
2026-02-04 20:51:19 [INFO] === VLAN Client Collection Started ===
2026-02-04 20:51:19 [INFO] Collecting clients from main router (JSON)...
2026-02-04 20:51:19 [INFO] Collecting VLAN clients (MAC-only) on Main Router
2026-02-04 20:51:19 [INFO] No active clients found on Main Router
2026-02-04 20:51:19 [INFO] ✓ Main router collection completed
2026-02-04 20:51:19 [INFO] No nodes configured in settings.json
2026-02-04 20:51:19 [INFO] Merging JSON results...
2026-02-04 20:51:19 [INFO] ✓ Client collection completed - JSON saved to /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:51:19 [INFO] === VLAN Client Collection Finished ===
2026-02-04 20:52:21 [INFO] === VLAN Client Collection Started ===
2026-02-04 20:52:21 [INFO] Cleared previous results at /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:52:21 [INFO] Collecting clients from main router (JSON)...
2026-02-04 20:52:22 [INFO] Collecting VLAN clients (MAC-only) on Main Router
2026-02-04 20:52:22 [INFO] No active clients found on Main Router
2026-02-04 20:52:22 [INFO] ✓ Main router collection completed
2026-02-04 20:52:22 [INFO] No nodes configured in settings.json
2026-02-04 20:52:22 [INFO] Merging JSON results...
2026-02-04 20:52:22 [INFO] ✓ Client collection completed - JSON saved to /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:52:22 [INFO] === VLAN Client Collection Finished ===

Quickly after i disable the service and reenable it, the watchdog cron job will install and detect the vlan is not configured and repairs it correctly. But it is a manual process (or i could also apply vlan manually)...

Thanks
 
Last edited:
@r80xcore

Hi!

Using v0.52.7. I got a new issue. There is an intermittent boot issue. While yesterday after booting, it was ok. Now with a new reboot, the vlan was not recreated. But if i check the service status, i got this (so service is enabled):

Code:
2026-02-04 19:59:34 [INFO] Status:
2026-02-04 19:59:34 [INFO] <--- Main Unit --->
2026-02-04 19:59:34 [INFO] RT-AC86U boot=enabled addon=active service-event=active cron=absent is_node=no

So i manually "Appy Vlan" after a while (10-15mn) and the vlan was reconfigured correctly.

After another reboot, vlan was still not enabled after booting.

So i disable the service, then enable it again then after another reboot, the vlan was recreated correctly this time.

When it is not working, in the gui, i can see the vlan configuration, status is enable, but when i look at the gui mervlan log, there was no active vlan. And i cannot see watchdog script checking vlan configuration i guess at that time in an attempt to repair it (but i didn't verify the cron job if it was there or not at that time)...

After multiple reboots, i can say that half the time it is working: 2 good reboots, 2 bad reboots. Investigating further, i can see that when i have a bad reboot, the watchdog health cron job is not installed at all!
And when there is a bad reboot, the log in mervlan is very short:

Code:
2023-12-31 19:00:26 [INFO] Installed service-event with MERV_BASE=/jffs/addons/mervlan (setup-only)
2023-12-31 19:00:27 [INFO] Skipping node propagation for 'setupenable' (MERV_SKIP_NODE_SYNC=1)
2026-02-04 20:51:19 [INFO] === VLAN Client Collection Started ===
2026-02-04 20:51:19 [INFO] Collecting clients from main router (JSON)...
2026-02-04 20:51:19 [INFO] Collecting VLAN clients (MAC-only) on Main Router
2026-02-04 20:51:19 [INFO] No active clients found on Main Router
2026-02-04 20:51:19 [INFO] ✓ Main router collection completed
2026-02-04 20:51:19 [INFO] No nodes configured in settings.json
2026-02-04 20:51:19 [INFO] Merging JSON results...
2026-02-04 20:51:19 [INFO] ✓ Client collection completed - JSON saved to /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:51:19 [INFO] === VLAN Client Collection Finished ===
2026-02-04 20:52:21 [INFO] === VLAN Client Collection Started ===
2026-02-04 20:52:21 [INFO] Cleared previous results at /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:52:21 [INFO] Collecting clients from main router (JSON)...
2026-02-04 20:52:22 [INFO] Collecting VLAN clients (MAC-only) on Main Router
2026-02-04 20:52:22 [INFO] No active clients found on Main Router
2026-02-04 20:52:22 [INFO] ✓ Main router collection completed
2026-02-04 20:52:22 [INFO] No nodes configured in settings.json
2026-02-04 20:52:22 [INFO] Merging JSON results...
2026-02-04 20:52:22 [INFO] ✓ Client collection completed - JSON saved to /tmp/mervlan_tmp/results/vlan_clients.json
2026-02-04 20:52:22 [INFO] === VLAN Client Collection Finished ===

Quickly after i disable the service and reenable it, the watchdog cron job will install and detect the vlan is not configured and repairs it correctly. But it is a manual process (or i could also apply vlan manually)...

Thanks
I have patched the missing cmd bug. Im currently patching the "missing on boot" bug. This is error that is grounded in multiple places but to make it short the router comes up and isnt finished, the manager runs but starts throwing errors. Because i havent guarded the services-starts commands, cron is never enabled and the vlans are not up until manually fixed.

Im currently fixing this by guarding the scripts, adding a boot mode to the manager, adding a filter feature that filters SSIDs based on what node it is on and adding that feature to the html too. This will enable the user to choose on what node the SSID is enable on, example:
We have a main and a node. On main we have SSID 1 and SSID 2, on the node we mesh SSID 1 but we dont have SSID 2, instead we have SSID 3. In the UI you can now choose what SSID resides where. (All defaults to main and is not configurable unless a node is present.)
This also enables us to fetch the right SSIDs so the script knows when the WiFi is ready for the manager.

The update should be ready in a couple of days. Have a little work and testing to do with it before i can push it.
 
@mistermoonlight1
mervlan v0.52.8

This update is mostly about increasing stability and giving a better visibility into what, when, and where things happen in thenetwork. By adding SSID/VLAN node filters and "boot-time gating," we are trying to to remove the guesswork of boot-up timing issues. We also included systemwide BusyBox fixes to to clean up old code that produced non-fatal but hard-to-debug log entries. While these look like new features—giving you per-node control in larger setups—their main job is to make the whole system more solid, predictable, and easier to monitor while at the same time piggyback on the SSID-filter.

This gives us two new buttons. On a single unit system this is non-configurable, yet still brings the stability improvements. But on a system with 1-5 nodes/AP's, this gives us the ability to choose on which unit the SSID lives and is allowed. Syncing this into the SSID-filter and boot-mode makes the booting much more predictable and stable. This also make the remote feature in MerVLAN much more robust as we can configure different SSID VLANs on different units, yet control everything from one unit.
1770414516658.png
1770414549961.png


NOTABLE FEATURES:

Node-Scoped SSID/VLAN Filtering:
- New lib_ssid_filter.sh library for per-node SSID slot assignment.
- Each SSID slot (1-12) can be assigned to MAIN and/or NODE1-NODE5.
- Format: "MAIN,NODE1,none,none,none,none" controls which nodes manage each SSID.
- Filter automatically respects NODE_ID on each device.
- Nodes only apply VLANs to SSIDs they are assigned to.
- Prevents VLAN misconfiguration on nodes that don't broadcast certain SSIDs.

Boot-Time SSID Gating:
- New boot_wait_for_configured_ssids() function in mervlan_manager.sh.
- Boot mode (invoked via "mervlan_manager.sh boot") waits up to 30 seconds.
- Validates configured SSIDs exist in nvram before proceeding.
- Logs warnings for SSIDs not found in nvram (likely typos).
- Only waits for SSIDs that are actually present on the router.
- Reduces VLAN apply failures caused by WiFi interfaces not being ready.

BusyBox Compatibility (merv_has/merv_cmd shim):
- Replaced all "command -v" calls which fail silently in BusyBox ash.
- New portable shim: merv_has() and merv_cmd() functions.
- Shim is guarded and defined in all library entry points.
- Functions use "type" builtin which is universally supported.
- merv_has() - Check if command/function/builtin exists (returns 0/1).
- merv_cmd() - Resolve external command's full path (like "which").
- Eliminates cryptic failures from command detection code.

UPDATED FILES:

lib_ssid_filter.sh (NEW)
- Core SSID filter library for multi-node deployments.
- ssid_filter_init() - Initialize filter based on NODE_ID.
- is_ssid_slot_allowed() - Check if SSID slot is assigned to current node.
- get_ssid_slot_value() - Get SSID value with filtering (returns placeholder if denied).
- get_vlan_slot_value() - Get VLAN ID with filtering (returns "none" if denied).
- get_apiso_slot_value() - Get AP isolation value with filtering.
- Reads SSIDassign_XX from WiFi.SSIDassign section in settings.json.
- Default assignment: MAIN-only for backwards compatibility.
- SSID_FILTER_FATAL flag for graceful abort when hardware profile not ready.

mervlan_manager.sh
- Added lib_ssid_filter.sh integration for filtered SSID/VLAN reads.
- All SSID, VLAN, and APISO reads now use filtered accessors.
- New boot mode with SSID readiness gating (boot_wait_for_configured_ssids).
- Boot mode validates SSIDs exist in nvram before waiting for interfaces.
- Logs warnings for typos/misconfigured SSIDs not found in wireless config.
- Added SSID_FILTER_FATAL checks with graceful abort on filter failures.

heal_event.sh
- Added lib_ssid_filter.sh integration for filtered VLAN reads.
- any_vlan_configured() now respects node assignment filter.
- expected_vlans_from_settings() uses filtered get_vlan_slot_value().
- Nodes only heal VLANs for SSIDs they are assigned to.
- Added SSID_FILTER_FATAL checks for graceful abort on filter failures.

var_settings.sh
- Added merv_has/merv_cmd shim for BusyBox compatibility.
- Shim is guarded to avoid redefinition if already present.

log_settings.sh
- Added merv_has/merv_cmd shim for BusyBox compatibility.
- Uses merv_has for logger availability check.

lib_debug.sh
- Added merv_has/merv_cmd shim for BusyBox compatibility.

lib_ssh.sh
- Added merv_has/merv_cmd shim for BusyBox compatibility.
- Uses merv_has for dbclient/ssh detection.
- Uses merv_has for json_set_flag/json_get_flag availability checks.

install.sh
- Made install a little more verbose

uninstall.sh
- Added merv_has/merv_cmd shim for BusyBox compatibility.
- Uses merv_has for JSON function and SSH client detection.
- Made uninstall a little more verbose.

mervlan_templates.sh
- Templates now invoke mervlan_manager.sh with "boot" argument.
- Enables boot-time SSID gating for VLAN configuration.
- Template: "mervlan_manager.sh boot >/dev/null 2>&1 || :"

settings.json
- New WiFi.SSIDassign section with SSIDassign_01 through SSIDassign_12.
- Each value is comma-separated: "MAIN,NODE1,NODE2,NODE3,NODE4,NODE5".
- "none" in any position means slot is not assigned to that node.
- Default: "MAIN,none,none,none,none,none" (MAIN only).

sync_nodes.sh
- Added lib_ssid_filter.sh to the copy & chmod lists

index.html
- Added SSID Node Assignment UI in assign view.
- Checkboxes for each node (M, 1, 2, 3, 4, 5) per SSID row.
- Checkboxes disabled for nodes without hardware detected.
- updateSSIDAssignCheckboxes() syncs checkbox state with settings.
- Settings serialization includes SSIDassign_XX in save payload.
- Fixed node LAN buttons and SSID assign checkboxes remaining disabled after
loading settings with configured node IPs.
- Buttons now enable when a valid IP is configured (hardware detection optional).
- Updated SSID assign tooltips to use node alias, IP, and PRODUCTID:
enabled: "Assign to {ALIAS}, {IP} ({PRODUCTID})"
disabled: "Configure {ALIAS} IP first"
- Added dynamic AP Isolation availability: select enables when both SSID and
VLAN ID are valid in the form (no save/load required).
- AP Isolation disabled tooltip: "Enter valid SSID and VLAN ID first".
- Added setupDisabledAssignClickGuard() to prevent checkbox toggling on
disabled labels while preserving tooltip hover.

TECHNICAL DETAILS:

Filter Token Matching:
- Node identity mapped to tokens: MAIN, NODE1, NODE2, NODE3, NODE4, NODE5.
- Token matching uses `,token,` wrapping to prevent partial matches.
- Example: ",MAIN,NODE1,none,none,none,none," matches MAIN and NODE1.

Filter Edge Cases:
- Empty/missing assignment defaults to MAIN-only.
- Explicit "none" string denies slot to all nodes.
- Non-numeric slot index returns denied.
- MAX_SSIDS < 1 sets SSID_FILTER_FATAL flag for graceful abort.

Boot Gating Logic:
- Partition SSIDs into: present in nvram (wait) vs. missing (warn only).
- Wait loop checks for interface availability with configurable timeout.
- Continues after timeout with warning (does not block boot indefinitely).
- Only active when invoked with "boot" argument.

BUGFIXES:

mervlan_boot.sh
- Fixed BusyBox flock compatibility: replaced "flock -w 5" (unsupported) with
"flock -x" in both copy_inject() and remove_inject() functions.
- BusyBox v1.25.1 only supports -s, -x, -u, -n flags; -w (wait timeout) caused
silent failures during setupenable and other template injection operations.

lib_json.sh
- Fixed json_set_section_value() to support upsert (insert if key missing).
- Previously only replaced existing keys; new keys like PRODUCTID_NODE1 and
MAX_ETH_PORTS_NODE1 written by sync_nodes.sh were silently discarded.
- Now inserts new key-value pairs before the section closing brace with proper
comma handling.
- Also handles numeric value replacement (not just quoted strings).

vlan_form_style.css
- Removed pointer-events:none from disabled SSID assign labels so tooltips
display on hover (matching LAN button disabled behavior).
- Added consistent disabled styling for AP Isolation select elements
(opacity, muted colors, cursor: not-allowed).
 
Last edited:

Latest threads

Support SNBForums w/ Amazon

If you'd like to support SNBForums, just use this link and buy anything on Amazon. Thanks!

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Back
Top