> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fyberpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MikroTik RouterOS

> Connect MikroTik routers to FyberPay for real-time monitoring, PPPoE management, queue control, and automated provisioning.

FyberPay communicates with MikroTik routers via the **RouterOS REST API**, introduced in RouterOS 7.1. For RouterOS 6.x devices, FyberPay uses the legacy API port. Each router connects over an encrypted SSTP management tunnel, so the API is never exposed to the public internet.

## Supported RouterOS Versions

| Version       | API Method               | Notes                                   |
| ------------- | ------------------------ | --------------------------------------- |
| RouterOS 7.1+ | REST API (HTTP/HTTPS)    | Recommended. Full feature support.      |
| RouterOS 6.x  | REST API via www service | Requires `/ip service set www` enabled. |

<Info>
  RouterOS 7.x is recommended for all new deployments. FyberPay uses the `/rest/` endpoint prefix for all API calls, which requires the `www` or `www-ssl` service to be enabled on the router.
</Info>

## Architecture Overview

```
MikroTik Router                    FyberPay Platform
+------------------+               +------------------+
| RouterOS REST API|<-- SSTP ----->| RouterOS Service |
| (port 80/443)    |   Tunnel      | (circuit breaker)|
+------------------+               +------------------+
| PPPoE Server     |               | Provisioning Svc |
| RADIUS Client    |               | CoA Service      |
| Firewall / Queue |               | RADIUS DB        |
+------------------+               +------------------+
```

FyberPay connects to each router through an SSTP tunnel. The router's REST API listens on the tunnel IP (10.99.x.x subnet), never on a public interface.

## Connection Setup

<Steps>
  <Step title="Register the NAS device">
    In the FyberPay admin dashboard, navigate to **Network → Gateways** and click **Add Device**. The wizard runs in four steps: type → name → provision → services. Provide:

    * **Device name**: a human-readable label (e.g. "Westlands-POP-1")
    * **IP address**: the router's WAN IP (used for RADIUS NAS registration)
    * **RADIUS secret**: shared secret for RADIUS authentication
    * **CoA port**: default is `3799`
  </Step>

  <Step title="Run the bootstrap script">
    FyberPay generates a RouterOS provisioning script for each device. Copy and paste it into the router's terminal. The script (`BOOTSTRAP_VERSION=2`, current) is **tunnel-first**: it brings the SSTP management tunnel up before touching anything else, so a partial bootstrap leaves the router reachable for retry. Steps:

    1. SSTP tunnel creation with a dedicated PPP profile (`fyberpay-tunnel`)
    2. RADIUS client configuration (authentication port 1812, accounting port 1813)
    3. REST API user creation with scoped permissions
    4. Firewall rules to allow tunnel traffic, ordered so the tunnel rule lands first

    <Warning>
      The SSTP client **must** use the dedicated `fyberpay-tunnel` PPP profile. Using `profile=default` causes IPCP negotiation failures because the default profile may have hardcoded addresses that conflict with the tunnel's dynamic IP assignment.
    </Warning>

    <Info>
      Once a device is bootstrapped, FyberPay's **firewall reconciler** keeps the FyberPay-managed firewall rules in lockstep with the platform's view: drift (a rule deleted on the router, or one shifted out of order) is reported on the device's detail page and can be fixed with one click. The reconciler only touches rules it owns (tagged `comment=FyberPay-fw:*`); the operator's hand-rolled firewall is never modified.
    </Info>
  </Step>

  <Step title="Configure the REST API user">
    The bootstrap script creates an API user with a custom group called `fyberpay-api`. This group has the following policies:

    ```
    read, write, api, rest-api
    ```

    Policies explicitly denied: `ftp`, `reboot`, `policy`, `test`, `password`, `sniff`, `sensitive`, `romon`.

    The REST API service is restricted to the tunnel subnet:

    ```routeros theme={null}
    /ip service set www disabled=no port=80 address=10.99.0.0/16
    ```
  </Step>

  <Step title="Verify connectivity">
    After the tunnel establishes, use the **Ping** button in FyberPay's NAS device detail page. FyberPay calls `GET /rest/system/resource` on the router and reports the response time.
  </Step>
</Steps>

## API Port Configuration

| Port | Protocol | Use Case                                       |
| ---- | -------- | ---------------------------------------------- |
| 80   | HTTP     | Default REST API port over SSTP tunnel         |
| 443  | HTTPS    | REST API with TLS (self-signed certs accepted) |
| 8728 | API      | Legacy MikroTik API protocol (RouterOS 6.x)    |
| 8729 | API-SSL  | Legacy API with TLS                            |

FyberPay auto-detects the protocol based on the configured port. Ports 443 and 8729 use HTTPS; all others use HTTP. Self-signed certificates are accepted since traffic flows over the encrypted SSTP tunnel.

## What FyberPay Reads from the Router

FyberPay polls the following data from each connected router:

<AccordionGroup>
  <Accordion title="System Resources">
    **Endpoint**: `GET /rest/system/resource`

    Retrieves CPU load, memory usage (total/free), uptime, RouterOS version, board name, and architecture. Displayed on the fleet dashboard.
  </Accordion>

  <Accordion title="Active PPP Sessions">
    **Endpoint**: `GET /rest/ppp/active`

    Lists all active PPPoE/PPTP/L2TP sessions with username, service type, caller ID, assigned IP, uptime, and encoding. Used for subscriber session monitoring.
  </Accordion>

  <Accordion title="Network Interfaces">
    **Endpoint**: `GET /rest/interface`

    Returns all interfaces with name, type, running/disabled status, TX/RX byte counters, packet counts, link-down count, and MTU.
  </Accordion>

  <Accordion title="DHCP Leases">
    **Endpoint**: `GET /rest/ip/dhcp-server/lease`

    Lists active DHCP leases with IP address, MAC address, hostname, status, and expiry time.
  </Accordion>

  <Accordion title="Simple Queues">
    **Endpoint**: `GET /rest/queue/simple`

    Reads all simple queues with name, target address, max-limit, and burst-limit. Used for bandwidth plan enforcement visibility.
  </Accordion>

  <Accordion title="PPPoE Servers">
    **Endpoint**: `GET /rest/interface/pppoe-server/server`

    Lists configured PPPoE server instances with service name, bound interface, and disabled state.
  </Accordion>
</AccordionGroup>

## What FyberPay Writes to the Router

FyberPay's writes to the router are driven by the **NasService model**: one row per customer-facing service (PPPoE, Hotspot, or CPE Management), reconciled against live router state by the **NasService Reconciler**. Operators create services in the FyberPay admin UI; the reconciler translates each service into the right combination of PPPoE servers, hotspot servers, IP addresses, pools, DHCP, VLAN sub-interfaces, walled-garden rules, and bridges on the live router.

### The NasService model

Each service is one row in the `nas_services` table with these fields:

| Field                                       | Meaning                                                                                                                                                                                                |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type`                                      | `pppoe`, `hotspot`, or `cpe-management`                                                                                                                                                                |
| `parentInterface`                           | The interface on the router the service binds to. Can be an ether port (`ether2`), an SFP port (`sfp1`), a VLAN sub-interface (`vlan100-mgmt`), or an **operator-managed bridge** (`bridge-customer`). |
| `vlanId`                                    | Optional VLAN tag. If set, the reconciler creates a VLAN sub-interface as the binding point.                                                                                                           |
| `subnet`, `gateway`, `poolStart`, `poolEnd` | Auto-allocated from `10.50.0.0/16` (customer) or `10.20.0.0/16` (management). Operator can override under "Advanced".                                                                                  |
| `state`                                     | `pending` → `active` (or `failed` / `degraded` if the reconciler hits an error or detects drift).                                                                                                      |

Each NasService has its own RouterOS resources, named with a deterministic 8-hex slug derived from the service id. For example, the PPPoE server for service `5d8f2a1e-...` is named `pppoe-svc-5d8f2a1e`. Every resource the reconciler creates carries `comment=FyberPay-svc:<service-id>` so ownership is unambiguous on the router.

### The reconciler primitives

Three idempotent primitives, all in `apps/api/src/network/nas-services/reconciler.ts`:

* **applyService(serviceId)**: read live state for the service tag, compute a diff against the desired state, execute the diff. Used on create, update, manual reconcile, and drift recovery.
* **removeService(serviceId)**: tear down every resource carrying the service tag. Used on delete.
* **reconcileDevice(deviceId)**: scan the device for orphaned resources (carrying a service tag for a service that no longer exists in the database) and remove them.

### Bridge as parent interface

If the operator already runs a downstream switch off their MikroTik and wants every customer plugged into any LAN port to use the same service, they create a bridge on the router and pick that bridge as the parent interface. See the [bridge-as-parent guide](/guides/mikrotik-bridge-as-parent) for the full operator workflow.

The parent-interface dropdown groups options by type (Bridges, Ethernet, SFP, VLAN). Bridges are filtered to exclude `bridge`, `bridge-lan`, `fyberpay-tunnel`, and any FyberPay-managed `br-svc-*` bridge (those are reserved by the reconciler itself). VLAN-on-bridge is allowed for tagged trunks.

### Reserved interfaces (safety guard)

Before any RouterOS write, the reconciler checks the parent interface against a **reserved set** computed live from the router. The reserved set is the union of seven sources:

1. Always-reserved names: `bridge`, `bridge-lan`, `fyberpay-tunnel`
2. Members of `bridge-lan` (the management LAN)
3. Interfaces with a bound DHCP client (the WAN uplink)
4. Interfaces a default route exits through
5. Members of the RouterOS `WAN` interface-list, if the operator set one
6. PPPoE/L2TP/SSTP **client** interfaces (the router dialing out to its upstream)
7. FyberPay-managed service-scoped bridges (`br-svc-*`) — identified by their service-tag comment

Any service create / update / reconcile that targets a reserved interface is rejected with a 400 before any router write happens. This prevents the entire class of "FyberPay accidentally bound a customer service to my management LAN" bugs.

### Drift detection

A BullMQ background job runs every \~15 minutes per device. For each NasService, it computes the diff between the live router state and the desired state. If the diff is non-empty, the service is marked `degraded` and an outbox event (`nas.service.drift_detected`) is emitted.

Drift detection is **detect-only** by default. The operator clicks **Reconcile** on the service card to apply the diff. Auto-healing is intentionally not on by default to avoid silently undoing deliberate operator changes made in Winbox.

### Reconfigure semantics (hard cut)

When an operator changes a service's `type`, `parentInterface`, or `vlanId`, the reconciler tears down the old resources and creates new ones. Active customer sessions on the affected service are kicked. The UI surfaces a confirmation dialog quoting the impact ("This will disconnect N active users. Re-issue with confirm=true to proceed.") before allowing the change.

Non-disruptive changes (subnet/gateway/pool tweaks, walled-garden adjustments) apply without kicking sessions.

### Other writes

<AccordionGroup>
  <Accordion title="Walled garden (hotspot)">
    **Domains**: `PUT /rest/ip/hotspot/walled-garden`
    **IPs**: `PUT /rest/ip/hotspot/walled-garden/ip`

    Each hotspot service maintains its own walled-garden rules (Safaricom Daraja endpoints, FyberPay's payment callback domain, M-Pesa portal). Operators can add custom entries per service.
  </Accordion>

  <Accordion title="Firewall Address Lists">
    **Add entry**: `PUT /rest/ip/firewall/address-list`
    **Remove entry**: `DELETE /rest/ip/firewall/address-list/{id}`

    Used for walled-garden enforcement on the L3 path (post-dunning subscribers redirected to a "pay your bill" portal until they pay).
  </Accordion>

  <Accordion title="Simple Queue Updates">
    **Endpoint**: `PATCH /rest/queue/simple/{id}`

    Optional. Used when an ISP manages bandwidth through router-side queues rather than RADIUS attributes (the recommended path is RADIUS-driven via FreeRADIUS, see the [FreeRADIUS integration](/integrations/freeradius)).
  </Accordion>

  <Accordion title="Device Reboot">
    **Endpoint**: `POST /rest/system/reboot`

    Triggers a remote reboot. Requires `admin` or `super_admin` role. All reboot actions are recorded in the audit log.
  </Accordion>
</AccordionGroup>

### What FyberPay does NOT manage on the router

* **WAN configuration**: never touched. WAN is detected (sources 3/4/5/6 of the reserved set) and protected.
* **Operator-managed bridges**: their existence and port membership are owned by the operator. FyberPay binds to bridges but never modifies their members or comments.
* **Default firewall**: never touched. The bootstrap script only adds tunnel-traffic allow rules.
* **Existing PPP profiles**: never touched. FyberPay creates per-service profiles named `ppp-prof-svc-<slug>`.

This separation is deliberate. The operator can edit anything in Winbox without breaking FyberPay, and the drift detector will surface any divergence rather than silently overwriting the operator's intent.

## Fleet Monitoring

FyberPay polls all configured NAS devices in parallel (batches of 10) to build the fleet status dashboard. Each device reports:

* Reachability (online/offline)
* Board name and RouterOS version
* CPU load percentage
* Memory usage percentage
* System uptime
* Active PPP session count

Devices without RouterOS API credentials configured appear as "unreachable" with an explicit error message.

## RADIUS Configuration

The bootstrap script configures each router as a RADIUS client pointing to FyberPay's FreeRADIUS server:

```routeros theme={null}
/radius add \
    service=ppp \
    address=10.99.0.1 \
    secret="<shared-secret>" \
    authentication-port=1812 \
    accounting-port=1813 \
    timeout=3s \
    comment="FyberPay RADIUS"

/ppp aaa set use-radius=yes accounting=yes interim-update=5m
/radius incoming set accept=yes port=3799
```

<Tip>
  The `interim-update=5m` setting tells the router to send RADIUS accounting updates every 5 minutes. This is required for accurate bandwidth tracking and session monitoring in FyberPay.
</Tip>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Router shows as unreachable">
    1. Verify the SSTP tunnel is established: check `/interface sstp-client print` on the router
    2. Confirm the tunnel has an IP in the 10.99.x.x range: `/ip address print where interface=fyberpay-tunnel`
    3. Ensure the `www` service is enabled and bound to the tunnel subnet: `/ip service print`
    4. Check that the firewall allows tunnel traffic: `/ip firewall filter print where comment~"FyberPay"`
  </Accordion>

  <Accordion title="API credentials not configured error">
    The NAS device record in FyberPay is missing the RouterOS API username or password. Re-run the bootstrap script or manually update the device credentials in **Settings > Network > NAS Devices**.
  </Accordion>

  <Accordion title="SSTP tunnel keeps disconnecting">
    * Confirm the tunnel uses `profile=fyberpay-tunnel` (not `profile=default`)
    * Check that `verify-server-certificate=no` is set (FyberPay uses self-signed certs for the SSTP server)
    * Verify the router has stable internet connectivity: `/ping 8.8.8.8 count=5`
    * Review tunnel logs: `/log print where topics~"sstp"`
  </Accordion>

  <Accordion title="REST API returns 401 Unauthorized">
    * Verify the `fyberpay-api` user exists: `/user print where name=fyberpay-api`
    * Check the user group has `api` and `rest-api` policies: `/user group print where name=fyberpay-api`
    * Confirm the password matches what is stored in FyberPay (re-run bootstrap if unsure)
  </Accordion>

  <Accordion title="Circuit breaker is open">
    FyberPay wraps all RouterOS API calls in a circuit breaker. If a router is consistently unreachable (multiple consecutive failures), the circuit breaker opens and stops sending requests for a cooldown period. The device will show as unreachable until the circuit closes and the next health check succeeds.
  </Accordion>

  <Accordion title="A service stays in 'pending' state forever">
    The reconciler hit a RouterOS write error mid-flight. Click **Reconcile** on the service card to retry. If it stays pending, common causes:

    * Parent interface no longer exists (renamed or deleted in Winbox)
    * Parent interface has its own IP that conflicts with the service's allocated subnet
    * A bridge member port has its own `/ip/address` (RouterOS won't allow it as a bridge port)
    * The router is unreachable (circuit breaker open)

    The **Diagnostics** panel on the device page shows the specific RouterOS error from the last reconcile attempt.
  </Accordion>

  <Accordion title="A service shows 'degraded' state">
    Drift detection found a difference between the live router state and the desired state. Common causes:

    * An operator manually deleted the PPPoE/hotspot server in Winbox
    * The router was reset to defaults but FyberPay's NasService row still exists
    * Someone modified a FyberPay-managed resource without going through the FyberPay UI

    Click **Reconcile** on the service card to re-apply the desired state. If the operator change was intentional (they wanted the resource gone), delete the NasService in FyberPay instead.
  </Accordion>

  <Accordion title="The bridge I created in Winbox does not appear in the parent-interface picker">
    Possible causes:

    * The bridge is disabled. Run `/interface/bridge enable <name>` on the router.
    * The bridge name is `bridge`, `bridge-lan`, or `fyberpay-tunnel` (always-reserved).
    * The bridge's comment carries a FyberPay service tag (`FyberPay-svc:...`). Either you accidentally edited an existing `br-svc-*` bridge's comment, or this is a FyberPay-managed bridge. Pick a different bridge with a different name.
    * The router is unreachable, so FyberPay cannot list its interfaces.

    See the [bridge-as-parent troubleshooting](/guides/mikrotik-bridge-as-parent#common-questions) for more.
  </Accordion>
</AccordionGroup>

## Security Considerations

* All API traffic flows through the encrypted SSTP tunnel. No RouterOS API ports are exposed on public interfaces.
* API credentials are encrypted at rest using AES-256 (the platform `ENCRYPTION_KEY`).
* The `fyberpay-api` user group has minimal permissions: no access to `policy`, `password`, `sensitive`, or `sniff` operations.
* Every write operation (reboot, queue change, address list modification) is recorded in FyberPay's audit log with the acting user, timestamp, and IP address.
