A real-time Morse code practice platform where users can practice CW (Continuous Wave) together over the internet.
The official Vail repeater is located at vailmorse.com.
We encourage everyone who wants to use Vail to join us there! Having a central community helps CW enthusiasts find each other and practice together, rather than splitting into isolated groups on separate servers.
That said, this project is open source and you're welcome to run your own instance for development, testing, or specific community needs.
We welcome pull requests that align with the Vail Community Values:
- Making Morse code practice accessible and enjoyable
- Building a welcoming, inclusive community for CW enthusiasts of all skill levels
- Keeping the platform simple and focused on its core purpose
Please open an issue first to discuss significant changes before submitting a PR.
- Multiple keyer modes: Straight key, bug, iambic, ultimatic, and more
- Physical key support: Connect real CW keys via Arduino adapter
- Break-in control: Toggle broadcasting to others on/off
- Adjustable timing: WPM rate, RX delay, tone frequency
- Visual feedback: Real-time charts showing key presses and transmissions
- Live decoder: Adaptive Morse code decoder for real-time transcription
- Public rooms: General, Channel 1-3, Echo, Null, Fortunes, Decoder
- Custom rooms: Create named rooms (public or private)
- Private rooms: Not shown in public room list, shareable by name
- Room list display: See all active public rooms with user counts
- Auto-reconnect: Handles disconnections gracefully
- Text chat: Send/receive messages in any room
- Chat history: Last 50 messages preserved per room
- User list: See all connected users with callsigns
- Callsign system: Set your callsign or use anonymous mode
- Event scheduling: Create/edit/delete events with date, time, timezone
- Recurring events: Daily, weekly, or monthly repeating events
- Timezone conversion: Automatically shows events in user's local time
- DST support: Handles Daylight Saving Time correctly
- Calendar view: Monthly calendar showing all scheduled events
- WebSocket protocol: Binary or JSON message formats
- Inactivity timeout: Users disconnected after 30 minutes of no activity
- Rate limiting: Prevents message floods
- Dark mode: Forced dark theme for better visibility
- Responsive design: Works on desktop and mobile
- PWA support: Installable as a Progressive Web App
cmd/vail/main.go- HTTP server, WebSocket handler, routingcmd/vail/book.go- Room management, user tracking, broadcastscmd/vail/repeater.go- Per-room message relay, chat historycmd/vail/message.go- Message encoding/decoding (binary/JSON)cmd/vail/events.go- Events API with Firestore backendcmd/vail/discord.go- Discord webhook integrationcmd/vail/enigma.go- Enigma machine simulation
static/scripts/vail.mjs- Main client, keyer logic, UIstatic/scripts/repeaters.mjs- WebSocket client, auto-reconnectstatic/scripts/events.mjs- Events calendar UI and APIstatic/scripts/keyers.mjs- Keyer implementationsstatic/scripts/outputs.mjs- Audio generation (Web Audio API)static/scripts/inputs.mjs- Keyboard/gamepad input handlingstatic/scripts/decoder.mjs- Morse code decoder integration
- Events: Google Cloud Firestore (when
GCP_PROJECTis set) - Runtime state: All in-memory (rooms, clients, messages)
- No database required for basic operation
- Go 1.24 or later
- (Optional) Google Cloud project for Firestore events storage
- (Optional) Discord webhook for join notifications
go build -o build/vail ./cmd/vail./build/vailThe server starts on port 8080 by default. Open http://localhost:8080 in your browser.
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 8080 |
HTTP server port |
GCP_PROJECT |
No | - | Google Cloud project ID for Firestore events |
ADMIN_CALLSIGNS |
No | - | Comma-separated list of admin callsigns |
ADMIN_PASSWORD |
No | - | Password for admin authentication |
DISCORD_WEBHOOK_URL |
No | - | Discord webhook for join notifications |
BASE_URL |
No | http://localhost:8080 |
Base URL for room links in Discord messages |
Note: Admin features are disabled unless both ADMIN_CALLSIGNS and ADMIN_PASSWORD are set.
go test ./cmd/vailA Dockerfile is provided for containerized deployments:
docker build -f build/Dockerfile -t vail-repeater .
docker run -p 8080:8080 vail-repeaterThe project is designed to run on Google Cloud Run or similar platforms.
- CPU: 1.0 (required for concurrency > 1)
- Memory: 512Mi
- Concurrency: 1000 (all users need to be on the same instance)
- Max Instances: 1 (shared state requires single instance)
- Min Instances: 1 (prevents cold starts)
- Timeout: 3600 seconds (supports long-lived WebSocket connections)
- Session Affinity: Enabled
WebSocket rooms require shared state. All users must connect to the same instance to see each other. Multiple instances would create isolated "rooms" where users couldn't interact.
| Room | Description |
|---|---|
| General | Default room for general practice |
| 1, 2, 3 | Additional public channels |
| Echo | Echoes back your transmissions (solo practice) |
| Null | Receive-only, no transmit |
| Fortunes | Automated fortune cookie transmissions |
| Decoder | Built-in live Morse decoder enabled |
| Custom | User-created rooms (public or private) |
See docs/protocol.md for the WebSocket protocol specification.
Supported subprotocols:
binary.vailmorse.com- Binary encoding (production)json.vailmorse.com- JSON encoding (debugging)
Vail can post notifications to a Discord channel when users join public rooms.
- Create a webhook in your Discord server (Server Settings > Integrations > Webhooks)
- Set the
DISCORD_WEBHOOK_URLandBASE_URLenvironment variables - Restart the server
- Notifications for public room joins only (not private rooms)
- Session-based notifications with message editing (shows join/leave times)
- Anonymous users filtered out
- 2-minute grace period for reconnections (prevents duplicate notifications)
When deploying changes to static files, update the version to bust browser caches:
cd static
.\update-version.ps1 1.0.10Or manually update static/version.js and the ?v= parameters in HTML files.
Based on Vail by Neale Pickett.
Currently maintained by KE9BOS Brett Hollifield, who operates the official Vail Repeater at vailmorse.com.