Skip to content

Add a Tauri-based frontend and a native service rewritten in Rust - #1142

Open
115dkk wants to merge 149 commits into
snowie2000:directwritefrom
115dkk:codex/upstream-pr-prep
Open

Add a Tauri-based frontend and a native service rewritten in Rust#1142
115dkk wants to merge 149 commits into
snowie2000:directwritefrom
115dkk:codex/upstream-pr-prep

Conversation

@115dkk

@115dkk 115dkk commented Jul 17, 2026

Copy link
Copy Markdown

What is this?

I rewrote everything except the MacType core, which I intentionally left intact so that the maintainer can continue maintaining it.

This project originally began as a frontend replacement. Eventually, however, it became clear that replacing the frontend alone was not enough.

Why?

The frontend and Tauri

I believe everyone already understands that the existing frontend, meaning the program’s user interface, has become outdated. I will therefore skip that explanation.

Why Tauri?

Because it is the only option that is both lightweight for users and reasonably easy to design and maintain.

To begin with, Win32 and MFC would be difficult for almost anyone here to work with. Since both MacType and my other project, EqualizerAPO-XT, are written in C++, I also considered Qt. In the end, however, I concluded that this would be a terrible idea. Without extensive styling, Qt applications tend to look rather unattractive. With extensive styling, they turn into monsters that the maintainer can no longer understand.

wxWidgets presents much the same problem.

I also considered C#, but Microsoft's Windows UI framework strategy has changed repeatedly over the years. I did not want to tie the project's long-term maintenance to another shifting frontend stack.

I therefore decided to abandon native UI toolkits and use a WebView, which has become the mainstream approach. Electron, however, places far too much weight on the user. The current MacType installer is only 5.59 MB. With Electron included, it would almost certainly exceed 100 MB.

Electron also has very slow startup times and poor responsiveness in several areas. It may be convenient for developers, but as a technology, its overall quality is deeply disappointing.

Tauri avoids all of these disadvantages. The current prerelease package is only 4.37 MB. At the same time, it retains the advantages of a WebView. The interface can be maintained entirely with web technologies, while avoiding many of the problems imposed by native UI frameworks.

One disadvantage of Tauri is that its appearance can vary between operating systems. This does not matter here, however, because MacType is Windows-only.

For these reasons, I concluded that Tauri was the best choice.

Retiring MacTuner and introducing a native service

I continued development in this direction, but eventually something began to feel wrong.

I had rebuilt the application in such a modern form, yet it still felt strangely old-fashioned. Then, during one test, I tried to register the service through what was described as a “new” feature. MacTuner, which I thought had disappeared, suddenly came back to life, printed an error, and died.

I could not leave things that way.

Why? Because MacTuner was a file I did not have. After carefully reading through the repository, I also concluded that I would probably never be able to obtain its source code.

Asking the maintainer to manually insert MacTuner into every otherwise complete build would also be an extremely clumsy and inelegant workflow.

I therefore began replacing it.

This was, however, a genuine reverse-engineering task. I had to reproduce the behavior of an unfamiliar program by studying only its DLL interactions and observable behavior. As a result, it took far, far longer than expected.

The result was worth the effort.

There is now no closed-source component left in the MacType control and service stack. MacType now has a complete, self-contained architecture that no longer depends on any proprietary Delphi program.

What changed?

This PR replaces the entire control and service layer around the existing MacType core.

The main changes are:

  • a new Tauri frontend written with React and TypeScript;
  • a native Windows service written in Rust;
  • a separate Rust setup broker for installation, upgrades, repair, recovery, and removal;
  • public x86 and x64 C++ injectors built together with the existing MacType core;
  • direct Windows Service Control Manager integration without calling MacTray or MacTuner as installation tools;
  • protected, versioned runtime and profile generations;
  • explicit service health reporting that distinguishes SCM Running, Ready, and actual injection results;
  • migration and rollback support for existing legacy MacTray service installations;
  • a new installer and a manually triggered one-click GitHub Actions packaging flow;
  • maintained build scripts for the frontend, open core, native service, and installer.

