-
Notifications
You must be signed in to change notification settings - Fork 0
Strict Gatekeeper
Strict Gatekeeper is Crow's optional fail-closed safety layer for bridge traffic entering Crow from Meshtastic or MeshCore before that traffic is forwarded into AREDN or other amateur-radio paths.
It is a software safety guard. It does not replace the control operator and it is not a legal-compliance engine. Its job is to make Crow's automatic or semi-automatic forwarding behavior easier to supervise under U.S. amateur-radio Part 97 expectations.
Strict Gatekeeper answers one question before Crow forwards traffic from Meshtastic or MeshCore into the amateur/AREDN side:
"Is this something I am willing to let my gateway station retransmit?"
If the answer is no, the packet is dropped before it enters the routing queue.
Once strict_gatekeeper.enabled is true, every bridged Meshtastic or MeshCore message is checked at router.uc:queue() before reaching the AREDN forwarding path. The checks are applied in order; the first failure drops the packet.
| Check | What Crow looks for | Pass behavior | Fail behavior |
|---|---|---|---|
| Encrypted ingress | Meshtastic encrypted packet detected before bridge forwarding | Continue to text check. | Packet dropped. No decrypt attempt. |
| Text-only | Message contains data.text_message
|
Continue to identity check. | Non-text bridge traffic dropped. |
| Sender identity | Sender name/metadata contains a US-style callsign token | Callsign extracted and used downstream. | Dropped as unidentified. |
| Whitelist | Sender callsign appears in allowed_callsigns (when non-empty) |
Continue. | Dropped as not whitelisted. |
| Gateway annotation | Accepted text is rewritten as [SENDER via GATEWAY] message
|
Forwarded into AREDN with attribution. | n/a |
When disabled, the router uses normal behavior with no extra checks.
-
gatekeeper.uc— policy engine -
router.uc:157— the single enforcement call:gatekeeper.filterInboundBridge(msg)runs on every queuedmeshtasticormeshcoremessage -
STRICT_GATEKEEPER.mdin the repo root — developer-facing source-of-truth notes
There is no second enforcement point. Backends never call the gatekeeper directly — the router does it once on every queued bridge message, which is also why the [SENDER via GATEWAY] annotation never stacks.
{
"callsign": "W6XYZ",
"strict_gatekeeper": {
"enabled": true,
"gateway_callsign": "W6XYZ",
"allowed_callsigns": ["KN6PLV", "KJ6DZB"]
}
}| Field | Type | Required? | Description | Recommended use |
|---|---|---|---|---|
enabled |
boolean | Yes | Turns Strict Gatekeeper on or off. |
true for public, event, or unattended bridge gateways. |
gateway_callsign |
string | Strongly recommended | Callsign used to identify the forwarding gateway in annotations. | Use the gateway/control operator callsign. Falls back to top-level callsign. |
allowed_callsigns |
array of strings | Optional | Whitelist of callsigns allowed to use the bridge. Empty means "any valid-looking callsign may pass." | Use a whitelist for real deployments. |
Accepted bridge traffic is rewritten so the forwarded text identifies both the apparent sender and the gateway station.
Original bridged text from KJ6DZB:
radio check from the hill
Forwarded by gateway W6XYZ:
[KJ6DZB via W6XYZ] radio check from the hill
This makes the forwarding path visible to downstream users and gives the gateway operator something audit-friendly.
| Incoming traffic | Strict Gatekeeper off | Strict Gatekeeper on | Why |
|---|---|---|---|
| Plain text from Meshtastic/MeshCore with valid callsign-like sender | Routed normally. | Allowed, annotated. | Plain-text, identifiable. |
| Plain text from allowed callsign | Routed normally. | Allowed, annotated. | Sender is on the approved list. |
| Plain text from callsign not in whitelist | Routed normally. | Dropped when whitelist is configured. | Gateway policy restricts who may use the bridge. |
| Plain text with no callsign-looking sender | Routed normally. | Dropped. | Sender is not sufficiently identified. |
| Encrypted Meshtastic packet | Decrypted/handled by normal Meshtastic logic. | Dropped before bridge forwarding. | Avoids forwarding obscured traffic into amateur paths. |
| MeshCore/Meshtastic non-text payload | Routed/handled normally depending on app behavior. | Dropped. | Strict bridge policy is text-only. |
| Native Crow/AREDN-originated message | Routed normally. | Not treated as bridge ingress. | Gatekeeper is for bridge ingress only. |
| Already annotated gateway traffic | Routed normally. | Allowed if it already carries this gateway's identity. | Avoids double-wrapping. |
Crow can connect non-amateur mesh transports (Meshtastic, MeshCore) to AREDN or other amateur-radio paths. When Crow automatically forwards a message, the gateway station may transmit content that did not originate from the control operator at the gateway. That creates real Part 97 concerns:
- amateur transmissions need responsible station control;
- messages should not be obscured/encrypted for the purpose of hiding meaning;
- station identity matters;
- third-party and automatically forwarded messages need operator attention;
- a gateway should not blindly retransmit unknown traffic.
Strict Gatekeeper gives the gateway an enforceable policy point before forwarding occurs.
| Part 97 concept | Plain meaning | Why a Crow gateway cares | How Strict Gatekeeper helps |
|---|---|---|---|
| Control operator | A licensed operator is responsible for station transmissions. | A bridge does not remove operator responsibility. | Requires/uses a gateway callsign and creates visible gateway attribution. |
| Automatic control | A station may transmit without a human pressing send only where the rules allow it. | Crow may automatically forward traffic once configured. | Filters traffic before it reaches the forwarding path. |
| Message forwarding system | A system that accepts and forwards messages between stations. | Crow can behave like one when bridging traffic. | Adds sender checks, text-only filtering, and gateway annotation. |
| First forwarding station | The first amateur-side station accepting a message has special responsibility. | Crow may be the first amateur/AREDN hop for Meshtastic or MeshCore traffic. | Whitelists and callsign checks reduce unknown-origin forwarding. |
| No obscured meaning | Amateur traffic should not hide meaning with encryption or codes, except where specifically permitted. | Encrypted mesh packets should not be blindly forwarded into Part 97 traffic. | Drops encrypted Meshtastic packets before bridge forwarding. |
| Station identification | Transmissions should identify responsible stations. | Downstream users need to know who originated and who forwarded. | Rewrites messages as [SENDER via GATEWAY] .... |
| Operator accountability | The gateway owner should be able to explain what crossed the bridge. | Logs and message annotation matter during events. | Drops policy failures and makes accepted traffic easier to audit. |
The built-in callsign matcher intentionally uses a simple U.S.-style callsign token:
1 or 2 letters + 1 number + 1 to 3 letters
Examples likely to pass: KJ6DZB, KN6PLV, W6XYZ.
Examples likely to fail: Tactical 1, Net Control, Portable Hill, DX1ABC/P.
This is conservative by design. For international callsigns, special-event callsigns, tactical labels, or suffix-heavy formats, expand the validator deliberately rather than loosening it.
| Deployment | Recommended setting | Notes |
|---|---|---|
| Private lab, no RF or AREDN forwarding | Optional | Useful for testing; lower risk if nothing leaves the lab. |
| AREDN-only amateur deployment | Enable with gateway_callsign
|
Keeps gateway identity visible. |
| Public Meshtastic-to-AREDN bridge | Enable with gateway_callsign and allowed_callsigns
|
Avoids becoming a public blind relay. |
| MeshCore-to-AREDN bridge | Enable with whitelist | MeshCore identity should be constrained before forwarding. |
| Emergency/event bridge | Enable with a preloaded whitelist | Tie the bridge to assigned operators/callsigns. |
| Unattended gateway | Enable, whitelist, monitor logs | Best fit for fail-closed behavior. |
Strict Gatekeeper supports optional per-channel callsign rules. When configured, each channel can enforce its own allow/deny list independently of the global whitelist.
{
"strict_gatekeeper": {
"enabled": true,
"gateway_callsign": "W6XYZ",
"allowed_callsigns": ["KN6PLV", "KJ6DZB"]
},
"channels": {
"#TacNet base64key==": {
"access_control": {
"require_callsign": true,
"allowed_callsigns": ["K6*", "W2*"],
"deny_callsigns": ["K6SPAM"]
}
},
"#OpenNet base64key==": {
"access_control": {
"require_callsign": false
}
}
}
}| Field | Type | Description |
|---|---|---|
require_callsign |
boolean | When true, messages without a valid callsign are dropped for this channel. |
allowed_callsigns |
array of strings | Glob-style patterns. Only matching callsigns are allowed. Empty or omitted means no restriction. |
deny_callsigns |
array of strings | Glob-style patterns. Matching callsigns are always denied, even if they match the allow list. |
Patterns use * as a wildcard matching zero or more characters.
| Pattern | Matches | Does not match |
|---|---|---|
K* |
K6DZB, KN6PLV, K0ABC | W2ABC, N5DZB |
*DZB |
K6DZB, W2DZB, N5DZB | K6PLV, W2ABC |
K6DZB |
K6DZB (exact) | K6PLV, W6DZB |
K6* |
K6DZB, K6PLV | KN6PLV, W6XYZ |
Deny patterns are checked first. If a callsign matches both an allow and a deny pattern, the deny wins.
Per-channel access control runs before the global filterInboundBridge check in the router:
- Group slot resolution (MeshCore group messages)
-
Per-channel ACL —
enforceChannelAccess() - Global gatekeeper —
filterInboundBridge() - Message delivered to channel
A message must pass both the per-channel ACL and the global gatekeeper to reach a channel.
MeshCore group messages use symmetric pre-shared keys. Any radio with the group’s AES key can send messages that appear to come from any group member. This is weak identity — the sender proves group membership, not individual identity.
When Strict Gatekeeper is enabled and a group message crosses into AREDN, Crow tags it with a distinct format that makes the group context visible:
[K6DZB@MCGW-TacNet via W6XYZ] radio check from the hill
Breakdown:
| Component | Meaning |
|---|---|
K6DZB |
Sender callsign (from node database) |
@MCGW-TacNet |
MeshCore Gateway, group name "TacNet" |
via W6XYZ |
Gateway station forwarding the message |
This differs from direct-message tagging ([K6DZB via W6XYZ]) by including the @MCGW-GroupName identifier, making it clear the message originated from a group channel with weak identity.
Group messages satisfy Part 97 station identification requirements differently than direct messages:
- Direct messages: sender proved identity via asymmetric key (strong identity).
- Group messages: sender proved group membership via symmetric key (weak identity).
The @MCGW-GroupName tag makes this distinction visible to downstream operators and auditors.
Strict Gatekeeper is a bridge policy tool, not a full identity or legal-compliance system.
It does not:
- prove a person legally owns a callsign;
- verify a callsign against FCC/ULS records;
- bind a callsign to a Meshtastic node ID or a MeshCore public key;
- decide whether a band, emission, bandwidth, or automatic-control segment is legal;
- approve third-party traffic;
- make encrypted content lawful for amateur forwarding;
- replace the station licensee or control operator.
For stronger event/public deployments, pair allowed_callsigns with known Meshtastic node IDs or MeshCore public keys as a hardening step. That is a future direction.
Strict Gatekeeper uses Crow's DEBUG0 / DEBUG1 channels for drop messages. On OpenWrt or AREDN nodes, confirm that logs are captured by the service manager or logd before relying on them for after-action review.
Useful things to verify during operations:
| Item | Why it matters |
|---|---|
| Gateway callsign loaded | Confirms forwarded messages will identify the gateway. |
| Whitelist loaded | Confirms the bridge is not open to everyone. |
| Drop counts | Shows whether unknown or encrypted traffic is trying to cross. |
| Accepted message examples | Confirms annotation is readable and useful. |
Review the current FCC/eCFR text directly. The most relevant sections:
- 47 CFR § 97.109 — station control / automatic control
- 47 CFR § 97.113 — prohibited transmissions, including obscured meaning and automatic retransmission limits
- 47 CFR § 97.115 — third-party communications
- 47 CFR § 97.119 — station identification
- 47 CFR § 97.219 — message forwarding systems
- 47 CFR § 97.221 — automatically controlled digital stations
- 47 CFR § 97.309 — RTTY/data emission codes and public documentation
Main eCFR page: https://www.ecfr.gov/current/title-47/chapter-I/subchapter-D/part-97
-
STRICT_GATEKEEPER.md— developer source-of-truth notes -
gatekeeper.uc— implementation - LoRa Gateway Tags — outbound-side tagging that complements gatekeeper attribution
- Home
- Change Log
- Configuration
- Configuring Channels
- Backend Selection and Test Deployment
- Command Reference
- APRS Bridge
- LoRa Gateway Tags
- Meshtastic API Backend
- Memory Use
- Strict Gatekeeper
- Winlink
- USB Storage
APRS.mdBackend-Selection-and-Deployment.mdChange-Log.mdCommand-Reference.mdConfiguration.mdConfiguring-Channels.mdHome.mdLoRa-Gateway-Tags.mdMeshtastic-API.mdMemory-Use.mdStrict-Gatekeeper.mdUSB-Storage.mdWinlink.md_Sidebar.md
- Keep every
.mdwiki page linked here. - Keep
Home.mdand_Sidebar.mdin sync. - When a wiki page is removed, remove it from both the Home page inventory and this sidebar.