Mission Control is a custom external web UI for nikki, backed by a router-side bridge for mihomo.
The UI is distributed as a Nikki-compatible ZIP archive, so it can be plugged into LuCI through Services -> Nikki -> Mixin -> External control settings -> UI URL.
The bridge is installed separately on the router and exposes /cgi-bin/mission-control-bridge, which keeps Mihomo secrets and router-side automation logic away from the browser.
router-export/: the current live Mission Control UI and bridge sourcesscripts/build-release.ps1: builds release-ready artifacts intodist/scripts/install.sh.template: template for the router bootstrap installer.github/workflows/build-release-assets.yml: CI job that validates the release bundle
Each GitHub release publishes these files:
mission-control-ui.zipmission-control-bridge.cgimission-control-manifest.jsoninstall.sh
Mission Control releases now use semantic versioning: X.Y.Z.
The version is sourced from router-export/ui/bridge/mission_control_bridge.cgi, and the release tag format is always vX.Y.Z.
Direct latest URLs:
- UI ZIP:
https://github.com/BlackF1re/mission-control/releases/latest/download/mission-control-ui.zip - bridge installer:
https://github.com/BlackF1re/mission-control/releases/latest/download/install.sh - update manifest:
https://github.com/BlackF1re/mission-control/releases/latest/download/mission-control-manifest.json
Prerequisites:
- the router has outbound HTTPS access to GitHub Releases
- the router runs a firewall4-based OpenWrt build supported by Nikki
Run one command on the router:
sh -c "$(wget -qO- https://github.com/BlackF1re/mission-control/releases/latest/download/install.sh)"What this does:
- detects whether Nikki is missing or installed incompletely
- detects OpenWrt release and architecture from
/etc/openwrt_release - if needed, first tries the official Nikki feed from
https://nikkinikki.pages.dev - if that feed is unreachable on
apk-based systems, falls back automatically to the matching official Nikki release bundle from GitHub Releases - installs or repairs
mihomo-meta,nikki, andluci-app-nikkifrom official Nikki sources - installs bridge dependencies if they are missing:
curl,yq,unzip - installs the Mission Control bridge into
/usr/libexec/mission-control/mission_control_bridge.cgi - installs the CGI wrapper into
/www/cgi-bin/mission-control-bridge - configures Nikki to use:
ui_path='ui'ui_name='Mission Control'ui_url='https://github.com/BlackF1re/mission-control/releases/latest/download/mission-control-ui.zip'
- seeds the live panel model with the Mission Control manifest URL
- restarts Nikki, primes the bridge state, and installs the UI assets
After that, open the panel at:
http://ROUTER_IP:9090/ui/
The intended first-run flow is:
- open Mission Control
- leave or adjust the prefilled runetfreedom lists
- add your subscription
- wait for the first pool rebuild to finish
The first subscription import can take a while, because Mission Control checks nodes one by one and fills the egress cache on the router side.
If you want the README path to match exactly what a user does by hand, use the commands below.
. /etc/openwrt_release
case "$DISTRIB_RELEASE" in
*24.10*) NIKKI_BRANCH="openwrt-24.10" ;;
*25.12*) NIKKI_BRANCH="openwrt-25.12" ;;
SNAPSHOT) NIKKI_BRANCH="SNAPSHOT" ;;
*) echo "Unsupported OpenWrt release: $DISTRIB_RELEASE" >&2; false ;;
esac
NIKKI_ARCH="$DISTRIB_ARCH"
NIKKI_REPO_BASE="https://nikkinikki.pages.dev"
NIKKI_FEED_URL="$NIKKI_REPO_BASE/$NIKKI_BRANCH/$NIKKI_ARCH/nikki"
echo "$NIKKI_FEED_URL"mkdir -p /etc/apk/repositories.d /etc/apk/keys
wget -qO /etc/apk/keys/nikki.pem https://nikkinikki.pages.dev/public-key.pem
printf '%s\n' "${NIKKI_FEED_URL}/packages.adb" > /etc/apk/repositories.d/customfeeds.list
apk update
apk add --allow-untrusted --force-reinstall -X "${NIKKI_FEED_URL}/packages.adb" mihomo-meta nikki luci-app-nikkiwget -qO /tmp/nikki.pub https://nikkinikki.pages.dev/key-build.pub
opkg-key add /tmp/nikki.pub
grep -Fv 'src/gz nikki ' /etc/opkg/customfeeds.conf 2>/dev/null > /tmp/customfeeds.conf || true
mv /tmp/customfeeds.conf /etc/opkg/customfeeds.conf 2>/dev/null || true
printf '%s\n' "src/gz nikki $NIKKI_FEED_URL" >> /etc/opkg/customfeeds.conf
opkg update
opkg install mihomo-meta nikki luci-app-nikkish -c "$(wget -qO- https://github.com/BlackF1re/mission-control/releases/latest/download/install.sh)"http://ROUTER_IP:9090/ui/
If you want to configure the Nikki dashboard selector yourself in LuCI, paste this into the custom UI URL field:
https://github.com/BlackF1re/mission-control/releases/latest/download/mission-control-ui.zip
That only installs the frontend ZIP. You still need the bridge on the router, so run:
sh -c "$(wget -qO- https://github.com/BlackF1re/mission-control/releases/latest/download/install.sh)"Mission Control does not rely on separate external scripts or hand-written cron jobs outside the installed bridge.
- Nikki stores the UI source in
nikki.mixin.ui_url - the bridge stores the release manifest URL in
/etc/nikki/panel/model.json - the bridge scheduler installs one cron entry that runs every minute and internally decides which jobs are due
- the scheduler handles:
- subscription refresh timers
- remote base sync timers
- log cleanup
- Mission Control release checks
- UI and bridge auto-update settings are managed from the Mission Control automation controls, not from browser
localStorage
The default manifest URL is:
https://github.com/BlackF1re/mission-control/releases/latest/download/mission-control-manifest.json
Current release-update behavior:
- when
uiAutoUpdateis enabled, the bridge applies the latestmission-control-ui.zip - when
bridgeAutoUpdateis enabled, the bridge replacesmission_control_bridge.cgiin the active install path and any detected legacy bridge path - release checks run on the timer configured in the Automation section
- new installs and upgraded routers probe the latest release on the first scheduler pass, then continue on the configured timer
- the default release check interval is
30minutes - the scheduler persists its state on the router, so versions and timestamps survive page reloads and service restarts
- Settings also provide a manual release check button that queries GitHub Releases, shows the latest changelog, and turns into
Updatewhen a newer version is available
From Windows PowerShell:
./scripts/build-release.ps1Artifacts will be written to dist/.
If dist/ is busy because you are serving it locally, build into a separate directory:
./scripts/build-release.ps1 -DistRoot ./dist-buildBuild the release bundle first:
./scripts/build-release.ps1The build script enforces semantic versioning in X.Y.Z format.
Then publish the current version embedded in router-export/ui/bridge/mission_control_bridge.cgi:
$version = (Select-String -Path .\router-export\ui\bridge\mission_control_bridge.cgi -Pattern 'MISSION_CONTROL_VERSION="([^"]+)"').Matches[0].Groups[1].Value
gh release create "v$version" `
dist/mission-control-ui.zip `
dist/mission-control-bridge.cgi `
dist/mission-control-manifest.json `
dist/install.sh `
--title "Mission Control $version"BSD 3-Clause. See LICENSE.