Skip to content

Microsoft Store submission

This page documents the Store route and, in particular, the two things a reviewer asks about: why the package requests runFullTrust, and what it does with a global keyboard hook.

Why MSIX, and why it costs nothing

Store policy 10.2.9 requires an EXE/MSI submission to be "digitally signed with a code signing certificate that chains up to a certificate issued by a Certificate Authority that is part of the Microsoft Trusted Root Program." An OV certificate costs roughly $150–300 a year.

That policy applies to the direct-download path only. An MSIX submitted to the Store is signed by Microsoft during certification, so the Store route needs no purchased certificate. This is the only free path to the Store.

This does not make the .exe signed

Microsoft signs the Store package. The installer distributed through GitHub Releases, winget, Chocolatey and Scoop is the same unsigned .exe as before and still triggers SmartScreen on first run. Fixing that needs a certificate for the binary itself — see code signing.

Full trust, not AppContainer

A packaged desktop app is not automatically sandboxed. AppContainer is opt-in (uap10:TrustLevel="appContainer"). YazSes declares uap10:TrustLevel="mediumIL", which Microsoft documents as a full trust app that "runs with the same permissions as a standard desktop app" and does not run in an AppContainer.

This matters because YazSes needs three things a sandbox would deny:

Needs Why
Global SetWindowsHookExW keyboard hook to notice the hold-to-talk key anywhere on the desktop, not only inside its own window
Raw microphone capture to record while the key is held
SendInput into other applications' windows to type the transcript into whatever has focus

SendInput is governed by UIPI, which permits injection into processes at equal or lesser integrity. Medium IL is exactly what the current unpackaged .exe runs at, so packaging as MSIX costs no capability.

runFullTrust justification

runFullTrust is a restricted capability. Certification permits it but asks the publisher to explain why. The explanation, stated plainly:

YazSes is a voice dictation tool for people who cannot type comfortably — because of RSI, a motor impairment, or an injury. It works by watching for one held key anywhere on the desktop, recording speech while that key is down, transcribing it locally, and typing the result into whichever application has focus. Those three operations require a global keyboard hook, microphone access, and synthetic keyboard input to another process. None is available to an AppContainer app, so the package is declared full trust.

Proactive disclosure: this is not a keylogger

A global keyboard hook combined with synthetic keystroke injection is, on its face, the signature of a keylogger. Rather than let a reviewer discover that, here is what the code does and where to check it:

  • The hook matches a single configured virtual-key code and never records key content. It answers one question — "is the hold-to-talk key currently down?" It is not a log.
  • Nothing leaves the machine. This is enforced mechanically, not promised: a test enumerates every module permitted to open an outbound connection and fails the build if any module gains an unregistered one (see ADR-019). Of the modules that touch the network, all but two only fetch model weights or a version string; the two that can transmit speech are confined to localhost and to a host the user names on the command line.
  • Transcription is local. faster-whisper runs on the CPU. There is no account, no API key, and no telemetry.
  • The same two OS facilities are used by Dragon NaturallySpeaking, Talon Voice and Windows' own Voice Access.
  • The source is public and Apache-2.0 licensed.

Capabilities declared

Capability Why
runFullTrust above
microphone recording while the hotkey is held

webcam is deliberately not declared. The gaze (Glance-Type) and Face-Gesture adapters are part of YazSes and work on a normal install — yazses features enable gaze installs the gaze extra (mediapipe, opencv-python) and they run.

What differs here is the packaging, not the product. The Store package is a frozen PyInstaller bundle built without optional extras, and an MSIX has no pip to add them afterwards, so those camera code paths are unreachable in this package specifically. Requesting camera access a user could never benefit from is a certification question with no upside. Anyone who wants gaze should install from winget, Scoop or the direct installer, where enabling it works normally.

Building the package

make msix VERSION=2.36.0 \
  IDENTITY_NAME=<from Partner Center> \
  PUBLISHER=<from Partner Center> \
  PUBLISHER_DISPLAY_NAME=<from Partner Center>

or directly:

./scripts/build-windows.ps1              # produces dist/YazSes (PyInstaller COLLECT)
./scripts/build-msix.ps1 -Version 2.36.0 `
    -IdentityName "..." -Publisher "CN=..." -PublisherDisplayName "..."

Requires the Windows 10/11 SDK for makeappx.exe. The output is unsigned on purpose.

Identity values are not guessable

Name, Publisher and PublisherDisplayName must match what Partner Center shows for the product exactly, or the upload is rejected. They are placeholders in the committed manifest, and build-msix.ps1 aborts if any survives substitution — a package built with a placeholder identity would otherwise fail at submission, long after the build looked fine.

Find them in Partner Center under the product's Product identity page.

⚠ Product type in Partner Center

The reserved YazSes product is typed "EXE or MSI app". That product type takes a download URL to a signed installer; it does not accept an MSIX package. Publishing this package needs a product of type "MSIX or PWA app", and a Store name can only be held by one product — so using the reserved name for MSIX means releasing it from the EXE product first.

That is a deliberate choice, not a formality: keeping the EXE product reserves the option of a signed direct-download submission later, if a certificate ever becomes available.