All apps · 0 apps
mynah
Docker app from Stoatworks Labs' Repository
Overview
Readme
View on GitHubAI-assisted project. This codebase was created with Claude Code. It has driven a real switcher: every verb in the grammar was executed against a physical Aquilon C on firmware 6.2.73 — store, recall, label, delete, a layer memory, a program recall, a Take and the six-op masked master store — with the filters read back exactly as compiled, and the hardware found two bugs the simulator never showed. Neither the X-Keys nor the Stream Deck path has ever been run against real hardware. See Status.
Mynah
A lighting-desk command line for Analog Way LivePremier (Aquilon) switchers.
Recall Screen 1 Memory 5
Store Master 12 If Screen 1 + 3 Category Source + Position
Recall Screen 1 Thru 4 Memory 7 Program
Take
Type the command, press Enter. The grammar rules are grandMA3's — verb first,
any unambiguous abbreviation, Thru / + / - ranges, an If clause that
masks a store. The vocabulary is the switcher's own: Screen, Aux, Layer,
Master, Memory, Preview, Program. Nothing here calls anything a fixture or a
cue.
It runs in a browser with nothing installed, talks to the switcher directly, and drives the same commands from a keyboard, an X-Keys panel, or a Stream Deck.
Download
v1.3.4 — prebuilt for macOS and Linux. Pick your platform:
macOS — Apple Silicon, Intel
| Build | Download | Size |
|---|---|---|
| Apple Silicon · .dmg disk image | Mynah_1.3.4_aarch64.dmg |
2.2 MB |
| Intel · .dmg disk image | Mynah_1.3.4_x64.dmg |
2.3 MB |
Linux — x64
| Build | Download | Size |
|---|---|---|
| x64 · .deb package (Debian/Ubuntu) | Mynah_1.3.4_amd64.deb |
2.8 MB |
| x64 · .rpm package (Fedora/RHEL) | Mynah-1.3.4-1.x86_64.rpm |
2.8 MB |
Also in this release:
Mynah_1.3.4_aarch64.app.tar.gz— Source tarball, 2.2 MBMynah_1.3.4_x64.app.tar.gz— Source tarball, 2.3 MB
All builds, checksums and release notes: github.com/stoatworks-labs/mynah/releases.
macOS builds are signed and notarised by Apple, so they open normally — no Gatekeeper warning and no quarantine step.
Why
A LivePremier has a lot of memories — 1,000 screen, 500 master, 50 layer — and the vendor Web RCS reaches them by pointing at things. Anyone who has operated a lighting desk knows the alternative: say what you want, in one line, without your eyes leaving the screen. That is all this is.
What it does today
Memories — recall, store, delete, label, take, and scope.
Live layer control — source, size, position and opacity, on any layer of any screen:
Set Screen 3 Layer 2 Source 1 Size 50% Position 33% 50%
Percentages resolve against the screen's real canvas, and position is the layer's centre because that is the device's anchor.
| Bank | Slots | Scope |
|---|---|---|
| Screen | 1–1000 | Screen S1–S24, Aux A1–A96, × Preview/Program |
| Master | 1–500 | record mask: screens, auxes, layers, categories |
| Layer | 1–50 | Screen × Preview/Program × layer (Native, 1–128) |
| Multiviewer | 1–50 | output |
Four command languages — the same line accepts Mynah, raw AWJ, raw Web RCS store JSON, and OSC. These are the same write:
Take Screen 1
AWJ DeviceObject/$screenAuxGroup/@items/S1/control/@props/xTake = true
{"path":["device","screenAuxGroupList","items","S1","control","pp","xTake"],"value":true}
/lp/screen/1/take
Each line is read as whichever language it looks like, and the verdict is shown
as you type; a leading MYNAH, AWJ, JSON or OSC says which outright, and
the picker can turn detection off altogether. See
docs/LANGUAGES.md.
New to it? Start with the programming guide — every command from one word upwards, with worked examples you can paste into the simulator. The full grammar is in docs/SYNTAX.md, and the device paths behind it, with the firmware they were verified on, are in docs/PATHS.md.
Two things worth knowing
A recall never reaches program by accident. An unqualified Recall goes to
Preview. An unqualified Store takes from Program, because that is the look
you just made live — and the device's own default agrees. Getting to air always
costs an explicit word: Program, or Take.
An empty memory is reported as empty. The device accepts a recall of a slot
that holds nothing, and then does nothing — no error, no response at all. Mynah
watches for that silence and says empty memory rather than done, because a
green tick on a command that changed nothing is worse than no feedback.
Connecting
The browser cannot open a raw TCP socket, so the documented AWJ protocol on port 10606 is out of reach from a page. Mynah uses the Web RCS WebSocket instead — the same socket the vendor's own UI speaks, on port 80 of a device or 3000 of the simulator. It needs no bridge and no install, has no five-client cap, and pushes state without a subscription list to get wrong.
An AWJ message typed at the command line still runs in a browser: it is
converted to the store spelling and lands at the same node. What a page cannot
do is read one back, because that socket carries changes rather than answers.
The desktop app opens a real AWJ socket, which is what makes
AWJ get DeviceObject/system/$device/@items/1/@props/dev answerable.
It also means Mynah sees what a human does in the vendor UI, and follows the Web RCS's own screen selection so that selecting a screen in either place selects it in both.
The desktop app, if you want the simple answer
Download it and it just works — macOS, Windows and Linux, about 5 MB. It talks to the switcher directly, with no browser sandbox in the way.
npm run desktop # run it from a checkout
npm run desktop:build # build the installers
It has to be served over http, and that is not negotiable
A LivePremier serves the Web RCS over plain http on port 80, with 443 closed.
There is no wss:// to connect to, and browsers block an insecure WebSocket
from a secure page as mixed content. So:
| Where Mynah runs | Can it drive a switcher? |
|---|---|
localhost from a checkout |
yes |
| self-hosted over http on the show network | yes |
| the desktop app | yes |
| the hosted https:// copy | no — blocked before the socket leaves the browser |
So the hosted copy runs a simulator instead, and says so in a banner you cannot miss. It is a model of a LivePremier built into the page: four screens with layers and three preset buffers each, the four memory banks, and a wireframe of what every screen is putting out. Recall a memory and watch the preview change; press Take and watch the buffers swap.
It reproduces the device's awkward behaviour on purpose, because those are the things worth meeting for the first time somewhere other than a show:
- a recall of an empty memory is answered with silence, and reported as an empty memory rather than as success
- preview and program are names for whichever buffer is pending or live, the mapping differs per screen, and a Take swaps it
The simulator is available in every build, not just the hosted one — tick Simulator in the connection bar.
For live control on a show, use the desktop app, the container, or a local checkout.
And no, a proxy cannot fix the hosted copy. A Cloudflare Worker runs at the edge and cannot reach a switcher on your own network, and tunnelling one out to the public internet would expose an unauthenticated control interface — anyone who reached it could drive your show. The desktop app is the answer to that question.
Devices
Keyboard, X-Keys and Stream Deck all run the same grammar — see docs/DEVICES.md. The short version: an X-Keys panel is opened directly from the browser over WebHID, and a Stream Deck cannot be, because Elgato's software holds it.
For a Stream Deck there are two routes. The bundled Elgato plugin in
streamdeck/, or — better, if you already run Companion —
companion-module-mynah,
which brings pages, variables, feedbacks and a preset library of around a
hundred ready-made buttons. Both bundle the compiler from src/lang/ rather
than reimplementing the grammar.
Running it
npm install
npm run dev
Then point it at a device, or at the LivePremier simulator on 127.0.0.1:3000.
npm test # the grammar and the store scanner
npm run build # static bundle in dist/
npm run build:streamdeck # the Stream Deck plugin
npm run build:lang # the language core alone, for other consumers
Status
v1.3.3, and it has driven a real switcher.
Verified on a physical Aquilon C on firmware 6.2.73: all 21 paths in
docs/PATHS.md resolve, and every verb was executed from the command line —
store, recall, label, delete, a layer memory, a program recall, a Take, and the
six-op masked master store, with the filters read back exactly as compiled. The
empty-memory behaviour reproduced identically to the simulator. Test memories
were deleted afterwards and the box left clean.
Hardware also found two bugs, both fixed: a label was reported unconfirmed
because the echo matcher only accepted true, and preview turned out to map to
buffer A on one screen and B on another, so there is nothing to hard-code
there.
Not yet done: neither the X-Keys nor the Stream Deck path has been run
against real hardware. That hardware run covered the memory paths, which are the 21 in
docs/PATHS.md. The grammar has grown past them since — live layer control and
the audio routing matrix — and neither of those has been exercised against a
device. The object model is larger still, and the syntax is built to grow over it.
Run your own copy
Mynah is a static page, so hosting it yourself is one container serving the built files — the same files the hosted copy serves, running somewhere that still works when the venue has no internet.
Docker. The image is built by this repo's docker.yml workflow on every
push and published as ghcr.io/stoatworks-labs/mynah:
docker run -d --name mynah --restart unless-stopped -p 8534:80 ghcr.io/stoatworks-labs/mynah:latest
Or docker compose up -d with the docker-compose.yml
in this repo, which maps the same port. Either way it is then at
http://localhost:8534/.
Unraid. Search Community Applications for Mynah — the template is
templates/mynah.xml
in stoatworks-unraid, which is what the CA feed reads.
Stoatworks Burrow lists it under Self-hosted, with the compose file a click away.
The Dockerfile, docker-compose.yml, docker/ and the workflow are
generated from fleet.json in stoatworks-unraid. Change them there and
regenerate rather than editing them here.
Install Mynah on Unraid in a few clicks.
Find Mynah in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/stoatworks-labs/mynah:latestRuntime arguments
- Web UI
http://[IP]:[PORT:80]/- Network
bridge- Shell
sh- Privileged
- false
Template configuration
Host port for the web interface.
- Target
- 80
- Default
- 8534
- Value
- 8534