Anchoran OS is an immersive desktop shell that runs as a Windows application
(AnchoranOS.exe). It is not a real operating system, kernel, or bootloader —
it is a fullscreen, kiosk-style Electron application designed to feel like an
independent, professional desktop environment.
- Electron — native Windows shell, fullscreen kiosk window, global
shortcuts, packaging, on-disk persistence (
electron-store), auto-update (electron-updater). - React + TypeScript — the entire desktop UI.
- Vite — dev server / renderer bundler.
- Zustand — state (WindowManager, preferences, filesystem, notifications).
- Vitest — unit tests for the WindowManager and virtual filesystem stores.
- electron-builder — produces the final
AnchoranOS.exe(NSIS installer).
electron/ Electron main process + preload (the host shell): fullscreen
window, on-disk stores, real system info, error logging,
global shortcuts, auto-update
installer/ AnchoranSetup — the branded fullscreen installer app (see
"Custom fullscreen installer" below); its own Electron app
src/
core/ Version source-of-truth, shared types, Electron bridge
types, persistence helper, synthesized system sounds,
error boundary / crash safety net
theme/ Light/Dark tokens, ThemeProvider, preferences store
windowmanager/ Centralized WindowManager — open/close/focus/minimize/
maximize/move/resize (all 4 edges + corners), edge
snapping, per-app remembered bounds, Ctrl+Tab switcher
desktop/ Wallpaper, system bar, dock, desktop icons, context menu,
running-windows strip (task switcher)
launcher/ Anchoran Launcher (search + open apps + Settings/Power)
applications/ Files, Terminal, Settings, Notes, Calculator, Browser,
System Monitor, App Center
filesystem/ Anchoran's own virtual filesystem (never touches host
files by default; Files supports drag-and-drop import)
notifications/ Notification store + toasts + persistent history panel
boot/ Boot screen
lock/ Lock screen
power/ Power menu, "Exit Anchoran?" confirmation, shutdown/
restart/sleep transition screen
components/ Shared UI primitives (Icon, etc.)
assets/
logo/ Anchoran logo (anchoran-logo.svg)
icons/ Reserved for non-code-generated icon assets
wallpapers/ Reserved for photographic/illustrated wallpapers (current
wallpapers are code-generated gradients, see
src/desktop/wallpapers.ts)
fonts/ Self-hosted Inter variable font
version.json Single source of truth for Anchoran's SemVer version
CHANGELOG.md Keep-a-Changelog formatted history
.github/workflows/ CI (typecheck+test+build) and tag-triggered release build
Anchoran uses strict SemVer: MAJOR.MINOR.PATCH,
starting at 1.0.0. The only file to hand-edit is /version.json.
npm run sync-version (run automatically before dev/build) propagates it
into both package.json and installer/package.json, which electron-builder
reads to stamp the version onto the built executables. This real SemVer value
is what every technical thing keys off forever — git tags, download URLs, the
update mechanism's own version comparisons, and anchoran changeto/anchoran version in the Terminal. Update CHANGELOG.md alongside every version bump,
including a **Update type:** line right under the new heading (security /
critical / stability / feature / performance / maintenance — see
src/core/updateInfo.ts), which scripts/generate-update-info.cjs turns into
the update-info.json release asset the updater reads to label what kind of
update it is.
Display convention, starting at v3.0.0: everywhere a person reads
Anchoran's version — the boot screen, Settings → About, Anchover, taskbar
update tooltips, the App Center, GitHub release titles — shows "Version
{major} | Build {major}H{minor}.{patch}" (e.g. "Version 3 | Build 3H0.1")
instead of the plain dotted number, built by src/core/buildNumber.ts from
the real version. v3.0.0 is the very last GitHub release named in the old
vX.Y.Z style; every release after it uses the new naming there too. This
is purely cosmetic — nothing that actually matches versions (tags, latest.yml,
anchoran changeto) ever looks at a release's display name, so the switch
doesn't affect update compatibility for devices on either side of it.
Releases ship through an Insider Preview Update (I.P.U.) stage before becoming stable — and unlike a single "beta of this one version", an I.P.U. streak can chain across several version bumps in a row, with no stable release cut until everything queued for that streak is actually ready:
- Insider Preview Update(s) — tag
vX.Y.Z-IPU.electron-updater'sallowPrerelease(toggled by Settings → About → "Insider Preview updates") means only devices opted into that ever see these offered automatically; the release workflow ships each one as a GitHub prerelease, namedvX.Y.Z I.P.U.forv3.0.0itself or "Version # | Build #H#.# I.P.U." for anything after it (see the display convention above). The next change, however small, bumpsversion.jsonforward and tags again as-IPU— there's no.1/.2respin numbering, and tags are never deleted/recreated (see the versioning rules above) — for as long as more is still queued to add before this goes out to everyone. - Stable release — once satisfied, tag the current version plainly
(
vX.Y.Z, no-IPUsuffix). This becomes the repo's Latest, available to everyone. Its release notes (and the in-app "What's new" panel, for anyone who updates straight from the last stable release) automatically cover every version in the I.P.U. streak that preceded it, not just this last one — see the "Determine previous stable version" step in.github/workflows/release.yml.
See the "Determine release channel" step in .github/workflows/release.yml
for how a tag's -IPU suffix changes the release's name/prerelease flag.
npm install
npm run devThis starts the Vite dev server and an Electron window pointed at it, fullscreen, frameless — the same shell used in production.
npm run testRuns the Vitest suite (WindowManager and virtual filesystem store logic).
npm run typecheck runs a full TypeScript check with no emit.
npm install
npm run distThis will:
- Sync
version.jsonintopackage.json. - Build the React renderer (
vite build→dist/). - Compile the Electron main/preload process (
tsc→dist-electron/). - Run
electron-builder, producing a Windows NSIS installer underrelease/.
installer/ is a second, small Electron app: a fullscreen, Anchoran-branded
installer experience, instead of showing users the default NSIS wizard.
npm run dist:fullruns, in order: npm run dist (builds AnchoranOS's own NSIS installer),
prepare-installer-source (copies that installer to a fixed filename), then
installer's own npm install + electron-builder build, producing
AnchoranSetup.exe under release-setup/. AnchoranSetup.exe is the one
executable meant to be distributed to end users.
How it works: AnchoranSetup bundles AnchoranOS's real NSIS installer and runs
it silently (/S) in the background while showing a fullscreen progress
screen (Preparing → Copying → Configuring → Done) with Anchoran's logo and
palette; on success it launches AnchoranOS.exe and closes itself.
Known caveats (MVP, not yet hardened):
- NSIS's silent mode doesn't report real byte-level progress, so the stage list is a timed animation rather than a literal progress readout — the same trade-off most branded installer wrappers around a silent NSIS/MSI payload make.
- After a silent install, AnchoranSetup guesses the installed
AnchoranOS.exepath (%LOCALAPPDATA%\Programs\Anchoran OS\) to auto-launch it; if that guess is wrong (e.g. a non-default install location) it falls back to telling the user to launch Anchoran OS from the Start Menu instead of crashing. - The installer's copy of the logo (
installer/renderer/logo.svg) is a manual duplicate ofassets/logo/anchoran-logo.svgfor packaging simplicity — update both if the logo changes. - This chain has been typechecked and compiled, but not smoke-tested end-to-end (installing, launching) in this environment — see "Known limitations" below for why.
As of v3.4.0, only ~21 apps under src/applications/ still ship inside
Anchoran OS's own build — genuine OS chrome (Files, Terminal, Settings, the
Webstore itself, …), a handful of apps wired in as Files'/Launcher's own
default file-type/URL handlers (notes, photoViewer, mediaPlayer, browser),
and apps that need privileged host access a plugin can't have (System
Monitor, Recycle Bin, Startup Apps, …). Every simple utility and game app
that used to live here (45 of them) moved out to standalone Webstore
plugins in the v3.4.0 migration — see CHANGELOG.md for the full list and
src/core/legacyAppIds.ts for the old-id → new-plugin mapping.
Adding or updating a still-bundled app needs a new Anchoran OS release. Third-party plugin apps are the opposite: they live in a completely separate repo, Anchoran-Webstore, versioned and released on its own, and Anchoran OS downloads one only when someone clicks Install on it in the Webstore's Community section — no Anchoran OS release required to publish or update a plugin.
This works because a plugin never touches Anchoran's own internal @/...
modules (no compatibility promise, they change freely between versions).
Instead, its one built file exports a mount(container, sdk, ctx) function;
Anchoran calls it once with a real DOM node and window.AnchoranSDK — see
src/core/anchoranSDK.ts for the exact shape (React, ReactDOM, Icon,
IconTile, pushNotification, getAccentColor, getThemeMode) and the
Anchoran-Webstore repo's own README for the plugin author's side of this
contract, including how to build and publish one. PluginHost.tsx is the
generic window every installed plugin opens into; downloaded plugin files
live in userData/plugins/<id>/ and are served to the renderer through a
privileged anchoran-plugin:// protocol (registered in electron/main.ts)
rather than a raw file:// import, since Chromium can block a cross-path
file:// ES module fetch under the default webSecurity Anchoran runs with.
The pilot ("Hello, Anchoran") proved the pipeline end to end in v3.3.6; the v3.4.0 migration is what actually populated the catalog for real — 37 plugins as of Anchoran-Webstore v2.0.0. Still just SDK v1.0.0 — it only ever grows in backward-compatible ways from here.
.github/workflows/ci.yml— typecheck, test, and build on every push/PR tomain..github/workflows/release.yml— on pushing av*tag (e.g.v1.0.0), buildsAnchoranOS.exeand attaches it to a GitHub Release.- Auto-update (
electron-updater) is live:electron-builder.yml'spublishblock points atfachu2012/Anchoran-OSon GitHub, Anchoran checks for updates on startup, andanchoran updatein the Terminal (or Settings → About → "Check for updates") trigger a check on demand with real progress ("Downloading… N%", then "Restart & install"). It only actually finds an update once.github/workflows/release.ymlhas published a GitHub Release — taggedvX.Y.Z— for a version newer than what's installed; until a tag is pushed there's simply nothing to find, which is expected, not broken. - Code signing is intentionally not set up — without it, Windows SmartScreen will show an "unknown publisher" warning on install. Signing needs a paid certificate, which is a business decision, not a technical one, so it's left for you to decide on.
- Windows key interception: Anchoran attempts to register the bare
Windows key as a global shortcut to open its Launcher, but Windows
Explorer/the Start Menu itself owns that key at the shell level —
globalShortcut.register("Super", …)often fails to register at all on Windows, and even when it succeeds, Windows may still win the race and open its own Start Menu. Electron'sglobalShortcutAPI — the only mechanism a normal, non-elevated app has — cannot reliably override this; doing so would require a native, system-wide low-level keyboard hook, which this project has intentionally not added yet.Ctrl+Alt+Lis registered as a reliable fallback and always opens the Anchoran Launcher — a combination that doesn't touch the Windows key at all, so it avoids that whole class of OS-reserved collision (two earlier choices were dropped for exactly that reason:Ctrl+Spacefrequently collides with Windows' own input-method switch hotkey, andCtrl+Win— despite not being the bare Windows key — turned out to still be claimed by Windows on some machines, e.g. for Ink Workspace/accessibility shortcuts). The Launcher is also always reachable from the dock and system bar. Anchoran never disables or intercepts Windows' own critical shortcuts (Ctrl+Alt+Del, Task Manager, sign-out, forced shutdown, or any recovery mechanism). - Not literal Alt+Tab: for the same reason as the Windows key, Anchoran's
window switcher is bound to
Ctrl+Tab/Ctrl+Shift+Tabinstead — Windows owns Alt+Tab at the shell level too. - No true kernel-level kiosk lock: Anchoran cannot and does not attempt to prevent Windows from regaining control through its own emergency mechanisms. It creates a convincing, immersive experience, not a locked device.
- Multi-monitor is partial: Anchoran is still a single fullscreen window (not an independent desktop per display); Settings → Display lets you move that window to a different connected monitor, but you can't currently have Anchoran occupy two displays as one spanned desktop.
- Files import is text-only: dragging a file from Windows into Files imports its text content when the file looks text-based; binary files (images, PDFs, archives, etc.) are added as empty placeholder entries rather than silently dropped, since Anchoran's virtual filesystem only stores text content today.
- This session's sandbox could not run the actual Electron GUI
(
ELECTRON_RUN_AS_NODE=1is forced in this tool environment), so builds here were verified by typechecking, unit tests, and successfulelectron-builderpackaging — not by visually running the app. Runnpm run devor the built.exeyourself to see it live.