The MacType rendering core and its existing public interfaces remain intact. This PR replaces the surrounding control, service, injection, installation, migration, and maintenance infrastructure.

What happens to existing installations?

Existing profiles remain compatible and are preserved.

The installer does not silently overwrite an existing legacy service or a foreign service with the same fixed name. If it cannot prove that the service and runtime belong to this project, it stops instead of taking ownership of them.

Migration from the legacy MacTray service is explicit. The migration path:

  1. records the existing service configuration and profile state;
  2. installs and starts the new native service;
  3. waits for strict Ready state;
  4. verifies actual x86 and x64 injection results for the active runtime and profile;
  5. removes the legacy service only after those checks pass.

If migration fails, the previous service, runtime, and profile state can be restored.

Upgrades preserve the active profile and publish a new immutable runtime generation. A failed upgrade restores the previous working generation instead of reporting success.

Uninstallation removes only files, services, and runtime generations owned by this project. Protected user profiles are preserved, and unrelated files or foreign services are not removed.

New installations no longer require MacTray or MacTuner for normal operation.

Reliability and recovery

The native service is intentionally conservative.

Runtime payloads are built from a fixed public file set and verified before activation. The active runtime and profile are stored as versioned generations rather than being overwritten in place.

Installation, upgrade, and repair use explicit activation records. If power loss, a process crash, or another failure interrupts an operation, the next machine-changing command first reconciles the stored runtime pointer with the actual Windows service configuration.

A service process being present is not considered success by itself. The service must reach Ready with the expected active profile, and legacy removal requires real x86 and x64 injection evidence for the current runtime generation.

Unknown states are not guessed. Results that cannot be verified remain failures, and ambiguous injection results are not retried blindly.

The installer also treats required machine operations as required. A failed bootstrap, failed upgrade, or failed removal cannot be hidden behind an otherwise successful installer exit.

Why is this PR so large?

A frontend-only replacement would still have left every complete build dependent on unavailable Delphi binaries.

Once that dependency became clear, the only honest solution was to replace the full surrounding lifecycle: service installation, process observation, x86 and x64 injection orchestration, profile publication, upgrades, migration, rollback, removal, and all of the tests needed to prove those paths.

A large part of this PR is the service lifecycle itself, maintenance documentation, build infrastructure, and refactoring that separates previously large modules into maintainable components. The much larger development-only validation suite was used before preparing this upstream branch, but it is intentionally not included here.

The size does not come from rewriting the MacType renderer. The renderer was intentionally left alone.

Suggested review order

This PR is easier to review as a set of subsystems rather than as one large frontend diff.

  1. Read the design maintenance guide and the build guide.
  2. Review service-runtime/contract for the shared runtime, profile, health, and migration contracts.
  3. Review service-runtime/host for the Windows service, process observation, target validation, health reporting, and injection orchestration.
  4. Review service-runtime/setup for SCM integration, protected storage, installation, upgrade, repair, recovery, and removal.
  5. Review service-injector for the public x86 and x64 C++ injection helpers.
  6. Review control-center/src-tauri for the Tauri commands, explicit machine actions, legacy migration, and frontend adapters.
  7. Review installer, .github/scripts, and .github/workflows/build.yml for installer behavior and the manual packaging path.
  8. Review control-center/src for the frontend itself.

Generated settings outputs do not need to be the first review target. Their source schema is more important.

The original MacType core is intentionally outside the scope of this rewrite and has not been modified.

Testing

Before preparing this upstream branch, the complete implementation passed all 19 checks in my development repository.

That validation covered:

  • frontend dependency installation, linting, production builds, settings generation, and translation consistency;
  • the complete browser gallery across public pages, supported locales, themes, directions, and target viewport sizes;
  • Tauri Rust formatting, Clippy with warnings denied, and tests;
  • the standalone service workspace with both default and all-feature test configurations;
  • source builds of the existing MacType x86 and x64 core;
  • x86 and x64 injector builds, unit tests, and MSVC static analysis;
  • the preview helper build and protocol tests;
  • single-instance startup stress testing;
  • smoke tests for every Tauri window state and hidden tray startup;
  • real Windows service installation, strict Ready, crash restart, repair, profile publication, rollback, x86 and x64 marker injection, stop, and removal;
  • real Inno Setup installation, a deliberately failing upgrade, rollback to the previous working runtime, a successful upgrade, launch, failed uninstall handling, successful uninstall, and cleanup;
  • preservation of foreign services, legacy services, protected profiles, user settings, and unrelated files;
  • exact empty application-root cleanup without recursively deleting foreign files;
  • release packaging, provenance, and immutable runtime versioning.

