SignalForge

Voice in the Storm

Community-powered radio monitoring for Yukon, Oklahoma and beyond. Run a hub, add a recorder, and share trusted local signals without towers or gatekeepers — including off-grid cells that never phone home to the cloud.

Listen live ↓ Why we're building this ↓

Yukon Cell — Live Monitor

Real traffic from the hosted SignalForge hub. Tap LIVE ON in the console below to hear calls as they land — same embeddable player operators share from any radio set.

Browsers require a click inside the player to start audio — use LIVE ON.

// origin

Why We're Here

This started with a simple idea: Yukon deserved a public GMRS repeater — a reliable community radio hub that would keep neighborhoods connected when cell towers go dark and power grids fail. We put together the proposal, found the tower, reached out to the city.

The city didn't answer. The tower owner went quiet. The project stalled.

Getting ghosted didn't kill the idea. It sharpened it. If we couldn't own the infrastructure, we'd build something that didn't need it.

SignalForge Hub is what came next — a software-defined radio monitoring platform where community members contribute scanner, repeater, SDR, or streamed audio sources. No tower lease. No $5,800 equipment budget. No waiting on a committee. Every person who runs a recorder expands the network.

No Infrastructure Required

Distributed architecture. Feed audio from a radio, scanner, SDR setup, or existing stream.

Community Owned

Every contributor shapes the network. No single point of control.

Always Resilient

Built for the moments when conventional systems go silent.

// download

SignalForge CLI

SignalForge CLI is the new one-binary recorder path for Windows, macOS, and Linux. Point it at an audio folder, validate your hub and source key, and upload calls without installing a separate desktop recorder console.

fetching latest release...
1 Install with Homebrew or Scoop, or download the release archive for your platform above.
2 Add it to your PATH and open a terminal.
3 Run sf recorder watch --input ./calls with your hub URL and source key. signalforge still works too.
brew tap CptPlastic/signalforge
brew install signalforge

scoop bucket add signalforge https://github.com/CptPlastic/scoop-signalforge
scoop install signalforge

sf onboard
sf rec check --source-key sk_live_REPLACE_WITH_SOURCE_KEY
sf rec inspect --input ./Input
sf rec w --input ./Input --source-key sk_live_REPLACE_WITH_SOURCE_KEY
sf update check

Monitoring modes

Pick one mode when you run sf rec w (folder watch). Profile settings live in ~/Library/Application Support/signalforge/profile.json on macOS.

Mode 1 — Folder only

Watch your ingest folder and upload new audio as calls land. No heartbeat clips.

sf rec w --input ./Input

Mode 2 — Canary only

Upload an audible pip on an interval so the hub knows the recorder is alive. Minimum interval is 30s.

sf rec w --canary --canary-interval 2m

Mode 3 — Folder + canary

Real traffic from the folder plus a periodic heartbeat. Use sf rec stop to end a watch session cleanly.

sf rec w --input ./Input \
  --canary --canary-interval 2m

Beacon (optional)

Re-upload a fixed audio clip on a schedule (separate from the canary pip). Handy for scheduled check-ins.

sf rec w --beacon \
  --beacon-file ./beacon.wav \
  --beacon-interval 30m
// onboarding

Create an Account on the Main Hub

Use the hosted SignalForge hub if you want to listen, contribute a recorder, or try the network before running your own hub.

  1. Open the main hub at p7hub.projectseven.us or pick a hub from the mobile app directory.
  2. Click Account in the top navigation bar (web) or sign in on the mobile app.
  3. Password hubs: enter your email and password when the hub has password login enabled.
  4. Email hubs: request a magic link, check your inbox, then paste the token or 6-digit code.
  5. After sign-in, open Radio Sets to listen live or create your own set.
Need help or want to connect with other contributors?
Email us or join our Discord (coming soon).
// operator path

Run Your Own Hub

The recommended install path is plain Docker Compose with official SignalForge images. No single-platform package, no hosted template, no lock-in: one generated environment file, one compose stack, and a PostgreSQL volume you can back up or move.

For a local test, install Docker Desktop, make sure it is running, and use http://localhost:3000 as the public hub URL when the helper asks.

git clone https://github.com/CptPlastic/signalforge-node.git
cd signalforge-node
SIGNALFORGE_PUBLIC_URL=http://localhost:3000 make init-env
make install-up
curl http://localhost:8080/api/v1/health

Put HTTPS in front of the web port with Caddy, nginx, Traefik, Portainer, or your hosting panel's reverse proxy. The web service handles public traffic and proxies API and WebSocket requests to the api service inside the stack.

1 Generate config. The helper asks for your public URL, hub name, ports, and mail settings.
2 Start Compose. Server, web console, and PostgreSQL run as separate services in one stack.
3 Own your data. Calls, users, sources, and hub identity live in your local PostgreSQL volume.
Off-Grid Cells Call Retention Install Notes Hub Source GitHub
// storage

Call Retention & Archive

Call audio lives inside PostgreSQL. High-traffic hubs can log a million calls in a month — without a retention policy the database volume fills up and the hub stops accepting new traffic.

SignalForge hubs export calls older than your retention window to day folders on disk, optionally sync them to S3-compatible object storage (DigitalOcean Spaces, MinIO, AWS S3), delete them from Postgres, and run VACUUM FULL when a sweep finishes so freed space returns to the operating system.

Automatic Sweeps

Set CALL_RETENTION_DAYS and CALL_ARCHIVE_DIR in the stack environment. The hub archives on a six-hour schedule — no cron job required.

One-Click Catch-Up

Hub admins open Account → Call Storage & Retention in the web console. Archive Now runs in batches until the backlog is cleared, with live progress.

Object Storage Offload

Point CALL_ARCHIVE_S3_URI at an S3-compatible bucket. Day folders sync via s3cmd before rows are deleted from the database.

Export Layout

Each archived call becomes a JSON manifest plus audio file under YYYY-MM-DD/call-{id}.mp3 — easy to browse, back up, or replay outside the hub.

Minimal retention setup for a Compose stack:

# Keep 14 days in Postgres; export older calls to a mounted volume
CALL_RETENTION_DAYS=14
CALL_ARCHIVE_DIR=/data/call-archive

# Optional: DigitalOcean Spaces (bucket name is the Space, not a subfolder)
CALL_ARCHIVE_S3_URI=s3://your-space/signalforge-hub/call-archive
SPACES_ACCESS_KEY=your-key
SPACES_SECRET_KEY=your-secret
SPACES_ENDPOINT=ams3.digitaloceanspaces.com
CALL_ARCHIVE_DELETE_LOCAL_AFTER_S3=true

Mount a spacious volume at CALL_ARCHIVE_DIR if you are not offloading to object storage immediately. Use DRY RUN in the Account panel first to see how many calls and how much audio would move before anything is deleted.

Retention API Hosted Hub
// off-grid

Off-Grid Hub Cells

Not every deployment should depend on Mailjet, a public directory, or an update manifest on the internet. SignalForge hubs can run as closed off-grid cells — a local PostgreSQL stack, password sign-in, recorder ingest, public radio-set players, and optional on-box transcription, with no outbound cloud services required.

Built for field ops, private networks, EOC laptops, and community cells that need to keep working when DNS, email, and the wider internet do not.

Password Sign-In

Bootstrap an admin with AUTH_BOOTSTRAP_EMAIL and AUTH_BOOTSTRAP_PASSWORD. Enable AUTH_PASSWORD_LOGIN_ENABLED so the web console and mobile app sign in without magic links.

Closed User Roster

Set AUTH_AUTO_APPROVE_USERS=true for small trusted cells, or keep approval on and manage accounts locally from the hub console.

No Phone Home

Leave Mailjet blank, set UPDATE_CHECK_URL= and HUB_DIRECTORY_URL= empty, and keep HUB_FEDERATION_ENABLED=false until you deliberately peer with another hub.

Group Playsets

Radio sets can follow talkgroup groups instead of hand-picked TG IDs — useful when your recorder tags traffic by agency or discipline and you want a share link that stays current automatically.

Local Speech-to-Text

The optional transcriber worker runs faster-whisper on your hardware. Audio never leaves the cell.

Capability Discovery

Clients call GET /api/v1/auth/capabilities to learn whether password login, email codes, or both are available before showing a sign-in screen.

Minimal off-grid environment for a Compose stack:

# Bootstrap admin (created or updated on API startup)
AUTH_BOOTSTRAP_EMAIL=admin@local.signalforge
AUTH_BOOTSTRAP_PASSWORD=change-me-strong
AUTH_PASSWORD_LOGIN_ENABLED=true
AUTH_AUTO_APPROVE_USERS=true

