Skip to content

Repository files navigation

fiberwatch

Your connection breaks for an hour or two at a time. By the time you have finished restarting the router it is working again. You report it, a technician comes, spends twenty minutes on a line that happens to be behaving, tests clean, and leaves. The file is closed. It happens again on Thursday.

The problem is not that nobody believes you. The problem is that a spot check at 14:00 on a Tuesday is not evidence about 21:00 on a Thursday, and neither is your memory of it.

fiberwatch measures your line continuously — every ten seconds, for days or weeks — and turns the recording into a single HTML page you can attach to an email. The page states how much of the time the connection was usable, when it was not, which segment of the path the trouble sat in, and what the measurements are not able to say. That last part matters more than it sounds: a document that overclaims is dismissed in one line, and then you have spent a fortnight recording for nothing.

It is not a speed test. It is a record of what happened while nobody was watching.


Contents


What you get

Every ten seconds fiberwatch measures seven points along the path out of your flat: the router, your provider's first hop, two independent points on the internet, your DNS resolver, a plain TCP connection, and — only if you ask for it — a download.

Measuring several points rather than one is the whole trick. "The internet is down" is not something a provider can act on. "The router answered every ping while the provider's first hop lost 60 % of them for ninety minutes" is, because it puts the fault on the access line and takes rebooting the router off the table.

report turns that recording into:

  • report.html — one self-contained file. No internet connection needed to open it, no external stylesheets, no fonts to fetch, nothing that stops working when you send it to someone. It prints to about 23 pages. Available in German, English and Italian.
  • events.csv and cycles.csv — the raw numbers, for a spreadsheet.
  • summary.json — the same figures for anyone who wants to process them.

Requirements

  • .NET 8 SDK to build from source. Nothing at all to run a published binary.
  • Windows, Linux or macOS.
  • No administrator rights to measure. On Windows, ping works as an ordinary user; on Linux you may need one capability, which the systemd unit sets for you.
  • A machine that stays on. This is the real constraint. A laptop that sleeps at midnight records nothing until morning, and fiberwatch will say so rather than pretend otherwise — but a recording full of holes is a weak document. See Keeping it running.

Install

From a published binary

One file, nothing to install, no runtime to download. Put it anywhere and run it.

fiberwatch.exe --version
0.1.0

On Linux and macOS, mark it executable first (chmod +x fiberwatch).

From source

git clone <this repository>
cd InternetChecker
dotnet build fiberwatch.sln -c Release

The binary lands in src/FiberWatch.Cli/bin/Release/net8.0/. You can also run it without building explicitly:

dotnet run --project src/FiberWatch.Cli -- doctor

Everything after -- is passed to fiberwatch. To produce the standalone single file instead, see Building and publishing.


Quick start

1. Check the machine can measure anything.

fiberwatch doctor
fiberwatch 0.1.0 - environment check
Microsoft Windows 10.0.26200 (X64)

[ OK ] Settings file
       No fiberwatch.json in this folder, so the built-in settings apply. That is a normal way to run fiberwatch, not something missing.
[ OK ] ICMP ping
       This process can send pings, so latency and loss will be measured with ICMP.
[ OK ] Default gateway
       192.168.178.1 via Wi-Fi. This is the address fiberwatch will use for the L1_LOCAL_GW target.
[ OK ] Traceroute
[ OK ] Wi-Fi details
[ OK ] Output folder
[WARN] Clock
       Local time is 2026-08-05 08:14:08 +02:00 (W. Europe Standard Time). The Windows time service did not answer, so nothing may be correcting the clock.
       Fix: Open an administrator command prompt and run:  w32tm /resync

Summary: 6 OK, 1 WARN, 0 FAIL

Fix any FAIL before starting. A WARN costs the report detail or credibility but does not stop it.

2. Record.

fiberwatch monitor --duration 48h