Those exhaustive workflows are intentionally not part of this upstream PR. I did not want to import my repository-specific CI policy and a large collection of review-only checks into the upstream project.

This branch contains one manually triggered GitHub Actions workflow: Build MacType Control Center. It does not run on pushes or pull requests. It builds the source-based x86 and x64 open core, fixed injectors, Preview Helper, frontend, Tauri application, native service payload, Inno Setup installer, and SHA-256 checksum, then uploads the complete installable artifact.

The full lifecycle validation was completed before this branch was prepared. The workflow included here is the maintained one-click build and packaging path for upstream use.

How will maintenance work from now on?

I documented the maintenance guidelines in the design maintenance guide.

Any change to the visible interface or its responsiveness can be handled through this layer. You do not need to read a single line of Rust.

The MacType core itself has not been changed, so it can continue to be maintained as before. I also intend to contribute to the core, but I plan to approach it from a maintenance perspective and avoid excessively radical changes in the future.

The Rust code covers a broader area than I originally expected because of the new native service. I will take primary responsibility for maintaining it for now. In the long term, however, learning Rust would probably be helpful. It would be even better if another contributor who already knows Rust were willing to help.

How do I build it?

Click the CI button.

I updated the build guide accordingly.

The guide still contains detailed instructions for building everything manually, but unless harming your own mental health is a personal hobby, I strongly recommend using the one-click CI build instead.

A manual build requires installing the Rust toolchain and setting up the rest of the build environment on your own computer. The CI build does everything for you with a single click.

The upstream branch intentionally includes only this manually triggered build workflow. It does not run automatically on pushes or pull requests, and the larger development-only validation suite is not included.

If the Inno Setup installer menus or related installer behavior ever need to be changed, I will maintain that part as well. The menu definitions themselves are not difficult, but changes may also require corresponding CI updates.

Known limitations

  • MacType remains Windows-only.
  • The frontend uses the system WebView2 runtime.
  • This PR intentionally does not modernize or rewrite the MacType rendering core.
  • The legacy MacTray path remains available only for migration and fallback. It is no longer required for new installations or normal operation.
  • CI cannot reproduce every third-party program, enterprise policy, or unusual Windows configuration. If real-world installations reveal environment-specific problems, I will continue maintaining this work.
  • Further UX refinements can be handled in follow-up PRs without changing the native service.
  • Resident MacTray tray mode and its supported auto-start entries are detected and handled through the Control Center. A trusted MacTray instance in the current session can be asked to exit normally, and recognized auto-start entries can be disabled with rollback records. Untrusted, cross-session, or otherwise unverifiable conflicts remain blocked and require manual review.

Screenshots

01-overview-en 02-profiles-en 03-wizard-apply-ko 04-execution-ready-en 05-dark-mode-titlebar 06-mobile-rtl-ar

More screenshots can be downloaded from the full gallery artifact here:

https://github.com/115dkk/mactype_tauri/actions/runs/29582148505/artifacts/8407536978

Which follow-up PR would you prefer?

A MacType core PR will definitely happen, so I have excluded it from this list. I would appreciate it if you could look through the following options at your leisure and let me know whether any of them interest you. Everything except option A could be started immediately.

A. A better design

GPT 5.6 was used for the current work, but Fable is especially good at UI work and could be used for another design pass. I could not use it this time because I had already reached the weekly usage limit.

The current design is perfectly usable, but there is still a difference between a usable design and a genuinely good one. Would you like me to take another pass at it?

B. Automated builds or releases (CD)

A push to the default branch or the publication of a tag would automatically build and publish a release.

C. Static analysis

