# Architecture

> How the panel is put together, from one Go binary serving a React SPA to the four ways it can reach a router.

Source: https://joinnasnet.com/en/guides/nasnet-panel/architecture/
Last updated: 2026-08-17

---

## Overview

Nasnet Panel is one process. A Go service (Echo v4) exposes a JSON API and
serves the React single-page app from the same port, because the compiled
frontend is embedded into the binary with `go:embed`. There is no separate web
server, no database, and no agent installed on the routers.

That shape is deliberate. The panel is meant to run *on* a MikroTik with 30 MB
of RAM to spare, and every extra moving part would have to fit in that budget
too.

## The pieces

| Piece | What it does |
| ----- | ------------ |
| React SPA | The operator interface. Talks JSON to its own origin. |
| Go API (Echo v4) | Serves the SPA, exposes the API, and proxies every router call. |
| Embedded assets | The built frontend, compiled into the binary via `go:embed`. |
| Subnet scanner | Sweeps a subnet for MikroTik devices and identifies services on common ports. |
| Batch executor | Runs sequences of RouterOS commands with progress, dry-run, and rollback on failure. |

## How it reaches a router

The backend speaks four protocols and falls back automatically, in this order:

1. **REST API**, the RouterOS v7 HTTP interface. Preferred.
2. **RouterOS API**, the native binary protocol on `8728`, or `8729` with TLS.
3. **SSH** on port `22`.
4. **Telnet** as a last resort, for devices where nothing else is open.

Fallback is per-router and automatic, so a fleet where one device only answers
on SSH and another has the REST API enabled needs no special handling. Nothing
is installed on the router itself; every operation is a command sent over one
of these four transports.

## Batching and rollback

Anything that changes more than one setting at a time goes through the batch
executor rather than being fired off command by command. A batch reports
progress as it runs, can be dry-run first to show exactly what would be sent,
and rolls back the steps it already applied if a later one fails. This is what
sits under the Easy Config wizard, which is a batch with a friendly face.

## Trust boundaries

Worth being precise about where things run, because it is easy to assume
otherwise:

- **Router credentials** are held by the Go backend for the duration of a
  session and used to open connections to the router. They are not sent to the
  browser.
- **The browser only ever talks to the panel**, never directly to a router.
- **Crash reports come from the browser, not the router.** The MikroTik never
  connects to an error-reporting service. Reports carry the error, stack trace,
  app version, and browser and OS name; IP addresses are stripped before
  sending, and the whole thing can be turned off per browser from the
  Diagnostics page.

## Deployment shapes

| Shape | What it looks like |
| ----- | ------------------ |
| On the router | A RouterOS container on `veth1` at `192.168.50.2/24`, reached through a dst-nat rule from the LAN port. |
| On a LAN server | A plain Docker container, usually with host networking so it can see the routers. |
| From source | Frontend dev server on `3000`, backend on `8080`, with API calls proxied across. |

The production image is built on busybox with musl, running a compressed Go
binary with `GOMAXPROCS=1` and a `/health` endpoint behind the container
healthcheck. Images are published multi-arch for `linux/amd64`, `linux/arm64`
and `linux/arm/v7`.

## Related

- [System requirements](/en/guides/nasnet-panel/system-requirements/)
- [Installation](/en/guides/nasnet-panel/install/)
- [Diagnostics](/en/guides/nasnet-panel/diagnostics/)
