# Meshlink

Meshlink is an open-source control plane for community-operated mesh-network ISPs. It helps local groups provision wireless nodes, manage subscribers, monitor network health, and provide reliable internet access without depending on a traditional last-mile provider.

Meshlink is designed for small cooperatives, neighborhood networks, rural broadband projects, and disaster-response deployments. It supports mixed hardware and backhaul technologies, including Wi-Fi mesh links, point-to-point radios, Ethernet, and fiber.

> Meshlink manages routing, provisioning, and operations. It does not replace your upstream internet connection, radio firmware, or local regulatory requirements.

## Features

- Automatic node discovery and secure enrollment
- WireGuard-based management overlay
- Babel and BATMAN-adv routing support
- Subscriber plans, quotas, and service suspension
- IPv4 NAT and native IPv6 delegation
- Captive-portal and voucher authentication
- Link-quality, latency, and bandwidth monitoring
- Prometheus metrics and Grafana dashboards
- Role-based access for operators and volunteers
- Configuration backups and staged node upgrades
- REST API and webhook integrations
- Offline-tolerant node operation during controller outages

## Architecture

A typical deployment contains:

- **Controller** — Stores network state, exposes the API, and coordinates node configuration.
- **Gateway** — Connects the mesh to one or more upstream internet providers.
- **Mesh nodes** — Relay traffic and provide local wireless coverage.
- **Agent** — Runs on each gateway and node to apply configuration and report health.
- **Dashboard** — Provides a web interface for operators.

The controller is not in the subscriber data path. Existing routes remain active if the controller becomes temporarily unavailable.

## Requirements

### Controller

- Linux host with Docker Engine 24 or later
- Docker Compose v2
- 2 CPU cores and 2 GB RAM minimum
- PostgreSQL 15 or later
- A DNS name with valid TLS
- Reachability from the management network

### Nodes

- OpenWrt 23.05 or later, or Debian 12
- 128 MB RAM minimum
- 16 MB free storage
- WireGuard support
- Babel or BATMAN-adv, depending on the selected routing mode
- Correctly configured radios and regulatory domain

## Installation

Clone the repository and create a local environment file:

```sh
git clone https://github.com/meshlink-isp/meshlink.git
cd meshlink
cp .env.example .env
```

Generate application secrets:

```sh
openssl rand -hex 32
openssl rand -base64 48
```

Add the generated values to `.env`, then start the controller:

```sh
docker compose up -d
```

Run database migrations and create the first administrator:

```sh
docker compose exec controller meshlink migrate
docker compose exec controller meshlink admin create
```

The dashboard is available at `https://mesh.example.net` after DNS and TLS are configured.

Check the deployment:

```sh
docker compose ps
docker compose exec controller meshlink doctor
```

## Node Enrollment

Create a single-use enrollment token:

```sh
docker compose exec controller meshlink token create \
  --site north-hill \
  --role mesh-node \
  --expires-in 30m
```

On an OpenWrt node, install the agent package and enroll it:

```sh
opkg install meshlink-agent_1.4.0_all.ipk

meshlink-agent enroll \
  --controller https://mesh.example.net \
  --token mlk_enroll_REPLACE_ME
```

Review and approve the node from the dashboard, or use the CLI:

```sh
docker compose exec controller meshlink nodes pending
docker compose exec controller meshlink nodes approve node-7f3a
```

Enrollment tokens should be treated as credentials and removed from shell history after use.

## Usage

### Create a site

```sh
docker compose exec controller meshlink sites create \
  --name "North Hill" \
  --slug north-hill \
  --timezone America/Toronto
```

### Register an upstream gateway

```sh
docker compose exec controller meshlink gateways create \
  --site north-hill \
  --name gateway-01 \
  --uplink wan0
```

### Add a subscriber

```sh
docker compose exec controller meshlink subscribers create \
  --name "Alex Rivera" \
  --site north-hill \
  --plan community-25 \
  --device 58:9C:FC:12:34:56
```

### Inspect network health

```sh
docker compose exec controller meshlink nodes list
docker compose exec controller meshlink links degraded
docker compose exec controller meshlink routes inspect node-7f3a
```

### Apply configuration

Validate changes before publishing them:

```sh
docker compose exec controller meshlink config validate
docker compose exec controller meshlink config diff
docker compose exec controller meshlink config publish
```

Configuration is rolled out in batches. Nodes keep their last valid configuration if validation or deployment fails.

## Configuration

Controller settings are read from environment variables. Network and site settings are stored in `config/meshlink.yaml`.

### Environment Variables

| Variable | Required | Default | Description |
|---|---:|---|---|
| `MESHLINK_PUBLIC_URL` | Yes | — | Public HTTPS URL of the controller |
| `MESHLINK_DATABASE_URL` | Yes | — | PostgreSQL connection string |
| `MESHLINK_SECRET_KEY` | Yes | — | Key used to sign sessions and tokens |
| `MESHLINK_ENCRYPTION_KEY` | Yes | — | Key used to encrypt stored credentials |
| `MESHLINK_LOG_LEVEL` | No | `info` | Logging level |
| `MESHLINK_METRICS_ENABLED` | No | `true` | Exposes Prometheus metrics |
| `MESHLINK_METRICS_ADDR` | No | `127.0.0.1:9108` | Metrics listener address |
| `MESHLINK_SMTP_URL` | No | — | SMTP connection URL for notifications |
| `MESHLINK_TRUSTED_PROXIES` | No | — | Comma-separated reverse-proxy CIDRs |

### Example Network Configuration

```yaml
network:
  name: north-hill-community
  domain: mesh.internal
  routing:
    protocol: babel
    metric: etx
  management:
    ipv4_cidr: 10.42.0.0/16
    ipv6_prefix: fd42:6d65:7368::/48
    wireguard_port: 51820
  subscribers:
    ipv4_pool: 100.96.0.0/16
    ipv6_prefix: 2001:db8:4200::/48
    dns:
      - 1.1.1.1
      - 9.9.9.9

sites:
  north-hill:
    regulatory_domain: CA
    timezone: America/Toronto
    channels:
      2ghz: [1, 6, 11]
      5ghz: [36, 44, 149, 157]
    rollout:
      batch_size: 5
      health_check_seconds: 120

plans:
  community-25:
    download_mbps: 25
    upload_mbps: 10
    monthly_quota_gb: 500
    priority: normal
  sponsor-100:
    download_mbps: 100
    upload_mbps: 50
    monthly_quota_gb: null
    priority: high

monitoring:
  link_loss_warning_percent: 8
  link_loss_critical_percent: 20
  node_offline_after: 5m
  retention_days: 30
```

Do not use the documentation prefix `2001:db8::/32` in production. Replace it with IPv6 space delegated by your upstream provider or regional internet registry.

## Backups

Create a controller backup:

```sh
docker compose exec controller meshlink backup create \
  --output /var/lib/meshlink/backups
```

A complete recovery requires both the database backup and the encryption key. Store them separately in encrypted, access-controlled locations.

Restore into an empty deployment:

```sh
docker compose exec controller meshlink backup restore \
  /var/lib/meshlink/backups/meshlink-2026-08-01.tar.zst
```

## Monitoring

Prometheus metrics are exposed at `/metrics` when enabled. Useful alerts include:

- Gateway unavailable for more than two minutes
- Mesh node unavailable for more than five minutes
- Packet loss above 20%
- Gateway utilization above 85%
- Configuration rollout failure
- Certificate or enrollment-key expiration

Avoid exposing metrics, the database, or the management API directly to the public internet.

## Security

- Use a dedicated management VLAN or VPN.
- Require multi-factor authentication for operator accounts.
- Rotate enrollment tokens and WireGuard keys regularly.
- Restrict SSH access to the management network.
- Back up the controller before upgrades.
- Test firmware and configuration changes on a small node group first.
- Follow local spectrum, privacy, interception, and telecommunications rules.

To report a vulnerability, email `security@meshlink.example` instead of opening a public issue.

## Upgrading

Read the release notes before upgrading, then back up the controller:

```sh
docker compose exec controller meshlink backup create
docker compose pull
docker compose run --rm controller meshlink migrate
docker compose up -d
```

Upgrade a small canary group of nodes before starting a network-wide rollout:

```sh
docker compose exec controller meshlink upgrade start \
  --version 1.5.0 \
  --group canary
```

## Development

Start the development services:

```sh
docker compose -f compose.dev.yaml up -d
```

Run tests and checks:

```sh
make test
make lint
make integration-test
```

## Community and Support

Questions, deployment notes, and hardware compatibility reports are welcome in GitHub Discussions. Use GitHub Issues for reproducible bugs and feature requests.

Community operators should maintain an out-of-band contact method in case the mesh or controller is unavailable.

## License

Meshlink is licensed under the GNU Affero General Public License v3.0. See `LICENSE` for details.