Leave it running. It prints a line every cycle and writes everything to disk as it goes, so nothing is lost if the machine is switched off. Press Ctrl+C to stop early — it closes the file cleanly and notes that you stopped it deliberately.

Two days is the minimum worth submitting. A week is much harder to argue with, because it shows the fault recurring rather than happening.

3. Write the report.

fiberwatch report --input ./data --out report.html --lang en
Wrote report.html (546,800 bytes, English).
It is a single self-contained file: open it in any browser, print it, or attach it to a message to your provider.
Beside it, for anyone who wants the raw numbers: events.csv (7,249 bytes), cycles.csv (269,550 bytes), summary.json (39,288 bytes).

Leave off --out to get a plain-text summary on screen instead, which is what you want for pasting into a support ticket.

Try it without recording anything. There is a seven-day sample recording in the repository, with real-looking faults in it:

fiberwatch report --input samples/ --out sample-report.html --lang en

The commands

Every command takes --help. Everything below was run to confirm it behaves as described.

doctor — will this machine be able to measure?

No options. Run it before a long recording, not after.

It checks the settings file, whether this process may send pings, whether the default gateway can be found, whether the path can be traced, whether Wi-Fi details are readable, whether the output folder is writable, and whether anything is keeping the clock correct. The clock check matters: a report whose timestamps are ten minutes off is a report whose timestamps a provider can dismiss.

doctor makes no network calls of its own.

fiberwatch doctor

monitor — record

Option What it does
--config <file> Settings file to use. Default: fiberwatch.json in this folder if it exists.
--interval <duration> Wait between measurements, e.g. 10s, 30s, 5m. Default 10s.
--duration <duration> Stop by itself after this long, e.g. 48h, 7d. Default: until Ctrl+C.
--out <folder> Folder to record into. Default ./data.
--throughput <on|off> Also measure download speed. Off by default — read this first.
-q, --quiet Print only problems. Use this when running unattended.

Durations accept ms, s, m, h and d.

fiberwatch monitor --duration 25s --interval 5s --out ./data
fiberwatch 0.1.0 - recording your connection every 5s.
  settings   built-in defaults
  recording  ...\data
  running    25s

Working out what this machine is connected to...
  public IP  159.48.61.143, from https://api.ipify.org/ - the only thing fiberwatch asks a third party.

  L1_LOCAL_GW    192.168.178.1            Home router
  L2_ISP_HOP     159.48.61.1              ISP first hop
  L3_ANCHOR_A    1.1.1.1                  Internet anchor A (Cloudflare)
  L3_ANCHOR_B    8.8.8.8                  Internet anchor B (Google)
  L4_DNS         192.168.178.1            ISP DNS resolver
  L4_TCP         1.1.1.1:443              TCP connect 1.1.1.1:443
  L5_HTTP        not resolved             Throughput sample

layers: + answered   ! nothing answered   x could not measure   - not measured
Press Ctrl+C to stop. Everything measured so far is already on disk.

[00:00:00]  cycles 1  LOCAL_GW+ ISP_HOP+ ANCHOR_A+ ANCHOR_B+ DNS+ TCP+  trouble ~0
[00:00:05]  cycles 2  LOCAL_GW+ ISP_HOP+ ANCHOR_A+ ANCHOR_B+ DNS+ TCP+  trouble ~0
...
Finished the configured run.
  6 cycle(s) recorded over 25.9s.
  1 gap record(s) marking stretches that were not measured.
  written to ...\data\session-2026-08-05.jsonl

Measurements go into one file per local day, named session-YYYY-MM-DD.jsonl. Stopping and restarting is safe: the next run appends to the same day's file and writes a gap record saying how long nobody was watching.

Only one monitor may write to a folder at a time. A second one refuses and changes nothing:

Another fiberwatch monitor is already recording into ...\data (process 9776, on PCRENE,
since 2026-08-05T06:15:13Z). Two monitors writing to one folder overwrite each other's
records without any error and without any gap record, so this one has not started and
nothing has been changed. Stop the other one, or record into a different folder with --out.

