Adopt factory-default devices without Winbox
A brand-new MikroTik is visible the instant it is plugged in — it announces
itself over MNDP and every managed neighbour reports it — but it could not
be adopted. Factory configuration puts it on 192.168.88.1/24 with no DHCP
client and no default route, so it is on the wire but not on the network.
The only way in was Winbox over MAC.
The manager does not need a path of its own. A managed device on the same
broadcast domain already has one, and can be borrowed.
Established on a real CRS310-8G+2S+ running 7.21.4:
- the API service is already enabled on a factory device, so nothing
needs turning on; reachability is the only real blocker - a managed neighbour given a temporary address in the target's subnet
reaches it immediately (399us) - /tool/fetch on that neighbour can drive the target's REST API, giving
full read and write without an interactive session
So adoption borrows a neighbour, gives the device an address on the real
network, and hands the neighbour back untouched.
Four things this got wrong first, all corrected against hardware:
Credentials. Modern units ship with a unique password printed on the unit,
not a blank one. Assuming blank produced a 401 that looked exactly like a
broken auth mechanism. An isolation test settled it: known-good credentials
returned data, a deliberately wrong password reproduced the identical
error. The password must come from the operator and the API refuses to
default it.
Free addresses. Probing for one, 192.168.0.64 answered ping while absent
from ARP. Either check alone would have handed a switch a duplicate
address, so both are required.
Ordering. Operations are additive: the factory address stays until the new
one is proven, and is removed afterwards over the new path. A failure
halfway leaves the device exactly as reachable as it was rather than
stranded.
The route is load-bearing. The manager sits off-subnet, so an address
without a default route cannot reply. Discovered by removing the route
during a revert and losing the session mid-command -- the command had in
fact executed. validateTargetAddress refuses an address outside the jump
host's subnet for the same reason: it would strand the device on both
networks at once.
The jump host is left as found on every path out of adopt(), and anything
temporary is commented so cleanupOrphans() can sweep what a crash left.
Verified end to end through the HTTP API, not just the internals: device
reverted to factory state, adopted via POST /api/adoption/adopt, all eleven
steps green, then confirmed on hardware -- CRS310 online and polling at
192.168.0.60, identity set, factory address gone, jump host holding only
its original address, and the fleet-wide orphan sweep finding nothing.
26 unit tests over the pure logic, fixtures taken from the real neighbour
rows this device produced.
Not pushed: needs testing on the local instance first.
Container image: ghcr.io/2gt-media-group-llc/mikrotik-manager:0.24.14-beta
Documentation: https://2gt-media-group-llc.github.io/mikrotik-manager/