Skip to content
skywardpixelPublic

About

Yet Another Mihomo Interface — a minimal macOS menu bar app for mihomo.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Yami

Yami

Yet Another Mihomo Interface — a minimal macOS menu bar app for mihomo.

Yami does three things: keeps one subscription up to date, runs the mihomo core, and flips the system proxy. Anything you would touch once a year lives in mihomo's own config, not in this UI.

┌─────────────────────────────────────┐
│  CONNECTION                         │
│  Mihomo                      [ ●──] │
│  ● Running · port 7890              │
│  System Proxy                [ ●──] │
│  Routing         [ Loyalsoldier ▾ ] │
│                                     │
│  SUBSCRIPTION                       │
│  ┌───────────────────────────────┐  │
│  │ https://example.com/sub…   ↻  │  │
│  └───────────────────────────────┘  │
│  Updated 2 hours ago                │
│                                     │
│  APP                                │
│  Launch at Login             [ ●──] │
│  View Config                        │
│  Reveal Log                         │
│  Quit Yami                          │
│  Yami 0.3.0 · mihomo 1.19.30        │
└─────────────────────────────────────┘

The menu bar mark carries both states independently — the core can run without the proxy, so a single dimmed→solid ramp would conflate them:

Menu bar icon states

Left to right: both off, core running, proxy on, both on. Dimmed means the core is stopped; the badge dot means the system proxy is on.

Non-goals

No node list or latency testing, no rule editor, no traffic graph, no multiple profiles, no TUN mode, no port configuration, no themes. The subscription's YAML decides proxies and rules; Yami never second-guesses it.

Requirements

  • macOS 14+ on Apple Silicon
  • An Apple code signing identity — the privileged helper will not load unsigned
  • brew install mihomo, unless you bundle the core (below)

Build

SwiftPM plus a bundling script; there is no .xcodeproj.

./build.sh          # debug  → build/Yami.app
./build.sh release  # release
open build/Yami.app

build.sh picks the first available signing identity; override with YAMI_IDENTITY="Developer ID Application: …" ./build.sh release.

Bundling the core

./build.sh release ships mihomo inside the app, so it runs on machines without Homebrew. scripts/fetch-mihomo.sh downloads a pinned arm64 release into a gitignored vendor/ and checks it against a SHA-256 recorded in the script; a debug build bundles it only if it is already there, and otherwise falls back to /opt/homebrew/bin/mihomo at runtime. The binary adds about 43 MB, and pins the core version to whatever the script says.

Upstream publishes no checksums or signatures for its release assets, so the recorded hash comes from a download verified by hand. Everything after that first pin is reproducible — a changed artefact fails the fetch rather than being signed and shipped. The core is re-signed with your identity and the hardened runtime before the bundle is sealed, since upstream ships it ad-hoc signed as a.out, which will not notarize.

mihomo is GPL-3.0. Yami runs it as a separate process, so Yami itself is not a derivative work, but bundling the binary is redistribution: the licence ships in Contents/Resources/mihomo-LICENSE.txt, and the corresponding source is the pinned tag at MetaCubeX/mihomo. Keep both in step if you bump the version. (Not legal advice — just the thing not to discover after shipping.)

Building under a different Apple developer account requires changing one thing: HelperInfo.clientRequirement and helperRequirement in Sources/YamiShared/HelperProtocol.swift pin a team ID. That pin is the security boundary for a root daemon, so it cannot be derived at runtime — replace AU534DT7GN with your own team ID.

All the artwork is drawn from the crescent in MenuBarIcon.swift, so the app icon, the status item and the figures above cannot drift apart. Resources/AppIcon.icns is generated on first build — delete it to regenerate after changing the mark. The README figures come from the same tool:

swiftc -O Sources/Yami/MenuBarIcon.swift tools/GenerateArtwork.swift -o /tmp/makeicon
/tmp/makeicon --png docs/icon.png
/tmp/makeicon --states docs/menubar-states.png

How it works

The core. Yami owns the mihomo process — spawn, supervise, restart on config change, bounded restart budget on crashes. The control API is a unix socket (-ext-ctl-unix) rather than a TCP port, so there is no second port to pick and no API secret to store; file permissions are the access control.

The subscription. mihomo has no concept of a subscription, so this is most of the actual logic: fetch with a mihomo User-Agent (providers serve HTML to browser UAs), override the keys that decide how Yami reaches the core, strip listeners the user was never told about, validate with mihomo -t, then swap atomically. A bad subscription can never take a working core down.

The system proxy. Changing proxy settings needs root, so a bundled SMAppService daemon does it over XPC — one approval at first use, silent thereafter. Both ends verify the other's code signature. The helper writes all network services in a single SCPreferences commit rather than shelling out to networksetup.

Routing. Providers often ship a single MATCH rule and no real routing, so Yami offers three positions: the subscription's own rules, the Loyalsoldier set (mainland China and LAN direct, ads rejected, everything else proxied), or Global. Switching re-renders from the subscription already on disk — no round-trip to the provider, and it works offline.

The config viewer. View Config opens a read-only window showing the YAML the core actually loaded — the subscription with Yami's overrides applied, which is the thing you would want to check. It is the only window in the app; the default .yaml handler is usually Xcode, which is a poor way to read a config.

The interlock. A system proxy is only as good as the core behind it. If the core stops, the proxy comes down with it; if Yami crashes, the helper undoes the proxy when the XPC connection drops. Readiness means serving — a TCP connect to the proxy port, not just a live process.

DESIGN.md has the full reasoning, including the failures that shaped it.

Releases

Every push to main replaces a single rolling prerelease tagged canary, so the newest build is always at a stable URL:

https://github.com/skywardpixel/Yami/releases/download/canary/Yami.zip

Tagged releases are cut by pushing a v* tag, which publishes a real release named for that version.

The version comes from git, so cutting a release is git tag v0.4.0 && git push origin v0.4.0 and nothing else — no file to bump. build.sh stamps three values into Info.plist before signing (editing it afterwards would invalidate the seal):

CFBundleShortVersionString the last v* tag, e.g. 0.3.0
CFBundleVersion commit count, monotonic across the history
YamiSourceVersion exact provenance, e.g. 0.3.0-4-g1a2b3c4-dirty

The About line shows the bare version for a tagged build and the full description for anything else, so a local build never passes for a release. The value committed in Resources/Info.plist is only a fallback for building from a tarball with no repository.

Artefacts are only published when a Developer ID certificate is configured — see docs/RELEASING.md. Without one, CI still builds and tests, but publishes nothing: an ad-hoc build cannot be opened after download and its privileged helper rejects it, so the System Proxy toggle does not work. Shipping one is a trap rather than a convenience.

With signing configured, every build is notarized and stapled, so a download opens with no warnings and no terminal commands.

Tests

swift test           # pure logic: config rendering, log parsing, port checks
./scripts/verify.sh  # process lifecycle against a real core

verify.sh runs against a scratch YAMI_HOME with its own defaults domain, so it never touches your real subscription, cache, or system proxy. It does need port 7890, so it stops a running Yami and puts it back afterwards.

Licence

Yami is licensed under the GNU General Public License v3.0 — see LICENSE.

Copyright (C) 2026 skywardpixel

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

The bundled mihomo core is GPL-3.0 as well, so a release is GPL-3.0 throughout. Its licence ships in Contents/Resources/mihomo-LICENSE.txt and docs/RELEASING.md records the exact upstream tag the bytes came from.

State

~/Library/Application Support/Yami/   config.yaml, api.sock, mihomo's caches (0700)
~/Library/Logs/Yami/core.log

About

Yet Another Mihomo Interface — a minimal macOS menu bar app for mihomo.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages