Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Default owners for everything
* @446564
# Every change to main needs a review from the repo owner
* @MrAlders0n
24 changes: 24 additions & 0 deletions .github/workflows/main-source-guard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Main PR source guard

# Runs from main's copy of this file, so a PR can't edit the check away.
on:
pull_request_target:
branches: [main]
types: [opened, reopened, synchronize, edited]

permissions: {}

jobs:
main-source-is-dev:
name: main-source-is-dev
runs-on: ubuntu-latest
steps:
- name: Require PR from this repo's dev branch
env:
HEAD_REF: ${{ github.event.pull_request.head.ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
if [ "$HEAD_REF" != "dev" ] || [ "$HEAD_REPO" != "$GITHUB_REPOSITORY" ]; then
echo "::error::PRs into main must come from $GITHUB_REPOSITORY:dev (got $HEAD_REPO:$HEAD_REF)"
exit 1
fi
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ Everything else in the file is optional and explained in
[Configuration](docs/configuration.md): the admin API key, log settings, Redis, and the
web app's map view, tabs, themes, name and banner.

If you enable MeshMapper imports, also set `MESHMAPPER_API_KEY`. Your local MeshMapper
regional or grouped-region admin can generate a regional key or a key covering multiple
regions. See [MeshMapper API key](docs/configuration.md#meshmapper-api-key) for setup and
the server rollout requirements. Keep this key out of `VITE_*` settings.

### 3. Describe your network in `config.yaml`

```bash
Expand Down
7 changes: 7 additions & 0 deletions app_config/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ LISTEN_ADDR=:8080
# At least 16 characters, no whitespace; e.g. `openssl rand -hex 32`.
# BEACON_API_KEY=

# MeshMapper regional or grouped-region API key for enabled imports.
# Your local MeshMapper regional or grouped-region admin can generate the key;
# grouped-region keys cover multiple regions. Never use the mobile App key.
# Overrides meshmapper.api_key, even when empty. Keep this out of VITE_* settings.
# Use the environment setting with backups; saved YAML keys prevent exports.
# MESHMAPPER_API_KEY=

# Override log.level / log.format from config.yaml (defaults: info / text).
# LOG_LEVEL=info
# LOG_FORMAT=json
Expand Down
10 changes: 9 additions & 1 deletion app_config/config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ ratelimit:
# A 429 returns error.code=rate_limited and Retry-After (1 or 60 seconds).
burst: 300

# MeshMapper integrations (optional, no API key). Import transport scopes and
# MeshMapper integrations (optional, require a regional or grouped-region API key).
# Your local MeshMapper regional or grouped-region admin can generate the key.
# A grouped-region key covers multiple regions, including imported group members.
# MESHMAPPER_API_KEY overrides meshmapper.api_key, even when empty. Use the
# environment for secrets, especially with backups; never use the mobile App key.
# Missing keys leave imports unconfigured; cached data and the rest of Beacon remain.
# 401/403 failures retain cached data and report authentication/permission errors.
# Import transport scopes and
# IATA border outlines from MeshMapper instead of maintaining scopes: and
# iatas.*.borderFile by hand.
#
Expand All @@ -63,6 +70,7 @@ ratelimit:
# Region-filtered scope lists and scope stats show imported names only under the
# IATAs whose catalogue lists them; IATAs without a catalogue show manual scopes only.
#meshmapper:
# api_key: ""
# scopes:
# enabled: false
# refresh_interval: 1h # 1h-24h (MeshMapper allows one get_scopes.php per region per 55m); one source per 15s tick
Expand Down
10 changes: 9 additions & 1 deletion docker-deployment-type1/data/app/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ ratelimit:
# A 429 returns error.code=rate_limited and Retry-After (1 or 60 seconds).
burst: 300

# MeshMapper integrations (optional, no API key). Import transport scopes and
# MeshMapper integrations (optional, require a regional or grouped-region API key).
# Your local MeshMapper regional or grouped-region admin can generate the key.
# A grouped-region key covers multiple regions, including imported group members.
# MESHMAPPER_API_KEY overrides meshmapper.api_key, even when empty. Use the
# environment for secrets, especially with backups; never use the mobile App key.
# Missing keys leave imports unconfigured; cached data and the rest of Beacon remain.
# 401/403 failures retain cached data and report authentication/permission errors.
# Import transport scopes and
# IATA border outlines from MeshMapper instead of maintaining scopes: and
# iatas.*.borderFile by hand.
#
Expand All @@ -64,6 +71,7 @@ ratelimit:
# Region-filtered scope lists and scope stats show imported names only under the
# IATAs whose catalogue lists them; IATAs without a catalogue show manual scopes only.
#meshmapper:
# api_key: ""
# scopes:
# enabled: false
# refresh_interval: 1h # 1h-24h (MeshMapper allows one get_scopes.php per region per 55m); one source per 15s tick
Expand Down
5 changes: 5 additions & 0 deletions docker-deployment-type2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ docker-deployment-type2/
Caddy redirects anything that is not `/api/*` or `/ws` to that host. The password inside
`POSTGRES_DSN` must match `POSTGRES_PASSWORD` in `docker-compose.yml`.

For MeshMapper imports, set `MESHMAPPER_API_KEY` in this server host's `.env`.
Your local MeshMapper regional or grouped-region admin can generate the regional or
grouped-region key. Do not copy it to the web host or a `VITE_*` setting. See
[MeshMapper API key](../docs/configuration.md#meshmapper-api-key).

3. Edit `data/app/config.yaml` for your network (see
[Configuration](../docs/configuration.md#configyaml)). Because the web app will be served
from another origin, browsers may only open the WebSocket if that origin is listed:
Expand Down
10 changes: 9 additions & 1 deletion docker-deployment-type2/server/data/app/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ ratelimit:
# A 429 returns error.code=rate_limited and Retry-After (1 or 60 seconds).
burst: 300

# MeshMapper integrations (optional, no API key). Import transport scopes and
# MeshMapper integrations (optional, require a regional or grouped-region API key).
# Your local MeshMapper regional or grouped-region admin can generate the key.
# A grouped-region key covers multiple regions, including imported group members.
# MESHMAPPER_API_KEY overrides meshmapper.api_key, even when empty. Use the
# environment for secrets, especially with backups; never use the mobile App key.
# Missing keys leave imports unconfigured; cached data and the rest of Beacon remain.
# 401/403 failures retain cached data and report authentication/permission errors.
# Import transport scopes and
# IATA border outlines from MeshMapper instead of maintaining scopes: and
# iatas.*.borderFile by hand.
#
Expand All @@ -64,6 +71,7 @@ ratelimit:
# Region-filtered scope lists and scope stats show imported names only under the
# IATAs whose catalogue lists them; IATAs without a catalogue show manual scopes only.
#meshmapper:
# api_key: ""
# scopes:
# enabled: false
# refresh_interval: 1h # 1h-24h (MeshMapper allows one get_scopes.php per region per 55m); one source per 15s tick
Expand Down
1 change: 1 addition & 0 deletions docs/api-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ edges spanning more than 180 degrees are rejected.
| Endpoint | Notes |
|---|---|
| `GET /observers` | Page of observers. Params: location filters, `type` (e.g. `meshcoretomqtt`), `broker`, `status` (`online`/`offline`), `name`, `scope`, `cursor` (`lastSeen` epoch ms), `limit`. |
| `GET /observers/directory` | Traffic/name pagination, row counts and type facets. See [observer directory contract](observer-directory.md) for rollout status and exact semantics. |
| `GET /observers/{observerId}` | Observer detail. |
| `GET /observers/{observerId}/telemetry?range=24h&interval=1h` | Telemetry history. `interval` is `1h` (default), `6h` or `24h`. `afterId` returns only newer points. |
| `GET /observers/{observerId}/activity?range=24h&interval=15m` | What the observer heard, bucketed. |
Expand Down
4 changes: 4 additions & 0 deletions docs/backup-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ changes, PostgreSQL roles and cluster settings, Redis and deployment files are n

## Requirements and limits

- Keep the MeshMapper API key in `MESHMAPPER_API_KEY`, with `meshmapper.api_key` empty or
omitted in the saved YAML. Both CLI exports and admin downloads refuse a nonempty saved
MeshMapper key because the YAML is included verbatim. Environment secrets remain excluded;
preserve them separately for recovery. See [MeshMapper API key](configuration.md#meshmapper-api-key).
- `pg_dump` must be in the **same runtime** as the exporter (the CLI's environment, or the
server's PATH for the download), with a major version at least as new as the database's. A client on
the Docker host or in another container doesn't count.
Expand Down
55 changes: 54 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ to open).
| `MQTT_BROKER_1_PASSWORD` | unset | Subscriber password for broker 1. |
| `MQTT_BROKER_2_URL`, `_USERNAME`, `_PASSWORD` | unset | The same for an optional second broker. A packet heard through both brokers is stored once. |
| `BEACON_API_KEY` | unset | Bearer key for `/api/v1/admin/*`. When set, even to an empty string, it replaces `auth.api_key` from the config file. See [Admin API key](#admin-api-key). |
| `MESHMAPPER_API_KEY` | unset | Regional or grouped-region key for enabled MeshMapper imports. When set, even to an empty string, it replaces `meshmapper.api_key`. Missing keys leave imports unconfigured while Beacon keeps running. See [MeshMapper API key](#meshmapper-api-key). |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn` or `error`. Overrides `log.level`. Anything else stops startup. |
| `LOG_FORMAT` | `text` | `text` or `json`. Overrides `log.format`. Anything else stops startup. |
| `BEACON_CPU_PROFILE_DIR`, `BEACON_CPU_PROFILE_UNTIL` | unset | Enable a bounded CPU capture in production. See [Profiling](profiling.md). |
Expand Down Expand Up @@ -68,7 +69,7 @@ which ones to read.
| `log` | Log level and format. | You want JSON logs for a collector. |
| `server` | `trusted_proxies`: the proxy addresses allowed to tell Beacon the real client IP. | Always, if Beacon sits behind a proxy. See [Reverse proxy](reverse-proxy.md). |
| `ratelimit` | Per-client REST limits for `/api/v1/*`. On by default at 300 requests a minute. | You get 429s you did not expect. |
| `meshmapper` | Import transport scopes, IATA borders, region groups and public channels from MeshMapper instead of listing them by hand. | You are in a region MeshMapper covers. |
| `meshmapper` | Import transport scopes, IATA borders, region groups and public channels from MeshMapper instead of listing them by hand. Requires a [MeshMapper API key](#meshmapper-api-key). | You are in a region MeshMapper covers. |
| `iatas` | Display names, coordinates and optional border files for airport codes. IATAs are created automatically when traffic arrives; this block only decorates them. A `borderFile` path is relative to `config.yaml`; in Docker the compose file mounts only `config.yaml`, so add a mount for the folder too (`- ./data/app/borders:/app/borders:ro` under the `app` service). | You want names on the map or a border drawn. |
| `regions` | Groups of IATAs with a name, map centre and zoom. | Always, unless MeshMapper imports them. |
| `channel_keys` | Hashtag channels and explicit keys for decrypting group messages. | Always. Without keys, channel messages are stored as hashes only. |
Expand Down Expand Up @@ -106,6 +107,58 @@ cannot honour. These are the ones people hit:
A negative `websocket.max_connections_per_ip` is not caught at startup and rejects every
connection instead, so leave it at a positive number.

## MeshMapper API key

MeshMapper APIs require either a regional API key or a grouped-region API key covering
multiple regions. Your local MeshMapper regional or grouped-region admin can generate
these keys. Request a key for the APIs Beacon uses, covering your IATA or every member IATA
in your group, including members added by `meshmapper.zones.import_groups`.

Set `MESHMAPPER_API_KEY` in the deployment's private `.env` or backend environment. Each
Beacon deployment can use a different key. It overrides `meshmapper.api_key` in
`config.yaml`, even when the environment value is empty. Both examples leave the key empty.
Do not use the mobile app's App key, `BEACON_API_KEY`, or a Coverage API key for this setting.
Never put it in a `VITE_*` value, browser configuration, logs, or git.

After changing `.env`, recreate the backend container from the deployment folder
(`docker-deployment-type2/server` for the split deployment):

```bash
docker compose up -d --force-recreate app
```

A plain `docker compose restart app` does not load changed container environment variables.
If you use the YAML setting instead, restarting Beacon is enough. Prefer the environment
setting when using [backups](backup-export.md): exports refuse a nonempty
`meshmapper.api_key` in the saved YAML so the key cannot enter a download.

Beacon sends `X-API-Key` to `get_zones.php`, `get_geojson.php`, `get_scopes.php`, and
`get_channels.php`. Its shared client supports the same header for `get_repeaters.php`,
although Beacon does not currently fetch repeaters. Requests use trusted HTTPS MeshMapper
endpoints and do not follow redirects. `get_zones.php` still needs its `country` parameter;
a key covering multiple regions does not change the request shape or remove rate limits.
IP exemptions are not authentication.

With no key, MeshMapper imports report unconfigured and make no requests. Beacon keeps
running and restores saved imports. Failed refreshes keep the last successful cached data
and its freshness timestamp; 401 reports authentication failure and 403 reports permission
failure, without falling back to anonymous requests. Existing backoff and longer
`Retry-After` delays on 429/503 remain in effect. See
[MeshMapper troubleshooting](troubleshooting.md#meshmapper-imports-are-unconfigured-or-stale).

### Coordinate the MeshMapper rollout

Before enforcement, confirm with the MeshMapper admin that Server supports `X-API-Key`,
that the key covers the required APIs and IATAs, and that these calls do not consume Coverage
quota. Header support must not be assumed deployed. The first four endpoints can require
keys before the app rollout; repeater enforcement waits for app 1.4.1 and its forced update.
Key issuance, permissions, quotas, and enforcement switches remain MeshMapper Server work,
tracked in [MeshMapper_Server#431](https://github.com/MeshMapper/MeshMapper_Server/issues/431).

Existing `coverage.php` consumers keep their own keys, scopes, quotas, and supported
`?key=` authentication. Do not move them to header-only authentication without confirmed
server support; Beacon's importer does not call `coverage.php`.

## Admin API key

The `/api/v1/admin/*` endpoints need `Authorization: Bearer <key>`. Everything else, including
Expand Down
64 changes: 64 additions & 0 deletions docs/mobile/privacy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# BEACON Mobile Privacy Policy

_Last updated: October 6, 2026_

BEACON Mobile ("the app") is an open-source iOS and Android client for Beacon, a
real-time analyzer for MeshCore radio mesh networks. This policy explains what
the app does and does not do with your information.

## The short version

- No account, no sign-in.
- No analytics, advertising, tracking or crash-reporting SDKs.
- The app does not access your location, contacts, photos, microphone or
camera.
- Your settings stay on your device.

## What stays on your device

The app stores its settings locally, using the platform's standard app storage:
the Beacon servers you have added, your selected region, theme, language and map
preferences. This data never leaves your device unless you back up your device
through Apple or Google. Deleting the app deletes it.

## Network connections the app makes

To work, the app connects to:

1. **The Beacon server you choose.** By default this is `dev.meshcore.ca`; you
can add or switch to any other Beacon server, including one you host. The app
requests packet, node, observer and statistics data from it over HTTPS or
WebSocket. Like any web server, that server sees your IP address and the
requests made. A server you add yourself is run by its own operator, under
that operator's own policies.
2. **Map tile providers.** Map imagery is loaded from
[OpenFreeMap](https://openfreemap.org) and elevation data from the public
AWS Terrain Tiles dataset. These providers see your IP address and the map
areas requested, as with any map.

The app sends no personal information to these services beyond what any network
request carries, such as your IP address.

## Mesh data shown in the app

The app displays radio traffic that MeshCore devices broadcast publicly, as
collected by Beacon servers: node names, public keys, advertised positions,
packet routes, signal readings and messages on public channels. This is not
data about you as an app user. If you operate a MeshCore node and want data
about it removed from a Beacon server, contact that server's operator. For the
default server, open an issue at
[github.com/MeshCore-Beacon/beacon-docs/issues](https://github.com/MeshCore-Beacon/beacon-docs/issues).

## Children

The app is not directed at children and collects no personal information from
anyone.

## Changes

Updates to this policy will be published on this page with a new date.

## Contact

Questions about this policy: [MrAlders0n@smal.ca](mailto:MrAlders0n@smal.ca)
or [open an issue](https://github.com/MeshCore-Beacon/beacon-flutter-app/issues).
33 changes: 33 additions & 0 deletions docs/mobile/support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# BEACON Mobile Support

BEACON Mobile is the iOS and Android client for Beacon, the real-time MeshCore
packet analyzer.

## Getting started

The app connects to the public Beacon server at `dev.meshcore.ca` out of the
box, so live packets, the map and analytics appear right away. To use a
different Beacon server, open **Settings → Servers** and add its address.
Running your own server is covered in the
[deployment guide](https://github.com/MeshCore-Beacon/beacon-docs#readme).

## Beta testing

- **iPhone and iPad:** join through the public TestFlight link and send feedback
from the TestFlight app (take a screenshot in BEACON to attach it).
- **Android:** join the Google Play testing program from the opt-in link, then
leave feedback on the app's Play page.

## Report a problem or request a feature

[Open an issue on GitHub](https://github.com/MeshCore-Beacon/beacon-flutter-app/issues)
with your device model, OS version, the app version (Settings → About) and what
happened.

## Contact

Email [MrAlders0n@smal.ca](mailto:MrAlders0n@smal.ca).

## Privacy

See the [privacy policy](privacy.md).
Loading
Loading