That refusal is deliberate and it is why a scheduled restart is safe to set up: worst case it declines. The guard is two small files in the output folder: monitor.lock.claim, empty and held open for the life of the run, which is what the operating system refuses the second monitor on Windows, Linux and macOS alike; and monitor.lock, plain text naming the process that holds the folder, which is what the refusal above quotes and what you can cat or open yourself. Neither is deleted on exit, and leftovers from a machine that was switched off are simply reclaimed and rewritten by the next run — nothing goes stale and needs clearing.

A note on --interval. If a cycle can take longer than the interval, monitor says so at startup rather than silently drifting:

warn: interval: a cycle can take up to 10s but the interval is 5s. Target 'L3_ANCHOR_A'
alone can spend that long (5 requests x 2s) when nothing answers - which is exactly what
happens during the outage you are trying to record. Measurements will still be taken and
every missed cycle is written down, but the spacing will drop to 10s or more at the worst
moments.

The default 10s is comfortable. Do not lower it without reading that warning.

annotate — write down what you noticed

Option What it does
<text> (required) What happened, in your own words.
--category <label> Optional label to group notes by, e.g. household.
--config <file>, --out <folder> Which recording the note belongs to.

Run this from another terminal while monitoring, the moment something goes wrong. The report lines your notes up against the measurements, and a note that lands squarely on a measured incident is worth a great deal — it connects "the call dropped" to a number.

fiberwatch annotate "video call froze for about a minute" --category household --out ./data
Noted at 2026-08-05 08:15:03 +02:00:
  video call froze for about a minute
  written to ...\data\session-2026-08-05-notes.jsonl

Notes go in a separate -notes.jsonl file, so writing one never touches the measurements. You can write notes before you start monitoring.

optical — record the light levels off your fibre modem

Option What it does
--rx <dBm> (required) Receive power, from the modem's status page.
--tx <dBm> Transmit power, if the modem shows one.
--note <text> Anything worth remembering about the reading.
--config <file>, --out <folder> Which recording it belongs to.

Receive power is the first number a fibre technician asks for. Find it on your ONT or modem's web page — usually under "status", "PON" or "optical". Take a reading every day or two; drift between readings is more informative than any single one.

fiberwatch optical --rx -21.4 --tx 2.1 --note "ONT status page" --out ./data
Optical reading recorded at 2026-08-05 08:15:03 +02:00:
  Rx -21.4 dBm, Tx 2.1 dBm (ONT status page)

Both -21.4 and -21,7 are accepted, so it does not matter which decimal separator your modem prints or your keyboard produces.

report — say what it all adds up to

Option What it does
--input <folder|file> (required) The folder monitor wrote to. A single .jsonl also works.
--out <file> HTML file to write. Leave off for a text summary on screen.
--lang <de|en|it> Language of the HTML report. Default en.
--from <timestamp>, --to <timestamp> Narrow to one bad evening, e.g. 2026-07-28T20:00.
--subscribed-mbit <mbit> The download speed you pay for, so the report can compare.
--config <file> Settings file, for the language and the subscribed rate.

Times in --from and --to are read as your own local time, not UTC.

fiberwatch report --input samples/ --lang de --out report-de.html
Wrote report-de.html (555,806 bytes, Deutsch).

events.csv, cycles.csv and summary.json are written next to the HTML file automatically.

export — just the numbers

Option What it does
--input <folder|file> (required) The recording.
--format <csv|json> (required) csv for a spreadsheet, json to keep detail a column cannot hold.
--out <folder> (required) Where to write them.
--config <file> Settings file.
fiberwatch export --input samples/ --format csv --out ./export
Wrote 3 files into ...\export:
  events.csv            7.249 bytes
  cycles.csv          269.550 bytes
  summary.json         39.288 bytes
The CSVs use a comma separator and a full stop for decimals, so every tool reads them the
same way. A spreadsheet set to a comma-decimal locale will offer to split them on import;
pick comma.

json writes events.json, cycles.json and summary.json instead.

config — what settings would actually be used

Option What it does
--config <file> Settings file to read.
--interval, --out, --throughput Pretend this flag was passed, to see what it changes.

This is the command that answers "why is it not doing what I told it to". It prints the settings after every layer has been applied, and its output is itself a valid settings file, so it is also the easiest way to start writing one.

fiberwatch config
// settings from: built-in defaults (no settings file was read)
{
  "interval": "10s",
  "outputDirectory": "./data",
  "locale": "en",
  ...
}
fiberwatch config --config config.example.json --interval 30s
// settings from: ...\config.example.json
{
  "interval": "30s",
  "duration": "48h",
  ...
}

Note interval is 30s there and not the 10s the file says: the flag won.


The settings file

You do not need one. Every key is optional, running with no file at all works, and the shortest valid file is {}.

Copy config.example.json to fiberwatch.json next to where you run fiberwatch and it is picked up automatically, or point at it with --config. Comments and trailing commas are allowed, unlike in strict JSON.

Precedence

A command-line flag beats the settings file, and the settings file beats the built-in default. No option carries its own default value, which is what keeps "you did not pass this" distinguishable from "you passed the same value the default happens to be". Run fiberwatch config to see the result of all three layers.

The keys

interval — how long to wait between measurements. Accepts ms, s, m, h, d. Default 10s, which is roughly 8,600 measurements a day: enough resolution for an hour-long fault, small enough to leave running for a fortnight.

duration — stop automatically after this long. Remove the key to keep going until Ctrl+C.

outputDirectory — where measurements are written, one file per local day. Default ./data.

locale — language of the generated report: de, en or it. Default en.

eventCloseAfterOkCycles — the hysteresis, and the least obvious key here. How many consecutive healthy measurements must pass before a bad patch counts as over. Default 3. Without it, one good measurement in the middle of a bad hour would split that hour into a dozen short incidents, each individually easy to wave away. Raise it if your recording is fragmenting a single fault; lower it if separate faults are being merged.

subscribedRate — what you are actually paying for, in Mbit/s, as downstreamMbit and upstreamMbit. Used as the yardstick in the report. Leave either out if you are not sure: the report omits the comparison rather than measuring you against a number you guessed.

throughput — the download speed test. Off by default. Read this before turning it on.

  • enabled — default false.
  • url — where the download comes from. Default https://speed.cloudflare.com/__down.
  • downloadMegabytes — size of each sample. Default 10.
  • frequency — how often. Default 15m. Chosen so that an hour-long fault contains four to eight samples rather than one lonely point that gets called a blip.
  • dailyDataCapMegabytes — hard ceiling on what the speed test may consume per day. Default 1000. Reached, it stops for the day. This bounds only the speed test.

publicAddress — looks up the public IP your provider sees, as opposed to the 192.168.x.x your router gave this machine. This is the only thing fiberwatch asks a third party: one small request at startup and one every few minutes, well under a megabyte a month. Nothing about you is sent, and only the IP address is kept from the reply. It is on by default because a provider cannot look you up by 192.168.1.42 but can by your public address and a timestamp — and when that address changes at 03:14 it is because your session was re-established, which their own records either confirm or contradict.

  • enabled — default true. Set it to false and nothing is contacted at all; you lose that one line of evidence and nothing else.
  • url — where the address is asked for. Default https://api.ipify.org. Any endpoint that answers with a bare address works, including one on your own server — for example https://checkip.amazonaws.com.

wireless — the survey of Wi-Fi networks around you, which is what lets the report tell a fault on your line apart from a neighbour's new access point landing on your channel. Ignored on a machine plugged in with a cable.

  • censusEnabled — default true.
  • censusFrequency — default 5m.
  • allowActiveScan — default false. Sweeping the channels takes your adapter off its own channel for a second or two, and doing that every five minutes for a month would write thousands of stalls that fiberwatch itself caused into your evidence. On Windows this changes nothing, since the survey reads a list Windows already keeps.

thresholds — where "fine", "degraded" and "down" are drawn. The report prints these values, so a technician can argue with the threshold rather than with the measurement.

  • degradedLossPct — default 0. Any measured loss counts as degradation; a cycle that lost nothing is never degraded whatever this says.
  • failedLossPct — loss at which a target counts as down. Default 50.
  • degradedRttMs — round-trip time counting as degraded. Default 120.
  • degradedJitterMs — variation in round-trip time counting as degraded. Default 40. This is the one that actually breaks video calls.
  • degradedThroughputPctOfSubscribed — default 50. Ignored without subscribedRate.
  • minimumEventDuration — ignore bad patches shorter than this. Default 60s. Every connection has the occasional bad second, and listing them beside a two-hour outage buries the evidence.

targets — what gets measured. Each target isolates one segment of the path, which is what makes a fault attributable rather than merely observed.

Each entry takes an id, a displayName, a kind (Icmp, Tcp, Dns or Http), a target, an isolates description, and three settings you are most likely to change:

  • enabled — whether to measure it at all.
  • burstCount — how many requests per cycle. Loss is measured across the burst, so a burst of 5 can distinguish 20 % loss; a burst of 1 cannot.
  • timeout — how long to wait per request. This is what bounds how long a cycle takes when nothing answers, so it is the number to lower if the interval warning appears.

target is an address, an endpoint, a URL, or one of the auto: values — auto:default-gateway, auto:first-isp-hop, auto:dhcp-resolver, auto:throughput-endpoint — which are resolved on the measuring machine when a run starts.

Listing targets at all replaces the whole default set. Copy all seven from config.example.json and edit the ones you care about, rather than listing only your changes.


Keeping it running across reboots

monitor is an ordinary foreground console process. The most likely thing to end a three-week recording is not a crash — it is the first unattended reboot. A Windows Update restart on night four closes the window, and nothing tells you until you come back and find the evidence stops there. That produces exactly the incomplete record you already had.

Ready-made service definitions are in deploy/. All three start the recording at boot, before anyone logs in, and restart it if it stops.

A scheduled restart firing while a run is already going is safe. The second instance finds the folder claimed, refuses, and changes nothing — no corrupted file, no interleaved records, no lost cycles. The Windows task and the Linux unit also tell their scheduler not to start a second copy, so it usually does not get that far. Either way, nothing here can damage a recording in progress.

Windows — deploy/windows/fiberwatch-monitor.xml

A scheduled task that runs at startup whether or not you are logged on, plus at logon to cover the machine that was shut down rather than rebooted.

Registering it requires an administrator PowerShell. Both the boot trigger and the "run whether logged on or not" logon type are privileged operations; without elevation Windows refuses with nothing more helpful than Access is denied.

$exe  = "C:\fiberwatch\fiberwatch.exe"
$data = "C:\fiberwatch\data"
New-Item -ItemType Directory -Force $data | Out-Null

$xml = (Get-Content .\deploy\windows\fiberwatch-monitor.xml -Raw).
  Replace('ACCOUNT_HERE',  "$env:USERDOMAIN\$env:USERNAME").
  Replace('EXE_PATH_HERE',  $exe).
  Replace('DATA_PATH_HERE', $data)

Register-ScheduledTask -TaskName "fiberwatch monitor" -Xml $xml -Force

Then:

Get-ScheduledTask "fiberwatch monitor" | Get-ScheduledTaskInfo
Start-ScheduledTask "fiberwatch monitor"
Stop-ScheduledTask  "fiberwatch monitor"
Unregister-ScheduledTask "fiberwatch monitor" -Confirm:$false