Static analysis can greatly reduce coding mistakes. However, fixing everything it reports can be tedious, and it occasionally produces false positives.

D. Frontend gallery

This is useful when modifying or reviewing the frontend design.

E. Automated real-world testing

This can greatly reduce the chance of regressions, but it may also become particularly burdensome to maintain.

Please choose without feeling any pressure. Every option can create additional maintenance work.

You may also simply merge this PR without selecting anything. In that case, I will assume that all of these proposals have been declined.

Acknowledgements

Thanks to @beefiker and Superloopy for the frontend design workflow used in the initial Tauri prototype.

Thank you for taking the time to review a change of this size.

115dkk and others added 30 commits July 12, 2026 13:36
Updated the vulnerability reporting section to use GitHub's private reporting feature.
Tauri Control Center phase 1 and CI gates
Guard Windows builds from glib advisory
Add complete multilingual UI and automatic installers
…n-gaps

fix: complete control center integration paths
Replace manual path and font entry with native pickers
Align font picker guidance with selection UX
115dkk and others added 13 commits July 25, 2026 03:37
Owned uninstall verifies ownership and the captured SCM configuration
before issuing DeleteService, but wait_until_absent then re-ran the same
comparison on every absence poll. After DeleteService succeeds the
record is delete-pending, and while an external handle keeps the service
open its QueryServiceConfigW output (display_name and friends) is not
stable, so the per-poll comparison raised a false tamper error - the
recurring "Runtime error (at 8:810)" with broker exit 1 seen in CI runs
30083778670 and 30082016303.

The pre-delete ownership and configuration verification is unchanged;
after a successful DeleteService the wait now polls for absence only.
The tamper diagnostic also names each changed field with its old and new
values so a genuine mismatch is diagnosable from the error alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restore the legacy MacType Tuner screen hierarchy in the guided setup
(net +1 step, shadow deliberately excluded):

- New 굵게·기울임 step after 글꼴 품질 with bold weight, bolder mode, and
  italic slant; contrast moves out of 글꼴 품질 into the 감마 step where
  the contrast and gamma sliders sit above the gamma mode; the RGB text
  tuning joins the LCD 배열 step. Steps render in legacy screen order,
  not schema order.
- Step-aware preview stacks: the bold/italic step renders the pangram as
  bold, italic, and bold italic lines; the LCD step renders four lines
  (current method plus channel-pure R, G, B foregrounds). Implemented as
  one render request per variant, stacked as labelled strips, because
  the preview helper draws one style per bitmap. The helper protocol
  gains optional bold/italic sample flags (backwards compatible).
- Preview panel compression with the legacy bold sample group: default
  height drops to 300px and the panel auto-grows by the measured canvas
  overflow so the last stacked line is never clipped, while the
  native-window control stays visible; a manual resize wins until the
  stack shape changes.
- Ten locale catalogs gain the step and strip labels plus a per-locale
  pangram; the ko step label is 굵게·기울임 because 볼 is not in the ko
  glyph subset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The native preview window gains a display-mode dropdown. Alongside the
existing sample it can now draw the legacy MacType Tuner listing: one
pangram repeated in black, red, green, and blue at a small and a large
size, in a normal-weight group followed by a bold group. That layout
exists so hue and channel tuning can be judged by eye, which a single
sample line cannot support.

The mode travels through the existing native-preview seam rather than a
new command, so older callers keep working, and the helper draws the
listing with the bold flag introduced for the step-aware preview.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"Tray injection apps" described neither the mechanism nor the outcome:
nothing is injected into the tray. The list holds programs the tray
starts through MacLoader with the applied profile, at sign-in or on the
tray command, which the description below it already said correctly.