# Stay local — no cloud callbacks
UPDATE_CHECK_URL=
HUB_DIRECTORY_URL=
HUB_FEDERATION_ENABLED=false

# Mailjet can stay blank; magic-link sign-in is simply unavailable

After the stack starts, open the hub console Account tab and sign in with the bootstrap email and password. The mobile app reads the same capabilities endpoint and switches to password mode when email delivery is not configured.

Operator Notes Auth API
// speech-to-text

Transcription

SignalForge Hub ships with an optional self-hosted transcription worker that turns recorded calls into searchable text — no third-party API key, no audio leaving your infrastructure. It runs faster-whisper inside a Docker container and pulls jobs from the hub's internal queue.

Enable the transcriber service in your Compose stack and calls are automatically queued for transcription when they arrive. Results appear in the web console alongside each call.

Local & Private

Runs entirely on your hardware. Audio is never sent to an external service.

Configurable Model

Choose the Whisper model size that fits your hardware. small is the recommended default — meaningfully better than base while still practical on CPU.

Radio-Optimized

Set a domain prompt to prime the model with scanner vocabulary — ten-codes, callsigns, local streets — before it touches a single call.

Key environment variables for the transcriber service:

# Model size — tiny · base · small · medium · large-v3
TRANSCRIPTION_MODEL=small

# Comma-free phrase list that primes the model for your traffic
TRANSCRIPTION_INITIAL_PROMPT=Police and fire dispatch radio traffic. Units responding. 10-4. Copy. Dispatch. Requesting backup. Officer down. Units on scene. Negative. Affirmative. Stand by. All units. Channel.

# Force a language or leave blank for auto-detect
TRANSCRIPTION_LANGUAGE=en

# Run on CPU (default) or cuda if you have a GPU
TRANSCRIPTION_DEVICE=cpu

Larger models produce better accuracy but require more RAM and CPU time. small is a good starting point for most deployments. medium or large-v3 are worth trying if you have the hardware and want the best results on noisy radio audio.

// push-to-talk

Push-to-Talk Inside a Radio Set

Hubs can carry more than just received radio traffic. Authorized users in a hub can press-and-hold a button — in the web client or a per-set "PTT mode" screen — to record a short audio message that is delivered to every listener of that radio set, alongside the real radio. Each PTT call gets a distinct amber badge, a Nextel-style chirp before playback, and the sender's identity in the call log, so listeners always know they're hearing a person, not the radio.

PTT is an opt-in capability. A hub admin enables tx_enabled per user, so a hub can be a pure listen-only system or a coordinated comms channel — your choice.

Per-Set Virtual Talkgroup

Every radio set gets its own PTT talkgroup (9_000_001+) auto-allocated on creation. PTT calls land on that channel so they fan out only to that set's listeners.

Hold-to-Talk in the Browser

Press and hold the PTT button — or hold spacebar on a dedicated PTT screen — to record. On release, the clip uploads, lands as a call, and plays for everyone on the radio set in seconds.

Dispatcher Broadcast

Users with the dispatcher_enabled flag can key multiple radio sets at once, delivering a single transmission across many talkgroups — useful for coordinated alerts across a hub's listener groups.

Listener Cue

Every PTT call plays a short two-tone chirp before the audio so listeners instantly know it's a person keying, not a scanned transmission. The call log labels the sender by email.

Idempotent Upload

Clients generate a UUID per transmission. Network retries return the same call without creating duplicates — important for mobile PTT on spotty connections.

Native Mobile App

SignalForge Mobile v1.0.1 — iOS and Android with live monitor, PTT, dispatcher broadcast, and background playback. App Store & Play coming soon; TestFlight and internal builds available now.

Endpoints surfaced by the PTT subsystem:

# Single-set PTT (held button on one radio set)
POST /api/v1/radio-sets/{id}/ptt
multipart: audio, duration, clientId

# Multi-set dispatcher broadcast (one keying, N talkgroups)
POST /api/v1/ptt/broadcast
multipart: audio, duration, clientId, radioSetIds=id1,id2,id3

Both endpoints require an active session with tx_enabled (and dispatcher_enabled for the broadcast endpoint). Guests cannot transmit.

// operator display

Built for operators who work in the dark

SignalForge web, hub console, and mobile app share a tactical display cycle — DARK → NITE → NVG — modeled after field coordination tools like SchedKit. The cycle never passes through full brightness, so you won't flash-blind NVG or night-adapted eyes mid-op.

◑ DARK

