Skip to content

Strict Gatekeeper

Builder Bob edited this page Jun 20, 2026 · 5 revisions

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.

Plain-English purpose

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.

What happens when you flip it on

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.

Where it sits in the code

  • gatekeeper.uc — policy engine
  • router.uc:157 — the single enforcement call: gatekeeper.filterInboundBridge(msg) runs on every queued meshtastic or meshcore message
  • STRICT_GATEKEEPER.md in 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.

Example configuration

{
  "callsign": "W6XYZ",
  "strict_gatekeeper": {
    "enabled": true,
    "gateway_callsign": "W6XYZ",
    "allowed_callsigns": ["KN6PLV", "KJ6DZB"]
  }
}

Configuration fields

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.

How messages are rewritten

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.

Forwarding decision table

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.

Why this matters for Part 97 auto-forwarding

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 concepts in plain language

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.

Callsign matching limits

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.

Recommended operating modes

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.

Per-Channel Access Control

Strict Gatekeeper supports optional per-channel callsign rules. When configured, each channel can enforce its own allow/deny list independently of the global whitelist.

Configuration

{
  "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
      }
    }
  }
}

Access control fields

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.

Wildcard patterns

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.

Enforcement order

Per-channel access control runs before the global filterInboundBridge check in the router:

  1. Group slot resolution (MeshCore group messages)
  2. Per-channel ACLenforceChannelAccess()
  3. Global gatekeeper — filterInboundBridge()
  4. Message delivered to channel

A message must pass both the per-channel ACL and the global gatekeeper to reach a channel.


Group Message Tagging (MeshCore)

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.

Why this matters

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.


What Strict Gatekeeper does NOT do

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.

Logging and review

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.

Part 97 rule anchors for operators

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

Related

Crow Wiki

Pages

Markdown files

  • APRS.md
  • Backend-Selection-and-Deployment.md
  • Change-Log.md
  • Command-Reference.md
  • Configuration.md
  • Configuring-Channels.md
  • Home.md
  • LoRa-Gateway-Tags.md
  • Meshtastic-API.md
  • Memory-Use.md
  • Strict-Gatekeeper.md
  • USB-Storage.md
  • Winlink.md
  • _Sidebar.md

Maintenance

  • Keep every .md wiki page linked here.
  • Keep Home.md and _Sidebar.md in sync.
  • When a wiki page is removed, remove it from both the Home page inventory and this sidebar.

Clone this wiki locally