The heading now says so across all ten locales. Korean reads
"MacType으로 시작할 프로그램" rather than a literal rendering, because
the covered glyph subset has no 께.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the raw newline textareas in the include/exclude Lists group with
structured per-entry editors: each entry is a monospace row with its own
delete button, every list gets an explicit add row (Enter or Add button)
with trim/empty/case-insensitive duplicate validation and an inline
rejection message, the font lists keep their installed-font select-to-add
flow in the same row UI, and the program/module/DLL lists gain a datalist
autocomplete fed by the running-process names from
listManualLaunchCandidates while still accepting free text. The profile
document hook now tracks lists as entry arrays and commits through the
unchanged updateProfileList seam. Adds add/placeholder/duplicate/empty
i18n keys across all ten locales, rewords the two per-line DLL help
strings, and extends the gallery with a ko structured-lists test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Judging a rendering change is inherently a comparison, but the preview
only ever showed the edited state, so the reader had to remember what
the previous setting looked like. A compare toggle now renders every
stack line twice, from the saved snapshot and from the working values,
and captions each with which side it is.

Comparison doubles the helper round-trip, so it stays a deliberate
switch and turns itself off once a save makes both sides identical. The
saved snapshot only enters the render batch while comparison is on;
otherwise a fresh document object would retrigger the round-trip, and
each extra batch grows the panel by its measured overflow, taking room
from the settings column the reader is working in. Captions resolve at
render time for the same reason.

The toggle sits in the preview footer with a short label: a wider or
wrapped control row is exactly what makes the settings column feel
cramped, and it does so through that same auto-grow path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guided setup lost every history control when its editor chrome was
removed, which left no way back from a mistaken slider drag short of
leaving the mode. The controls return as a per-step toolbar whose reach
stops at the step on screen: each step keeps its own record of edits, so
undo never reaches back into a step the reader has already left, and
"discard step changes" restores only that step's settings to their saved
values.

The record lives in the frontend and the backend document history is
untouched, so the global undo in All settings keeps working as before.
Ctrl+Z and Ctrl+Y drive the same step record, and only claim the key
when this step actually has something to undo.

The substitution step opts out: it owns one schema setting, but its
substance is the mapping list, which has no saved snapshot here, so a
revert would silently restore half the step. Tools and shortcuts stay
inert there together, through one rule in wizardModel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two steps kept titles that no longer described their contents. The gamma
screen took over contrast when the legacy screen order was restored, and
the LCD screen took over the RGB text tuning, so both headings named one
core setting while the screen carried several.

The steps are now named for the outcome the reader is after: brightness
and contrast, and LCD layout with color tuning. The setting rows still
carry their exact names, so nothing is hidden; only the screen titles
stop leaking a core-setting name into a place where it reads as jargon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The absolute-timeout test allowed 50 ms of slack over the 700 ms timeout
it was exercising, so a busy machine failed it while the launcher behaved
correctly. The upper bound now only has to exclude the child process's own
five second sleep, which is what the test actually proves; the lower bound
that shows the launcher waited for the timeout is unchanged.

The two tests share a lock, and a panic in one poisoned it so the other
died on the lock rather than on its own assertions, reporting a single
failure as two. Taking the lock through the poison recovers the guard so
each test still reports its own result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A launch that finds the startup gate held waits for the instance that
holds it, and the caller can only panic when that wait returns an error.
So a first launch slow enough to exceed the wait, which a cold run behind
antivirus or a first-run WebView2 setup can be, turned every other launch
into a crash dialog. The single-instance stress test saw seven of eight
probes abort with STATUS_STACK_BUFFER_OVERRUN once the harness waited long
enough to watch them.

A timed-out wait means another instance is already starting, which is the
situation the single-instance plugin resolves: the later process hands its
activation to the running one and exits. So the timeout now starts without
the gate rather than refusing to start, and release tolerates a gate that
was never held. The wait itself grows to two minutes so that handing over
early stays rare.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The guided step body and the preview panel were starving each other, and
the native preview window was showing whatever the strip stack rendered
last. Both trace back to the second sample group added with the legacy
Tuner steps.

- The default stack renders the sample once again. Bold stays on the
  bold and italic step, where the weight is the subject rather than a
  duplicate of the line above it; the now unused strip label leaves the
  ten catalogs.
- The preview docks beside the settings at a per-mode width: the guided
  step is a short column of choices and trades width for height readily
  (780px), while the settings table needs room for a label beside its
  control column (1000px). A 1280px window measures 800px of workspace,
  so the guided step now docks there instead of splitting vertically.