Stop-ScheduledTask ends the run cleanly; the session file is closed and a gap record notes that monitoring was stopped deliberately.

Choices worth knowing about, all commented in the file: it never times out (the Task Scheduler default of three days would end a week-long recording); it runs on battery (the default does not, which on a laptop would record only the hours it was plugged in); and it does not wait for the network to be available, because "the network is not available" is precisely the condition under investigation.

If your paths contain two consecutive hyphens, do not paste them into the comment block at the top of the file — that is legal in a Windows path and illegal inside an XML comment, and Task Scheduler will reject the file as malformed. The substitutions above only touch the elements, which is why they are written that way.

Linux — deploy/linux/fiberwatch.service

sudo install -m 0755 fiberwatch /usr/local/bin/fiberwatch
sudo install -m 0644 deploy/linux/fiberwatch.service /etc/systemd/system/fiberwatch.service
sudo sed -i "s/YOUR_USER/$USER/g" /etc/systemd/system/fiberwatch.service
sudo systemctl daemon-reload
sudo systemctl enable --now fiberwatch
systemctl status fiberwatch
journalctl -u fiberwatch -f
sudo systemctl stop fiberwatch

The unit grants CAP_NET_RAW so that ping and the hop-limited probes that find your provider's first hop work without running as root. If you would rather not grant a capability, the commented alternative in the file is sysctl -w net.ipv4.ping_group_range="0 2147483647".

systemctl stop sends SIGTERM, which fiberwatch handles: it finishes the current cycle, writes a gap record and closes the file. Do not change KillSignal to SIGKILL — that ends the run with the last cycle unwritten and no record of why the file stops.

macOS — deploy/macos/com.larchsys.fiberwatch.plist

sudo install -m 0755 fiberwatch /usr/local/bin/fiberwatch
sudo mkdir -p /usr/local/var/fiberwatch && sudo chown $(id -un) /usr/local/var/fiberwatch
sed "s/YOUR_USER/$(id -un)/g" deploy/macos/com.larchsys.fiberwatch.plist > /tmp/fw.plist
sudo install -m 0644 -o root -g wheel /tmp/fw.plist \
  /Library/LaunchDaemons/com.larchsys.fiberwatch.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.larchsys.fiberwatch.plist
sudo launchctl print system/com.larchsys.fiberwatch
sudo launchctl bootout system/com.larchsys.fiberwatch

A LaunchDaemon rather than a LaunchAgent, deliberately: an agent runs only while its user is logged in, so the recording would stop at every logout and the report would show a gap for every evening the machine was locked and left.

On recent macOS the daemon appears under System Settings → General → Login Items and Extensions the first time it starts and must be allowed there. If the recording stops whenever nobody is at the machine, check that first, then check that the Mac is not simply sleeping.

Verification status. The Windows task XML was registered on a real Windows 11 machine, started, confirmed to launch the binary and write measurements, and the resulting recording was run through report. The systemd unit and the launchd plist were written against their documented schemas and are XML/INI-valid, but no Linux or macOS machine was available to run them on. If either needs a correction, that is a very welcome bug report.


How to read the report

Open report.html in any browser. It reads top to bottom and each section says what it is for. A few parts are worth explaining in advance.

The one thing that matters most: inside the flat, or beyond it

Every finding in the report is placed on one side of a line — your flat, or past your front door. The whole document is built around that distinction, because it is the only one your provider cares about:

  • Beyond the flat — the access line or the provider's network. "This is the one kind of finding a complaint can be built on."
  • Inside the flat — your own equipment or wiring. Your provider is not responsible and will say so, correctly.
  • Neither — a fault somewhere further out on the internet. Presenting it as your provider's would be disproved immediately.
  • Undetermined — the measurements do not separate the two, and the report says so plainly instead of guessing.