Default amber phosphor on CRT black — console monitoring and PTT.

▌ NITE

Dim red palette for naked-eye dark adaptation between screen checks.

◈ NVG

Ultra-low luminance blue-grey — minimum software glow for Gen III tubes.

☀ LIGHT

Daylight paper terminal — click ☀ LIGHT in the nav (or long-press DARK on touch).

Use the mode bar in the nav on any page, or read DISPLAY-MODES.md for palette tokens and per-product implementation notes.

// source

Build Your Own Hub

signalforge-node is the public, buildable source for the hub stack: server, web client, recorder clients, compose files, operator docs, and SignalHub federation code. The public repo stays buildable, documented, and free of local secrets or deployment-only clutter.

Yes, the clients belong in the mirror. A hub is not just the Go server. Operators need the React console, recorder tools, and container recipes to run the whole stack without reverse-engineering any projectseven deployment.

Server

Go API, database migrations, ingest endpoints, auth, hub identity, and federation routes.

Web Console

React client for monitoring calls, managing sources, radio sets, peers, and updates.

Recorder Clients

Desktop and CLI tools that turn local scanner, SDR, or audio feeds into signal sources.

1 Clone signalforge-node, or pull the official containers from ghcr.io/signalforge-org.
2 Run make init-env to generate a local environment file, then deploy with make install-up.
3 Log in, create your hub identity, enable federation only when you are ready, and peer with trusted hubs by invite.
Source & Build Notes API Docs Hub Source GitHub
// federation

SignalHub

SignalHub is the next layer: a peer-to-peer pub/sub network for community scanner hubs. Each SignalForge Hub can publish local sources, subscribe to trusted remote sources, and keep control of what it shares downstream.

SignalForge is peer-to-peer first. Operators run their own hubs, connect by invite, and choose which local sources they share with trusted peers.

Publish

Local scanner, SDR, repeater, or stream sources announce live calls, metadata, and health.

Subscribe

Trusted hubs can pull selected sources or talkgroups without depending on one central server.

Validate

SignalForge can list known-good hubs while still allowing direct peer-to-peer trust.

The goal is simple: lots of community hubs sharing local sources, with origin preserved, resharing controlled, and operators staying in charge of their own infrastructure.
// directory

SignalForge Directory

The directory is a public trust list for known SignalForge hubs. It separates discovery from federation: operators can still peer directly by invite, while listed hubs can publish a directory status and trust level that local consoles can refresh on demand.

Community

Default local trust. The hub exists, but the directory has not vouched for it.

Listed

The hub is known to the directory and can be shown in public operator surfaces.

Verified

The operator and public URL have been checked by the SignalForge directory issuer.

Trusted

A known stable operator with useful community signal sources and clean behavior.

Official

Infrastructure operated by projectseven or SignalForge.

Suspended

A listed hub that should not currently be used for trust or discovery.

One-click register: hub console HUB tab → complete checklist → OPEN REGISTRATION FORMSubmit to Directory. That opens a GitHub review issue, emails you a receipt, and gives you a status link while we approve the listing.
Register Hub Track Listing Directory Feed
// roadmap

What We Are Building Next

SignalForge is intentionally layered: run a cell today, list it in the directory when you are ready, federate with peers when you want shared sources. These are the active platform tracks.

Operator polish

Monitoring mode docs, password/off-grid onboarding, admin user management, and hub registration wizard in the console.

Directory & discovery

Public hubs.json feed, mobile directory picker, trust levels, and the register-hub intake flow.

Federation

Peer invites, shared sources, imported remote calls — optional mesh between cells without a central tower.

Platform (later)

Custom RSS/JSON feeds, multi-protocol ingest, API key scoping, export tooling, and operator monitoring dashboards.

Want to help shape priority? Reach out — especially if you are running a hub cell and want directory listing or federation with Yukon.

// the vision

The Network

The original goal was a repeater at every Yukon landmark — a cohesive signal network stitched across the city. The hardware approach hit walls. The software approach has no walls.

Every SignalForge Hub instance can stand on its own. Every recorder running is a signal source. Contributors in Yukon, in OKC, anywhere — each one extends the reach of the network without a single piece of shared infrastructure.

The city didn't give us the tower. So we built something that doesn't need one.

If you want to contribute a scanner source or run your own hub, install the SignalForge CLI and reach out. SignalForge is community infrastructure — and you're the infrastructure.

Get Involved