- The bottom panel no longer auto-grows by the measured canvas overflow.
  A four-line stack scrolls inside the canvas instead of pushing the
  settings form down to its floor, and that floor rises to 240px.
- The native window carries its own foreground and background through
  the show request rather than inheriting them from the last rendered
  bitmap. The listing mode honours the background too, picking a legible
  channel set for dark backgrounds, and the default mode draws the
  sample upright at normal weight instead of echoing the final variant.

The gallery branches on whether the preview docked, asserts the guided
step body keeps its room beside the four-line LCD stack, and proves the
background choice reaches an already open native window. The helper
runtime tests cover the colour round trip and its omitted-field default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
At tablet widths the guided workspace column measured 527px inside a
768px window, so the step body and the preview toolbar were cut off at
the right edge with nothing to scroll. The preview panel is a grid item
without min-width: 0, so its min-content width (a font-family select
plus a full toolbar row) set the column width and pushed the layout past
the window. Only the docked variant had carried that min-width.

The 768px to 1023px range had also inherited none of the narrow
treatments the phone breakpoint already applies, which is what let the
inflated column hide the problem. It now stacks the preview toolbar, lets
the toolbar selects shrink, starts the step tools from the leading edge,
and drops the list grid to a single column: two list columns leave too
little room for an entry field beside its add button once the workspace
stops overflowing. Text actions no longer break a word per line when
squeezed, and the list add row wraps instead of pushing its button into
the neighbouring editor.

The generic overflow gate skips anything inside an overflow-hidden
ancestor, which is why this survived. The guided test now checks the
workspace column against the window bounds directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hosted gallery caught a French overflow at 390px on the overview and
diagnostics views: a text action measured 116px wide against a 390px
window and could no longer wrap. Local fonts render the same label
narrow enough to fit, so only the runner saw it.

The nowrap guard existed for the preview toolbar, whose selects should
absorb the shrinking rather than squeezing an action until its label
breaks a word per line. Scope it there instead of applying it to every
text action, since longer translations elsewhere still need to wrap. The
preview footer keeps wrapping too; it was never the crowded row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@115dkk

115dkk commented Jul 25, 2026

Copy link
Copy Markdown
Author

Updated based on the review.

New pre-compiled binary

Thank you for your patience!

@snowie2000

snowie2000 commented Jul 27, 2026

Copy link
Copy Markdown
Owner

I can't install your service.

Here is the diagnostic log:

{"timestampUnixMs":1785118764000,"operation":"install","stage":"install","errorChain":"Control Center is outside the fixed Program Files layout; elevated service broker exit code 21","win32Code":null,"brokerExitCode":21,"channelFailure":null,"rollback":"not-applicable-or-unavailable","finalState":"legacy=Absent/Unknown/win32=None; modern=Absent/Stopped/Unknown/win32=None; receipt=unavailable"}
{"timestampUnixMs":1785118816715,"operation":"install","stage":"install","errorChain":"Control Center is outside the fixed Program Files layout; elevated service broker exit code 21","win32Code":null,"brokerExitCode":21,"channelFailure":null,"rollback":"not-applicable-or-unavailable","finalState":"legacy=Absent/Unknown/win32=None; modern=Absent/Stopped/Unknown/win32=None; receipt=unavailable"}
{"timestampUnixMs":1785118819297,"operation":"install","stage":"install","errorChain":"Control Center is outside the fixed Program Files layout; elevated service broker exit code 21","win32Code":null,"brokerExitCode":21,"channelFailure":null,"rollback":"not-applicable-or-unavailable","finalState":"legacy=Absent/Unknown/win32=None; modern=Absent/Stopped/Unknown/win32=None; receipt=unavailable"}

@115dkk

115dkk commented Jul 27, 2026

Copy link
Copy Markdown
Author

Sorry! This happened because, to prevent arbitrary privilege-escalation attempts, service installation is only allowed from the fixed installation path. A development executable launched from another location was therefore rejected when it tried to install the service.

The latest commit changes this so that the development executable delegates the elevation request to the already installed MacType Control Center. (pre-compiled binary)

Please let me know if you did not install MacType Control Center at all.

@snowie2000

Copy link
Copy Markdown
Owner
{"timestampUnixMs":1785227817341,"operation":"install","stage":"The system cannot find the path specified. (os error 3); local-machine startup restoration failed","errorChain":"The system cannot find the path specified. (os error 3); local-machine startup restoration failed: The system cannot find the path specified. (os error 3)","win32Code":null,"brokerExitCode":null,"channelFailure":null,"rollback":"failed","finalState":"legacy=Absent/Unknown/win32=None; modern=Absent/Stopped/Unknown/win32=None; receipt=unavailable"}

Seems like it requires external files to install or maintain the service?

If your control center is going to be bundled with mactype, it must be installed to the default program files folder. I don't quite understand your control center file structure and its requirements.

@115dkk

115dkk commented Jul 28, 2026

Copy link
Copy Markdown
Author
{"timestampUnixMs":1785227817341,"operation":"install","stage":"The system cannot find the path specified. (os error 3); local-machine startup restoration failed","errorChain":"The system cannot find the path specified. (os error 3); local-machine startup restoration failed: The system cannot find the path specified. (os error 3)","win32Code":null,"brokerExitCode":null,"channelFailure":null,"rollback":"failed","finalState":"legacy=Absent/Unknown/win32=None; modern=Absent/Stopped/Unknown/win32=None; receipt=unavailable"}

Seems like it requires external files to install or maintain the service?

If your control center is going to be bundled with mactype, it must be installed to the default program files folder. I don't quite understand your control center file structure and its requirements.

You're right. I incorrectly assumed that an installed Control Center was already present in the fixed Program Files location.

The standalone development executable does not contain the complete service-management payload by itself. Those files are bundled with the installer, not downloaded externally. The latest change attempted to delegate privileged operations to an installed copy, but when no installed copy existed, it failed before the broker could even start. The rollback message is also misleading because no machine state had been changed.

I will fix this so that an uninstalled development build reports that the full package must be installed, without requesting elevation or attempting rollback. I will also clarify and align the required installation root with the way Control Center will be bundled into MacType.

And, please don’t worry. no external files or downloads are required. This is an integration issue caused by the service security model being stricter than the current development-build workflow accounts for, and I am correcting that now.


Additionally, I’m working on improving the diagnostic logs so that they include more useful information, such as the expected and actual paths. This should allow me to make the right fix based on concrete evidence, even if the issue still persists.

@115dkk

115dkk commented Jul 28, 2026

Copy link
Copy Markdown
Author

pre-compiled binary

@nikita-edel

Copy link
Copy Markdown

waiter, 100k lines of slop please

@snowie2000

Copy link
Copy Markdown
Owner

Now I got another error:
Install MacType Control Center first Service installation and maintenance are unavailable from a standalone development executable. Run the complete installer first.

Why on earth do I have to use your installer, and what's the whole point of preventing me from installing your service all the time?

Please do not solely rely on AI to solve problems. If you have to, please give it clearer direction than letting it fix random things.

@snowie2000

Copy link
Copy Markdown
Owner

The whole point of using your control center version to replace my existing solution is to make things easier and cleaner, not to create more confusion or ask users to follow stricter rules.

@115dkk

115dkk commented Jul 31, 2026

Copy link
Copy Markdown
Author

Now I got another error: Install MacType Control Center first Service installation and maintenance are unavailable from a standalone development executable. Run the complete installer first.

Why on earth do I have to use your installer, and what's the whole point of preventing me from installing your service all the time?

Please do not solely rely on AI to solve problems. If you have to, please give it clearer direction than letting it fix random things.

The whole point of using your control center version to replace my existing solution is to make things easier and cleaner, not to create more confusion or ask users to follow stricter rules.

I see.... This was my fatal mistake for insisting on my own stubbornness. I lost sight of the fact that no matter how great something may seem, it is meaningless if it cannot actually be used.

I do use AI when modifying the code, but I am the one directing those changes. Therefore, the problems that resulted are entirely my responsibility.

This fix may take a little longer, as it will require substantial changes to both the behavior and the tests.

I would like to apologise once again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants