An Obsidian plugin that treats your home network as notes: one per device, one per VLAN or subnet. It reads their frontmatter and draws the topology, checks the addressing, maps the switch ports and generates DHCP, DNS and hosts configuration from the same data.
Nothing is stored in the plugin. Every note works with it switched off.
- Topology diagram built from each device's
uplink:— internet → router → switches → leaves, laid out so each parent sits over the middle of its children. Icons are the device type, colour is the segment, and a small tag on each link is the port (bold for a trunk). A second layout puts one band per VLAN with the uplinks still drawn, so the crossings show what sits where. - Device profiles — interfaces, segment, assignment, MAC, uplink, services, and for anything with ports a port map whose occupancy is derived from which devices claim which port, never stored twice. Two devices on one port is therefore detectable rather than invisible.
- Ports as hardware. Each port has a presentation (electrical or optical), a connector, capable
speeds and PoE, written in ranges the way a switch is actually specified —
ports: 1-8, speeds: 10/100/1000, connector: RJ45. Tap a port to edit it. The payoff is that the cable can be checked: optical into electrical is an error no cable fixes, two ports with no speed in common will not come up, and a 2.5G switch on a gigabit port is reported as a gigabit link — the thing people are surprised by. Copper is assumed to fall back down the BASE-T ladder; an optic is taken at its word, because a 10G SFP+ will not talk to a 1G one. - Address map per segment: every address tagged device / gateway / pool / reserved / free, with usage figures and the next free static address. Tap a free one to create a device on it.
- Health checks (an unmanaged switch is never a gap for having no address — it has none to
record; one whose
managed:nobody has written down still is), split by how much they matter: errors are things that cannot both be true (a duplicate address or MAC, an address outside its segment, two claims on one port — including a switch's own uplink port — a reused VLAN id, an uplink loop, a link joining optical to electrical or two ports with no speed in common); warnings are the documentation arguing with itself (a static address inside the DHCP pool, an uplink pointing at nothing, overlapping subnets, a VLAN configured on a switch marked unmanaged, a port described that the device does not have); gaps are what is simply not written down yet, and what that costs you — including a link quietly running slower than either end could. - Exports — dnsmasq reservations, dnsmasq
host-record(forward and reverse),/etc/hosts, a Markdown table, CSV. Each says what it left out and why, because a silently missing reservation is how a "static" address quietly moves one day.
It never touches the network: no scanning, no SSH, no calls out. It is a documentation tool that happens to be able to check its own documentation.
A device is any note tagged Network/Device (anywhere in the vault — the tag is the marker, the
folder is only where new ones are created). A note inside the devices folder also counts if it
carries an ip, mac or device field.
---
tags: [Network/Device]
device: l2-switch # picks the icon, and whether things can plug into it
managed: true # L2/L3 switches: an unmanaged one cannot carry a VLAN
status: active # active | planned | offline | retired
ip: 192.168.0.51/24 # a list is fine, and so is prose after the address
mac: 2c:cf:67:5b:70:26 # needed for a DHCP reservation
vlan: 20 # an explicit tag, which beats matching the address to a subnet
assign: static # static | dhcp | reserved
uplink: "[[SW1]]" # this is what draws the diagram
uplink_port: 3 # the far end: a port on SW1
local_port: 1 # the near end: a port on this device
ports: 8 # for anything others plug into
poe_ports: [1, 2, 3]
hostname: rasputin
location: Office
---Both ends of a link are recorded. Knowing a cable lands on SW1's port 24 does not tell you which
of SW2's own ports it left from — and that port is occupied on SW2 either way, so a switch's uplink
port used to look free and could be handed to something else. uplink_port is theirs, local_port
is ours, and the diagram labels the link 24⇄1.
Device types are router, firewall, l2-switch, l3-switch, ap, extender, server,
nas, sbc, vm, desktop, laptop, phone, tablet, tv, console, printer, camera,
iot and other, each with its own icon, and editable in settings. Written names are folded onto
them, most specific first: a plain switch is an l2-switch, "Cisco L3 switch" is an l3-switch,
"managed switch" and "access point" land where you would expect. managed: is a property rather
than a pair of types, because it is one bit of information and not a different kind of thing — and
it is three-state, since "nobody has said" must not be read as "it cannot do VLANs".
A VLAN or subnet is a note tagged Network/VLAN, with vlan (omit it for an untagged subnet —
which is what most home networks are), cidr, gateway, dhcp_range, reserved, colour and
purpose. Multi-homed devices can use an interfaces: list of mappings instead of the flat fields;
port_config: describes access and trunk ports.
Parsing is deliberately tolerant. These notes are written by hand and usually predate the plugin, so an address field like
IP:
- 192.168.0.51 /24 (wlan0, LAN, static — router direct, was 192.168.5.116 until 2026-08-03)yields the interface name, address, prefix and assignment, and keeps the rest of the sentence rather than dropping it. Refusing to read that would mean rewriting your notes to suit the code.
Two questions off one set of numbers: can this switch supply what is hanging off it, and what does the lot cost to leave running.
poe_budget: 100 # total watts it can hand out — the binding constraint
poe_max_per_port: 60 # ceiling for any one port
poe_standard: 802.3bt Type 3 (PoE++) # displayed, never parsed for maths
watts: 2.74 # its OWN draw, excluding the PoE it supplies
watts_max: 20poe_class: 6 # 0–8. Gives a conservative figure with nothing measured
poe_watts: 12.4 # a real reading, which beats the class maximum
poe_powered: true # implied anyway by either of the above
watts: 45 # mains-powered devices use this instead — from the datasheet
watts_estimated: 8.5 # no datasheet exists: labelled "an estimate" wherever it appears
watts_measured: 31.5 # a meter reading, which beats both
measured_on: 2026-10-03A PoE budget is metered at the switch, not at the device. An 802.3bt class 6 camera receives 51 W but reserves 60 W of budget — the difference is lost in the cable. The budget maths always uses the PSE-side figure, because using the device-side one invents headroom that is not there.
| Class | Standard | Reserved (PSE) | Delivered (PD) |
|---|---|---|---|
| 1 / 2 / 3 | 802.3af · PoE | 4 / 7 / 15.4 W | 3.84 / 6.49 / 12.95 W |
| 4 | 802.3at · PoE+ | 30 W | 25.5 W |
| 5 / 6 | 802.3bt Type 3 · PoE++ | 45 / 60 W | 40 / 51 W |
| 7 / 8 | 802.3bt Type 4 · PoE++ | 75 / 90 W | 62 / 71.3 W |
The office XMG-108HP has eight ports rated 60 W against a 100 W budget. Port capability and budget are different numbers, and it is the second that runs out — the Power tab says so in as many words.
Log reading on the Power tab writes the figure to watts_measured: (or poe_watts: for a
powered device), stamps measured_on:, and appends a dated line under a ## Power log heading in
the note. The body is only ever appended to.
Nothing is defaulted from the device type. A device with no wattage recorded is named in the running-cost section rather than assumed to draw zero, because an invented figure would flow straight into a yearly cost and read as a fact. Every figure carries where it came from — measured, from the datasheet, an estimate, PoE class maximum, or not recorded — in that order of precedence.
watts_estimated: exists because projection needs rough figures to work at all, and the only thing
worse than a guess is a guess that has stopped looking like one. The running-cost section reports
how many watts of its own total rest on estimates.
Health gains four checks: budget exceeded (error), a powered device on a port that supplies no
power (error), one device over the per-port limit (warn), and a PoE load with no figure at all
(info — it means the budget total is an understatement).
One view with six tabs (topology, devices, addresses, power, health, export), on the network ribbon
icon. One toolbar row: the tabs, and a + for a new device or VLAN. The tree / VLAN-bands switch sits
with the topology's zoom buttons, and a selected device's profile has ✎ to edit it; each gap on the
Health tab has Edit too, since most are one field to fill in. On a phone the device list and a
device's interfaces become cards rather than tables off the right edge. Commands: Open LAN, New
device, New VLAN or subnet, Check the network, and Mark this note as a device for a note you
already keep elsewhere.
The network is read once — one pass over the vault — and kept until a device or segment note changes; edits anywhere else no longer redraw the view, and each tab keeps its scroll position.
Settings cover the two marker tags, the two folders, the local domain used by the exports, the layout and spacing, whether to draw the internet, whether retired kit counts, and the device-type list — each type an id, a label, a Lucide icon name, and whether other devices can plug into it.
Not in the community directory. Either install from this repository with
BRAT, or copy main.js, manifest.json and
styles.css into <vault>/.obsidian/plugins/brewin-lan/ and enable it.
npm install
npm run dev # watch
npm run build # type-check, then bundle
npm test # 140 testsThe addressing, layout, health, export and viewport maths are pure modules with no Obsidian imports,
which is why they can be tested at all — src/settingsData.ts is split from src/settings.ts for
exactly that reason.
Written for one vault, so the default folders match its layout; all of them are settings. IPv6 addresses are recorded and displayed but deliberately kept out of the address arithmetic. No support is promised.
MIT licensed.