SlopScore
00 crowd

tikpilot (github.com/maximdr86/tikpilot)

Self-hosted web panel for a MikroTik fleet: monitoring, bulk RouterOS operations, backups, syslog. Python, no build step
Python · ★ 7 · 1 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 2 hours ago by maximdr86 · last checked 31 minutes ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-11: a self-hosted web panel for managing a fleet of MikroTik routers whose README says "The code was written by Claude, an AI model by Anthropic.". 7 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as maximdr86. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Self-hosted web panel for a MikroTik fleet: monitoring, bulk RouterOS operations, backups, syslog. Python, no build step
topics
backupcentralized-managementfastapifleet-managementmikrotikmikrotik-routerosnetopsnetwork-automationnetwork-managementnetwork-monitoringpythonrouterosself-hostedsqlitesyslogthe-dudewinboxwireguard
created
2026-08-10 · pushed 17 hours ago · 91 commits · 1 contributor
release
v1.74.0 · 2026-09-04
languages
Python 76%HTML 17%CSS 3%JavaScript 2%Shell 2%Batchfile 0%
paperwork
contributingpull request templatelicensereadme 85% health
dependencies
no mappable packages · OSV.dev, checked 2 hours ago

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: a self-hosted web panel for managing a fleet of MikroTik routers whose README says "The code was written by Claude, an AI model by Anthropic.". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README (read the rest on GitHub)

Tikpilot

English · Русский

tests license: MIT python

Central management for a fleet of MikroTik routers over the RouterOS API: monitoring, bulk upgrades, scheduled backups, syslog, WireGuard. Self-hosted, no cloud, MIT.

Runs on any server with Python, as a single process. No Redis, no Celery, no Node.js, no frontend build step. The panel itself needs no internet access: nothing is loaded from third-party sites.

The interface comes in English and Russian, switchable from the menu.

The code was written by Claude, an AI model by Anthropic. See How this project came about.

The prompt and the project context behind it: docs/prompt-mikrotik-panel.en.md.

Site: tikpilot.ru

Dashboard

Devices Monitoring
Devices. Filters, search, sorting, RouterOS version, uptime and mobile operator for each site Monitoring. Fleet map, uptime over 24 h, latency and loss
Scripts Device log
Scripts. A library of commands and what actually sits on the routers Device log. Syslog from the whole fleet, with filters and hiding rules
Bulk action Jobs
One action for the whole fleet. Reboot, upgrade, a script, a backup, an API call Jobs. History with per-device success and failure counts
Device card WireGuard
Device card. Status, clients behind the router, latency and loss WireGuard. Router-to-router links through a hub, with handshakes and traffic
More screenshots: clients, backups, groups, audit log, rights, ports and services

Clients Backups Groups Audit log Rights Settings Ports and services WireGuard links

The screenshots are taken from a live fleet with screenshot mode on: site names, addresses, client names and accounts are replaced with made-up ones by the panel itself. Addresses come from the ranges reserved for documentation.


What it does

Device list. Name, address, group, RouterOS version, uptime, status, time of the last check. Filters, search, sorting by any column. Add devices by hand or load a CSV file.

Bulk operations. Pick devices or a whole group and run:

  • a status check;
  • a reboot;
  • a script that already sits on the device;
  • pasted script source;
  • a script upload without running it;
  • any RouterOS API command;
  • a command in console syntax over SSH, exactly as in Winbox, with wrapped lines joined back and the current menu remembered;
  • the same with a safety net: the router rolls back to its previous configuration unless the change is confirmed;
  • a backup (binary and export) downloaded to the server;
  • setting the identity;
  • a RouterOS upgrade;
  • a speed test to another site in the fleet.

Everything runs in the background. You see the progress and a separate result for each device. A job can be cancelled, or deferred to a chosen time such as 02:00.

Needs attention. The first block on the dashboard: offline and flapping sites, packet loss, stale backups, syslog gone quiet, armed rollbacks, failed jobs, CPU, memory and disk pressure, open services and available updates - one list ordered by severity. When everything is calm, the block is not there.

Monitoring. The panel checks availability once a minute and keeps a history of outages with downtime totals. It shows a fleet map, uptime percentage over a day, a week or a month, and a separate list of sites that keep flapping.

Short drops are told apart from real outages by MONITOR_MIN_OUTAGE: they are marked as flaps, kept out of the downtime figure and out of the feed, but still visible as a separate count.

Link latency. The devices ping the configured targets and their own gateway themselves. The graphs show a link degrading before the site actually drops.

Separately, the panel pings on its own, but only the sites that did not answer over the API. The status answers the question "is the site working": a router that answers ping stays online even when the panel cannot reach its API. An unreachable API is a separate mark, "no API" in the list and a line on the card: the site is up, but commands do not reach it - the link, the tunnel or a service on the router. Offline means both are silent.

That is the right split: driving to a site and fixing a link are different jobs, and downtime in a report should be about the site, not about our API. Switch it off with ICMP_CHECK_ENABLED=0 and the API decides everything again.

Clients. What is connected at the sites: name, vendor, MAC, address, port, VLAN and when it was last seen. Built from DHCP leases, ARP and the bridge host table, so it also shows boxes with hand-set addresses and which port the cable is in. You can see whether a client is wired or wireless, and for wireless ones also the network and the signal level. A comment set on the router itself is shown as the name.

Backups. Files are stored on the server, searchable and downloadable.

Console. A tab showing what the panel is doing right now: checks, jobs, device errors. Lines live in memory, and passwords never reach them.

Device inventory. The device card shows ports as tiles coloured by link speed and marked where PoE is on, bridges and VLANs, neighbours from the discovery table, the router's enabled services, and sensor readings. A neighbour that also lives in the panel becomes a link to its own card.

Services deserve a separate mention: telnet, ftp, www without ssl, or api with no address restriction are flagged as risky. One forgotten site with telnet open is a hole, and you cannot find it by eye across fifty boxes.

Collected during the full poll, in the same session, and rendered from the database: the card opens instantly and still works when the site is down.

The WebFig button comes from the same data: if www or www-ssl is enabled on the router, the card header links to the router's own web interface, on the port recorded in the inventory and over https when that is configured. No web service, no button. The link opens from your machine rather than from the panel's server, so it only gets through from wherever the site is reachable anyway.

Device logs. The panel receives syslog over UDP and TCP itself and keeps the lines next to everything else: a line is tied to its site right away, and you can jump from it to that device. Filters by text, site, severity and topic, a live feed that loads older lines as you scroll, and a text export.

Rules decide what happens to a line: highlight it in one of nine colours, hide it (still in the database, brought back by a checkbox), or do not store it at all. The second is for what irritates the eye, the third for what is not worth keeping. The panel's own API logins come with a ready made hide rule, switched off.

On a phone. The panel can be added to the home screen and opens like an app: no address bar, its own icon, a bottom tab bar within thumb reach and padding around the notch. Nothing to install, it is still a web page: Safari, Share, Add to Home Screen.

Audit log. Who did what and when.

Security. Login and password to get in. Administrator passwords are hashed (bcrypt), device passwords are encrypted (Fernet). After a few failed login attempts the address gets a pause that grows with every excess.


What you need to run it

The numbers are measured on a live install with 50 devices, not estimated.

Resource Minimum Recommended
CPU 1 core 2 cores
RAM 512 MB 1 GB
Disk 2 GB 10 GB and up if you keep backups
Python 3.10 3.12
OS anything with systemd Ubuntu 22.04 or 24.04 LTS

The panel uses 57 MiB of memory at startup and 62 MiB with fifty devices. The install including the virtualenv takes 72 MiB. The database grows by about 1.6 MiB per day and then stops: old rows are pruned automatically.

Backups take the most space. A hAP ac lite copy is 80 to 150 KiB, a text export 15 to 60 KiB. For 49 sites that is roughly 67 MiB for seven daily copies.

A VM with one core and one gigabyte of RAM is plenty. The bottleneck is not the server, it is the links to the sites.


Installing

Docker

git clone https://github.com/maximdr86/tikpilot.git && cd tikpilot
cp .env.example .env
docker compose up -d --build

The panel opens at http://your-server:8080. Login admin, password admin, change it right after the first login.

Settings live in .env next to docker-compose.yml, on the host, not inside the container. The image does not contain .env at all: the values are passed in as environment variables at start. Edit the file and apply:

nano .env
docker compose up -d

No rebuild is needed for that, --build is only for code changes. To check what actually arrived: docker compose exec tikpilot env | sort.

The published port is the left side of ports, for example "6060:8080". There is no reason to change the inner one.

Ubuntu as a service

This is the way to go on a production server. Copy the project folder and run:

cd ~/tikpilot
sudo bash install-ubuntu.sh

The script installs the dependencies, creates a system user, deploys the panel to /opt/tikpilot, sets up autostart and prints the address and the password.

The same script works as an updater. Replace the files, run it again: the database, the keys and .env stay where they are.

Managing the service:

sudo systemctl status tikpilot
sudo systemctl restart tikpilot
sudo journalctl -u tikpilot -f

The installer, restore-data.sh and run.sh speak English or Russian depending on the server locale. Force one with TIKPILOT_LANG=en or TIKPILOT_LANG=ru:

sudo TIKPILOT_LANG=ru bash install-ubuntu.sh

Windows, or just to try it out

git clone https://github.com/maximdr86/tikpilot.git && cd tikpilot
cp .env.example .env
./run.sh

On Windows run run.bat. The script creates the environment and installs the dependencies itself.

Python 3.10 or newer is required. Tested on 3.10 and on 3.14.

Where this actually runs. The panel is plain Python and will start almost anywhere, but it is meant for Linux: the installer writes a systemd service, and the tests run on Ubuntu against Python 3.10, 3.12 and 3.13. Docker is the same Linux inside, so it is the right answer on Windows and macOS too.

Running run.bat or run.sh directly on the machine is for having a look at the panel rather than keeping a fleet on it: no service, no autostart, close the window and the panel is gone. The ICMP check does work on all three systems, the command is built for whichever one it is on.

requirements.txt uses lower bounds rather than pinned versions, on purpose. Prebuilt packages for a brand new Python release may not exist yet, and then pip tries to compile them from source, which needs Rust and a C++ toolchain. With floating versions it always finds a ready-made wheel.


Setting up the MikroTik devices

Each device needs the API enabled and a dedicated user. Run this on the router:

# 1. Enable the API
/ip service set api disabled=no port=8728
# optionally restrict it to the server address:
/ip service set api address=192.0.2.10/32

# 2. FTP is only needed to download backups
/ip service set ftp disabled=no address=192.0.2.10/32

# 3. A group with minimal rights
/user group add name=tikpilot policy=api,read,write,test,ftp,reboot,policy

# 4. The user
/user add name=tikpilot group=tikpilot password="StrongPassword" address=192.0.2.10/32

What each policy is for:

Policy Why
api required, nothing works without it
read status, version, uptime
write configuration changes, creating scripts
test running scripts
ftp downloading backups
reboot mass reboot
policy needed for scripts that carry policies

Do not grant sensitive unless you need an export containing passwords.

If you do not need reboots and backups, api,read,test is enough.

The password must use Latin letters and digits. RouterOS does not accept anything else over the API, and the panel says so up front.

If you want an encrypted channel, enable api-ssl on the router and tick "use API-SSL" with port 8729 on the device page.


Loading the device list

The easiest way to load a whole fleet: Devices → Import CSV.

name;host;username;password;group;comment
core-01;10.0.0.1;tikpilot;Secret123;Core;backbone
acc-01;10.0.1.10;tikpilot;Secret123;Access;entrance 1

Recognised columns: name, host, username, password, api_port, ftp_port, ssh_port, group, comment. The delimiter (comma or semicolon) is detected automatically. Groups that do not exist yet are created for you.

The devices.csv file in the project is a template with made-up data.


Users and permissions

Every account has its own set of capabilities and its own scope. The model follows MeshCentral: not roles, but checkboxes.

Capabilities are granted one by one: editing devices, managing groups, viewing and downloading backups, the audit log, the settings, user management. Every bulk action is a separate right of its own, so an operator can be allowed to take backups and check status while reboots and RouterOS upgrades stay out of reach. A newly added action appears in the list automatically.

Scope limits what the account sees to chosen groups and individual devices. Everything outside is invisible: the device list, the dashboard, the fleet map, the backups, even other people's job results. "Check all" means all of their devices.

Roles survive as quick presets. Viewer, Operator and Administrator simply tick a set of boxes, and you edit from there.

A new account starts with no rights at all. The opposite would be a nasty surprise: an administrator adds someone "just to look" and that someone reboots the fleet. You also cannot change your own rights, or one wrong click would lock you out and the fix would be editing the database on the server.

Checks live on the server, not in the interface. A hidden button is a convenience: a URL can be typed by hand and a request sent with curl.

Password reset

Every other account in the list has a «Password» button: set a new one without asking for the old. You cannot do this to yourself, there is a separate form for that and it asks for the current password.

Changing a password, yours or someone else's, ends all of that person's existing sessions: the session generation number moves and cookies issued earlier stop matching. A password that leaves someone else's tab working protects against nothing.

Invitation links

Instead of inventing a password for someone and sending it in a chat, issue an invitation under Settings. The person follows the link, picks a username and a password of their own, and lands inside; you grant the rights afterwards.

The link works once and expires, by default after two days. The sign up page is an ordinary panel page, so it only opens from a trusted network (ADMIN_NETWORKS): a link that goes astray is useless to an outsider. An existing username cannot be taken over, and the token never reaches the audit log, which keeps only the note saying who the invitation was for.

The address in the link is the one you are viewing the panel on. If admins come in on one address while you use another, set the right one in PANEL_BASE_URL. Do not confuse it with PUBLIC_BASE_URL: that one faces outward for the status page, and the sign up page will not open from outside anyway.


Public status pages

Each group can have a link like /status/<token> that opens without signing in. Hand it to contractors or the on-call shift: they see which sites are up, which are down, and since when.

Alongside the current state it shows downtime over the last 24 hours, both per site and as a total for the group. "How was it today" gets asked more often than "how is it right now", and the figure is computed by the same code as in the panel, so the contractor and the administrator look at the same number.

The page picks its language from the browser, and a link in the corner switches it. The panel deliberately ignores the browser language; here it is the other way round, because the link is opened by an outsider.

The page serves exactly that and nothing more. Deliberately absent: device addresses (a list of internal IPs is a map of your network), RouterOS versions (useful mainly to whoever plans to exploit them), error texts (they describe your internals), and any other group.

The token is random and 32 characters long. The link is created and revoked with a button under Groups, and revoking takes effect immediately. Turning it back on always issues a new token.

Who follows the link

Logs → Public links shows who opened a status page: address, device and browser, when the session started and when it was last active. A block at the top lists the pages that are open right now: the page refreshes itself once a minute, and that trail shows live tabs.

A row is a session, not a request, so a tab left open all day stays one row with a refresh counter. A link sent to a messenger is opened by the messenger first to build a preview; those visits are marked as robots and hidden from the list of people.

Visits with a token that does not exist are behind their own filter. One is a typo, ten in a row from one address is someone guessing: revoke the link and issue a new one. The wrong token itself is not stored, only its last six characters.

This is information about people, so visits.view is a separate right that is not granted by default, and retention has its own setting:

PUBLIC_VIEW_DAYS=90

The address in the link comes from PUBLIC_BASE_URL. Without it the server only knows the address the request arrived on, and since the panel is opened from the inside, the link would carry a local 10.x:8080:

PUBLIC_BASE_URL=http://vpn.example.com:6060

The group list shows when each link was last opened and how many visits it had over the last day and week. Visits count people, not requests: the page refreshes itself every minute, so a tab left open counts once. A sudden rise means the address travelled further than you intended.

The page is closed to search engines and to caching. An unknown token and a revoked one answer identically, so guessing tells the visitor nothing.


The operator on the public page. Off by default: the page is meant to be "a site name and its state". If it helps the contractor to know whose link a site runs on, and it usually does since they will call the ISP themselves, tick "show the operator" next to the link. The tick belongs to one group.

Only the operator name is shown. The details, the public address, the radio technology and the signal level, stay inside the panel: that is information about the network rather than an answer to "whose link is this".

Site-to-site WireGuard

The WireGuard section sets up router-to-router links in a hub-and-spoke layout: one router is the hub, the other sites connect to it.

Creating a link adds a peer and routes to the far side's networks on the hub, and hands you a ready .rsc for the spoke router. If the spoke is already in the device list, the script can be applied to it with one button. For non-MikroTik peers a plain wg-quick config is offered too.

Everything created is tagged with a wgpanel:<link> comment, so deleting a link touches only its own peer and routes; the rest of the router configuration is left alone.

Hub settings are read from the router itself: the interface, the tunnel address and the LAN subnets are filled in for you, so all that is left is to cross out what does not belong and save.

It all runs over the same device API the panel already uses to manage the fleet. No REST service, no certificate, no extra router user. The spoke private key is shown once and stored nowhere.

The config can be copied to the clipboard or downloaded as a file: the desktop WireGuard app takes it by import, and to a phone it is easier to send as a message. The panel shapes the filename into something the client will accept, because the client takes the tunnel name from it and only allows latin letters, digits and _=+.-, up to fifteen characters.

For a phone or a laptop, a QR code is shown next to the wg-quick config. It is drawn on the server, so no internet access is needed for it. An existing link has no code: the private key is gone by then, and without it there is nothing to put on the phone.

One thing worth knowing about routes: in RouterOS a peer's allowed-address decides which spoke a packet is encrypted for, but writes nothing into the routing table. Networks behind the tunnel therefore need ordinary routes, which the panel creates for you.

Their gateway is the spoke tunnel address, 10.8.0.43 for example, not the interface name. With a single peer there is no difference; with several, a route through the interface does not tell the router which one to hand the packet to.


Site operator

When a site goes down the first question is not "what is wrong with the router" but "whose link is it". The device list has an "Operator" column, and search covers it too.

The panel finds it on its own, in this order:

  • from the modem. /interface/lte/monitor reports the network code, and the name comes from the code: 25001 is always MTS, while the string baked into the firmware differs from model to model. The technology and signal level are shown under the name;
  • from the address registry. Where there is no modem, the panel asks RDAP about the public address of the site. This is the only place in the whole panel that reaches out to the internet, so it is off by default: OPERATOR_LOOKUP=1. Private and CGNAT addresses are never looked up: behind a carrier NAT the registry knows the owner of the NAT, not of the link;
  • by hand. The "Operator" field on the device card. What you type there wins and is never overwritten by a poll; a contract number fits nicely there too.

Rechecked once a day: an operator changes approximately never.


Health check

GET /healthz answers {"status": "ok"} with code 200 while the panel can read its database, and {"status": "error"} with 503 when it cannot. No login required and no trusted-network check: the caller is usually not a person but the installer, systemd, a container monitor or an external watchdog, and they arrive from an address that is not in the trusted list.

The answer is deliberately thin. The page is open to everyone, so it says nothing about the version, the number of devices or the uptime.


Needs attention

The first block on the dashboard. It answers the question "what should I deal with", not "how are things": one list sorted by severity instead of a walk through six sections.

What feeds it:

  • availability - sites that are offlin
Scan report · 2026-09-11
  • Prohibited terms or links
  • Repository eligibility
  • slopscore.md paperwork
  • Content policy
  • Risk review — +25 binaries at repo root (run.bat)

0 comments

log in to comment.

report this listinglog in to report