That is why the report puts a specific instruction near the top when it applies: plug the computer into the router with a network cable and record again. Wi-Fi trouble and line trouble look identical from a laptop. The cable test costs nothing and is the one thing that tells them apart — if the same degradation appears over the cable, it is not your Wi-Fi.

Do this before contacting your provider. A complaint built on measurements that turn out to be your own access point is the fastest way to have the whole document dismissed, and to be taken less seriously the next time.

The availability strip — "Connection state over time"

One column per time bucket, coloured by the worst state measured in that bucket. Worst rather than average, on purpose: a five-minute outage inside an hour is the thing you are trying to show, and an average would erase it.

Hatched grey is not an outage. It is time nobody measured — the machine was asleep, off, or the tool was not running. It is excluded from every percentage in the report. Do not quote it as downtime; the first competent person to read the document will notice, and everything else you claimed becomes suspect.

The heatmap — "Degraded time by weekday and hour"

Each cell is one weekday and one hour of local time, coloured by the share of monitored time in it that was degraded or down.

This is the chart that turns a complaint into an argument. A single incident is an anecdote. A column that lights up every weekday between 19:00 and 23:00 is a pattern, and patterns have ordinary explanations a provider can look up — contention in the local segment being the usual one. A cell too faint to distinguish by colour carries a dot, so a small but real share never reads as nothing.

The attribution — "What this points at"

For each incident, and for the recording as a whole, the report states what the measurements are consistent with, which side of the front door that puts it on, and at what confidence — followed by the alternatives it considered and could not rule out.

It works by comparing the segments. If the router answered normally while the provider's first hop lost packets, the trouble is past the router. If everything including the router degraded together, it is not.

When it cannot tell, it declines and says it declined. That is not a gap in the tool. A report that guesses is one a technician can discredit in a sentence.

The rest

"How much of that was actually measured" is the section a careful reader checks first, so check it yourself: it states what share of the elapsed time was actually monitored. A figure over 40 % of a week is a much weaker statement than the same figure over 96 % of it.

"How these measurements were taken, and what they cannot tell you" is the report's own methodology section, and it lists its limitations in more detail than the summary below. Leave it in when you send the document. It is what makes the rest credible.


What this cannot tell you

These are in the report itself, in its methodology section. They are here so nothing is a surprise after a fortnight of recording.

  • fiberwatch watches from one machine. It sees symptoms at one endpoint and never a cause. Nothing it produces is a statement about which piece of equipment is faulty or who is responsible — only about what was measured.

  • A sleeping or switched-off host recorded nothing, and nothing is how it is counted. Those stretches are marked "not monitored", excluded from every percentage, and are not outages. Where fiberwatch could tell why the gap happened — the machine slept, the process restarted, monitoring was stopped deliberately — it says which.

  • A captive portal makes an unusable connection record as clean. The hotel or café portal accepts the TCP connection itself, so the connection test passes, and answers the hijacked name lookup with a valid response pointing at itself, so the DNS test passes too. The records look healthy while nothing useful can pass. fiberwatch does not detect this and cannot. A recording made behind such a portal says nothing.

  • A VPN adapter can masquerade as your network interface. If it presents itself as an ordinary Ethernet interface it can supply the default gateway and resolver for a whole recording, and what was measured is the tunnel rather than the line in your flat. The interface used is named in the report so a reader who recognises the name can see it, but fiberwatch does not flag it, because it has no reliable way to tell one from a physical adapter. Turn your VPN off before recording.

  • On a host without ICMP the attribution table cannot be evaluated at all. In a container, or on a machine whose policy refuses raw sockets, four of the seven targets can only record an error in every cycle, and the segment comparison that separates a fault inside the flat from one beyond it has nothing to work with. fiberwatch declines to attribute rather than guessing, and says that it declined. Such a recording is still evidence about availability, but not about location. fiberwatch doctor tells you in advance.

  • Every timestamp comes from this machine's clock. Where fiberwatch could check that clock against a time source it says so. Where it could not, the times are internally consistent but may sit at a small offset from your provider's own logs. This is what the doctor clock warning is about.

  • Optical readings are typed in by hand. They record what the terminal displayed at that moment, not an independent measurement, and the terminal's own specification decides what is normal for it.


Before you turn on the speed test

The throughput test is off by default, and the reason is a number worth seeing before you change it.

At the shipped defaults — a 10 MB transfer every 15 minutes — it moves about 960 MB per day. The daily cap is 1000 MB, so a full day fits inside it with very little to spare, and the cap will not save you.

On a mobile hotspot, a metered tariff, or anything with a data allowance, that is real money. Nobody should find that out after a week of recording.

If you need throughput measurements on a metered line, lower downloadMegabytes, lengthen frequency, or lower dailyDataCapMegabytes first. Roughly:

downloadMegabytes frequency Per day
10 15m ~960 MB
10 1h ~240 MB
5 1h ~120 MB
5 4h ~30 MB

The 15-minute default is not arbitrary. A sample every two hours puts nothing, or one lonely point, inside an hour-long fault — which is exactly what gets waved away as a blip. Every fifteen minutes puts four to eight in it. If you lengthen the interval, understand you are trading away the thing that makes the throughput chart persuasive.

Two further effects, regardless of cost. The test saturates the line it is measuring, so latency figures taken at that moment are distorted — the report marks those cycles as measured under fiberwatch's own load rather than pretending otherwise. And the download comes from a third party (Cloudflare by default; change throughput.url to your own server if you would rather it did not).

The publicAddress lookup is separate and much smaller — a few hundred bytes every few minutes, well under a megabyte a month. It is deliberately not counted against the speed test's cap, which exists to bound something ten thousand times larger.


Contributing

The most useful thing you can send is real wireless output.

The Windows wireless parser is developed and tested against a real adapter. The Linux and macOS parsers are tested only against fixture text — no such machine was available while they were written. They parse what the documentation and manual pages say those tools produce, which is not the same as what your hardware actually produces.

If you run fiberwatch on Linux or macOS, please paste your real output into a bug report, or add it as a fixture in tests/FiberWatch.Tests/Fixtures/Wireless/:

# Linux
iw dev
iw dev wlan0 link
iw dev wlan0 scan

# macOS
wdutil info

A single file of genuine output from an adapter nobody has tested against is worth more than any amount of careful reading of the manual pages. Redact your SSIDs and MAC addresses if you like — the shape of the output is what matters.


Building and publishing

dotnet build fiberwatch.sln -c Release
dotnet test  fiberwatch.sln -c Release

A single self-contained binary

One command per platform. No .NET runtime needed on the target machine, and the output folder contains exactly one file.

dotnet publish src/FiberWatch.Cli -c Release -r win-x64
dotnet publish src/FiberWatch.Cli -c Release -r linux-x64

Add -o <folder> to choose where it lands; by default it goes under src/FiberWatch.Cli/bin/Release/net8.0/<rid>/publish/. Other runtime identifiers (osx-arm64, linux-arm64, win-arm64) work the same way.

Approximate sizes, compressed, with symbols embedded:

Runtime Size
win-x64 34.4 MB (36,036,544 bytes)
linux-x64 35.3 MB (36,976,287 bytes)

Everything needed is inside the file, including the German and Italian report text, which is an embedded resource rather than a satellite assembly precisely so that it survives being copied around as one file.

The build is deliberately not trimmed. Trimming would remove perhaps 30 MB, but the settings loader binds JSON onto a record and the report serializes its own summary, both of which the trimmer resolves by guesswork. A tool whose entire purpose is producing evidence must not lose a field to an optimisation nobody would notice until the report was already in the provider's inbox.

About

Find out what your internet connection is really doing. fiberwatch measures your line around the clock and turns the result into one page you can hand to your provider.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages