Windows Desktop клиент — Developer гарын авлага¶
eID платформын Windows (C# / WinUI 3) desktop клиент — гар утасны e-ID
Mongolia апп-аар QR / РД push нэвтрэлт, USB токен (смарт карт) удирдлага,
хэрэглэгчийн хяналтын самбар, байгууллагын төлөөлөл, гэрчилгээ баталгаажуулалтыг
хийх ширээний програм. Эх код: desktop/windows-app/ (Clean Architecture,
.NET solution eIDMongolia.Desktop.sln).
First-party client — rewire дууссан. Энэ клиент нь
geregev1 desktop-оос импортлогдож eID Mongolia болгон rebrand хийгдсэн (eIDMongolia.*namespace, MSIX identitymn.eidmongol.desktop). macOS клиент (DESKTOP_MACOS.md) болон iOS апп-тай яг ижил зарчмаар энэ бол RP биш — клиентэд ямар ч RP secret, RP UUID, device HMAC нууц, удаан насладаг bearer token байхгүй. Бүх дуудлага web backend-ийн нийтийн/api/*route-уудаар (browser-тэй яг ижил зам) дамжина; Go RP-API (/v3/*)-ийнRP_API_SECRET-ийг зөвхөн web сервер (web/src/lib/rpclient.ts) барьдаг. Хуучин gerege-ийн шууд-backend (/web2app/v1,/rp/v1, device-HMAC, Bearer session) загвараас/api/*руу шилжүүлэх ажил дууссан; дэлгэрэнгүй буулгалт:desktop/windows-app/BACKEND-INTEGRATION.md.
1. Юу хийдэг вэ¶
Кодоос батлагдсан боломжууд (Views/Pages/*, Infrastructure/*). Нэвтрэлтийн
дараа sidebar-т Home · Dashboard · Organizations · Tokens · Verify цэсүүд
гарна (нэвтрэхээс өмнө зөвхөн Login) — ShellViewModel.BuildMenu():
- Нэвтрэлт (
LoginPage/LoginViewModel,CitizenAuthService) — хоёр идэвхтэй горим: - РД / иргэний дугаараар push — утас руу мэдэгдэл илгээж PIN1-ээр
баталгаажуулна (
POST /api/login-notify). - QR — QR код харуулж (
QRCoder-оор рендэр), утсаар скан хийлгэнэ (POST /api/start). - USB токен удирдлага (
TokensPage/TokenDetailPage) — залгагдсан смарт карт / токенуудыг илрүүлэх, төлөв (Blank/Initialized/Provisioned/ Enrolled) харах, түлхүүр үүсгэх, PIN удирдлага. Токены стек нь бие даасан (Windows CNG + PKCS#11, §6 үз). - Хяналтын самбар (
DashboardPage/DashboardService) — нэвтэрсэн иргэний гэрчилгээ / төхөөрөмж / идэвхийн тоолол, нэр (/api/dashboard,/api/devices,/api/activity). - Байгууллага (
OrganizationsPage/OrgService) — нэвтэрсэн иргэний төлөөлж чадах ACTIVE байгууллагуудын жагсаалт (/api/representations). - Баталгаажуулалт (
VerifyPage/VerifyViewModel) — гэрчилгээг PEM paste эсвэл файлаар клиент талд (ICertificateService.ParsePem/ParseFile) задлаж, subject / хүчинтэй хугацаа / validity-г харуулна. Backend дуудлагагүй (macOS-той адил).
Note — PDF гарын үсэг зурах UI одоогоор алга. First-party PDF-signing service нь бэлэн (
RpAuthService→POST /api/sign-pdf-start, PDF-ийн SHA-256 digest-ийг клиент өөрөө тооцно), гэвч түүнийг дуудах ViewModel / Page байхгүй —IRpAuthService-ийг DI-д бүртгэснээс өөр газар хэрэглэдэггүй, sidebar-т Sign цэс алга. МөнRpAuthServiceнь тамгалагдсан PDF-ийг татаж авдаг/api/sign-pdf-download-ийг дууддаггүй (зөвхөн raw signature авдаг). Иймд Windows тал дээр PDF-д гарын үсэг зурах бүрэн урсгал одоогоор байхгүй — macOS клиент дээр л бүтэн ажилладаг. USB-токен нэвтрэлт мөн идэвхгүй (§5, stub).
2. Архитектур¶
2.1. Clean Architecture давхаргууд¶
Solution нь дөрвөн эх давхарга + WinUI entry point + тестээс бүрдэнэ
(eIDMongolia.Desktop.sln):
| Төсөл | Үүрэг | Хамаарал |
|---|---|---|
eIDMongolia.Domain |
Entity, value object, Result<T>, ApiError, токены төрлүүд. Гадаад хамааралгүй. |
— |
eIDMongolia.Application |
Use-case abstraction (IBackendApi, ICitizenAuthService, ICryptoTokenProvider, …), eIDMongoliaOptions. |
Domain |
eIDMongolia.Infrastructure |
HTTP клиент, cert pinning, DPAPI vault, Windows Hello, PKCS#11 / CNG токен провайдер. Windows TFM. | Application, Domain |
eIDMongolia.Presentation |
ViewModel-ууд (CommunityToolkit.Mvvm), navigation, validation. UI framework-оос хараат бус. | Application, Domain |
eIDMongolia.Client |
WinUI 3 entry point — App.xaml, MainWindow, Views/Pages/*, Generic Host bootstrap (Hosting/AppHost.cs). |
бүх дээрх давхарга |
eIDMongolia.UnitTests |
xUnit тест. | бүх src төсөл |
Reference граф: Client → Presentation, Infrastructure, Application,
Domain; Presentation → Application, Domain; Infrastructure →
Application, Domain; Application → Domain.
Тодотгол —
Clientбол HTTP клиент БИШ. Нэрнээс үл хамааранeIDMongolia.Clientнь WinUI 3 UI/entry point давхарга. Бодит HTTP клиент ньInfrastructure/Http/BackendApiClient.csболонInfrastructure/Auth/*Service.csфайлуудад байдаг.
Хост: Microsoft.Extensions.Hosting Generic Host — DI, Configuration, Options,
Serilog. Bootstrap Client/Hosting/AppHost.cs.
2.2. Backend-тэй холбогдох — first-party /api/*¶
Infrastructure/DependencyInjection.cs дотор нэрлэсэн HttpClient
(eIDMongolia.Backend) тохируулна: BaseUrl (web origin), TLS 1.3,
cert-pinning primary handler, дараа нь AddStandardResilienceHandler (Polly
retry). Device-HMAC signer болон Bearer-attach handler нь pipeline-д
холбогдоогүй — first-party загварт хэрэггүй. Клиент нь web/src/app/api/*
(Next.js) route-уудыг browser шиг шууд дууддаг:
| Route | Метод | Зориулалт | Service |
|---|---|---|---|
/api/health |
GET | Серверийн эрүүл мэнд (live=ready) | BackendApiClient |
/api/login-notify |
POST | РД/иргэний дугаараар push нэвтрэлт | CitizenAuthService |
/api/start |
POST | QR нэвтрэлт эхлүүлэх | CitizenAuthService |
/api/status |
GET | Long-poll (?sessionId=&pollToken=) |
CitizenAuthService, RpAuthService |
/api/sign-pdf-start |
POST | PDF digest-д гарын үсэг эхлүүлэх (service бэлэн, UI алга) | RpAuthService |
/api/dashboard |
POST | {personId} → cert/төхөөрөмж/идэвхийн тоолол + нэр |
DashboardService |
/api/devices |
POST | {personId} → бүртгэлтэй төхөөрөмжүүд |
DashboardService |
/api/activity |
POST | {personId} → RP-scoped session түүх |
DashboardService |
/api/representations |
POST | {personId} → төлөөлж чадах ACTIVE байгууллага |
OrgService |
Эдгээр /api/* route-ууд нь Go RP-API-ийн /v3/*-руу web сервер дээр
first-party RP Bearer-ээр proxy хийгддэг (personId = нэвтэрсэн иргэний
civil_id / etsi, identity cache-аас). pollToken нь session эхлүүлэгчид л
олгогддог бөгөөд /api/status-аас PII (нэр / иргэний дугаар) авахад заавал
шаардлагатай.
Identity bridge (impedance mismatch-ийн шийдэл).
/api/statusньsession_token/user_idбуцаадаггүй; амжилттай нэвтрэхэд cert subject-оосname+idNumberшууд ирдэг. ИймдCitizenAuthService.PollAsyncнь энэ identity-гISessionIdentityCache-д хадгалж, session бүрд синтетикSessionToken(=sessionId) +UserId(GUID) үүсгэнэ;UserProfileService,DashboardService,OrgServiceнь personId-г энэ cache-аас уншина (/meendpoint байхгүй).
Хараахан эквивалент /api/* route байхгүй тул honest stub (crash биш —
ApiError.Internal("…not_available_on_first_party_backend") буцааж, UI цэвэрхэн
унана):
- USB-токен нэвтрэлт (
CitizenAuthService.InitiateTokenChallengeAsync/VerifyTokenAsync) — web backend дээрtoken/challenge·token/verifyалга. - Гэрчилгээ enroll (
CertEnrollmentService). - Байгууллагын WRITE үйлдлүүд (
OrgService-ийн lookup / register / members / name-en / X-Road CSR) — зөвхөнListAsyncжинхэнэ.
Хуучин handler-ууд tree-д үлдсэн. gerege-ийн
/web2app/v1device-HMAC (HmacSigningHandler) болон Bearer-attach (BearerAuthHandler) класс-ууд эх модонд хэвээр байгаа ч HTTP pipeline-д холбогдоогүй — ирээдүйн шууд-backend build-д лавлагаа болгож үлдээв. Тэднээс юу ч хамаардаггүй.
Тохиргоог eIDMongoliaOptions-оос уншина (appsettings.json +
appsettings.{ENV}.json overlay + EIDMNG_ prefix-тэй env). Prod-д
BaseUrl=https://eidmongolia.mn; dev overlay (appsettings.Development.json)-д
BaseUrl=http://localhost:3000, RequireCertificatePinning=false,
RequireWindowsHello=false. CertificateSpkiPins болон XRoadClient нь
first-party загварт хоосон / null.
3. Урьдчилсан нөхцөл¶
Кодоос батлагдсан (Directory.Packages.props, *.csproj):
- .NET 8 (LTS), C# latest (12);
TreatWarningsAsErrors=true, nullable + implicit usings идэвхтэй. eIDMongolia.ClientTFM:net8.0-windows10.0.26100.0(TargetPlatformMinVersion10.0.17763.0) — өөрөөр хэлбэл Windows 10 1809+, build-д Windows 11 SDK (26100) шаардлагатай.- Windows App SDK 2.0.1 + WinUI 3 (
UseWinUI=true). - Visual Studio 2022 17.10+, Windows application development workload;
эсвэл
dotnetCLI. - Platform:
x86/x64/ARM64. - USB токен ашиглах бол хостод PKCS#11 модуль суусан байх (OpenSC
opensc-pkcs11.dllэсвэл Feitianeps2003csp11.dll) — заавал биш, байхгүй бол токены хэсэг л идэвхгүй болно.
4. Build ба ажиллуулах¶
cd desktop/windows-app
dotnet restore eIDMongolia.Desktop.sln
dotnet build eIDMongolia.Desktop.sln -c Debug /p:Platform=x64
dotnet test tests/eIDMongolia.UnitTests/eIDMongolia.UnitTests.csproj
Visual Studio-д: eIDMongolia.Desktop.sln нээж, eIDMongolia.Client-ийг
startup project болгоод F5.
Хоёр гүйцэтгэлийн горим (eIDMongolia.Client.csproj):
EidMsixтохируулаагүй → unpackaged single-file exe (dev,dotnet run).EidMsix=true→ packaged MSIX (publish /pack-msix.ps1, §8).
Note — build/run зөвхөн Windows дээр. Infrastructure давхарга нь Windows TFM (WinUI, CNG, DPAPI, PKCS#11) тул macOS/Linux дээр build хийгдэхгүй. CI-д Windows runner дээр бүрэн build + test + MSIX багцлалт хийгддэг (§7).
Тохиргоо¶
src/eIDMongolia.Client/appsettings.json нь канон тохиргоо;
appsettings.Development.json overlay-гаар dev утга. Env-ээр override:
Бүх утга eIDMongoliaOptions-д bind хийгдэж DataAnnotations-оор эхэнд шалгагдана.
5. Гол урсгалууд¶
Нэвтрэлт (CitizenAuthService)¶
РД push: POST /api/login-notify {register} → {sessionId, vc, pollToken}.
register нь РД / civil-id / PNOMN-… — сервер алийг ч resolve хийдэг. Утас
руу push (rate limit ~3/60с per target). Дараа poll.
QR: POST /api/start {} → {sessionId, qr, deviceLinkBase, vc, pollToken}.
qr (==sessionId)-ийг QR болгож (QRCoder) харуулна.
Poll (хоёуланд нийтлэг): GET /api/status?sessionId=&pollToken=. Сервер
~1с барьж буцаадаг, ViewModel дахин poll хийнэ. Локал session state machine
(server/internal/domain/enums.go): state = RUNNING (үргэлжлүүл) |
COMPLETE (терминал); endResult = OK | TIMEOUT | USER_REFUSED* |
WRONG_VC | FAILED | …. OK дээр identity (name, idNumber,
certificateLevel) inline ирнэ (bearer / cert PEM байхгүй) → identity cache-д
хадгалагдана.
USB токен нэвтрэлт (M9.B) — идэвхгүй (stub). InitiateTokenChallengeAsync /
VerifyTokenAsync нь ApiError.Internal("token_login_not_available_on_first_party_backend")
буцаана — web backend дээр token/challenge·token/verify эквивалент байхгүй.
"Токен" login таб цэвэрхэн degrade хийнэ.
Logout: сервер талд цуцлах session/bearer байхгүй — зөвхөн identity cache + локал session store-ийг цэвэрлэнэ.
PDF гарын үсэг (RpAuthService) — service бэлэн, UI алга¶
InitiateAsync нь PDF bytes-ийн SHA-256 digest-ийг локал тооцоод
POST /api/sign-pdf-start {etsi, digestB64, fileName, callbackUrl} дуудна;
PollAsync нь /api/status-аар (sessionId → pollToken-ийг per-session
ConcurrentDictionary-д threading) төлвийг хардаг. OK дээр
signatureValueB64 inline ирнэ. Гэвч энэ service-ийг дуудах ViewModel / Page
одоогоор байхгүй, мөн тамгалагдсан PDF-ийг татдаг /api/sign-pdf-download-ийг
дууддаггүй (§1 note үз).
Хяналтын самбар / Байгууллага¶
DashboardService.LoadAsync → /api/dashboard + /api/devices + /api/activity
(personId нь identity cache-аас). OrgService.ListAsync → /api/representations
(нэвтэрсэн иргэний ACTIVE төлөөлөл). Бусад org үйлдэл stub.
Баталгаажуулалт (VerifyPage)¶
Гэрчилгээг PEM paste эсвэл файлаар клиент талд задалж (ICertificateService),
subject / хүчинтэй хугацаа / validity-г харуулна — backend шаардлагагүй.
6. USB token / смарт карт (self-contained)¶
Windows клиент нь USB токен / смарт картыг өөрийн дотоод хэрэгжүүлэлтээр
дэмждэг (Infrastructure/Tokens/). (USB-токен нэвтрэлт нь first-party
backend дээр эквивалентгүй тул идэвхгүй — §5; гэвч токен удирдлага
бүрэн ажиллана.)
- Windows CNG (
Tokens/Cng/WindowsCngTokenProvider.cs) — Microsoft Base Smart Card Crypto Provider ашиглан Windows өөрөө PIN dialog харуулна. - PKCS#11 (
Tokens/Pkcs11/Pkcs11TokenProvider.cs,Pkcs11Interop5.2.0) — олдсон модуль тус бүрт нэг провайдер бүртгэдэг (OpenSCopensc-pkcs11.dll+ Feitianeps2003csp11.dllзэрэг зэрэгцэн). Модулийн замыгTokenOptions.Pkcs11ModuleCandidates-аас (literal → System32 → ProgramFiles дарааллаар) resolve хийнэ;Pkcs11ModulePathoverride-оор ганц DLL заах боломжтой.TokenRegistryнь public key fingerprint-ээр давхар харагдсан ижил картыг dedupe хийдэг.
Токены төлөвийн машин (Domain/Tokens/TokenTypes.cs): Blank → Initialized →
Provisioned → Enrolled. Enroll зам (SO PIN init → user PIN → keypair → CSR)
нь CertEnrollmentService-д хүрдэг боловч тэр нь одоогоор stub
(§2.2).
Note —
gerege-token-kit-тэй холбоогүй. macOS клиент нь Swift SPM packagedesktop/gerege-token-kit/-ийг ашигладаг. Windows клиент түүнийг хэрэглэдэггүй — өөрийн C# CNG + Pkcs11Interop стек нь бие даасан.
7. CI / CD¶
Windows workflow: .github/workflows/windows-app.yml.
desktop/windows-app/** эсвэл workflow өөрчлөгдөхөд ажиллана. Хоёр job:
| Job | Runner | Trigger | Юу хийдэг |
|---|---|---|---|
build |
windows-latest (GitHub-hosted) |
push main / PR / dispatch |
restore → build (Debug) → test → dev-cert self-sign → MSIX багцлаж артефакт (eid-mongolia-windows-msix) болгон upload |
selfhosted |
[self-hosted, windows-signing] (win11-build, 38.180.136.249) |
зөвхөн workflow_dispatch |
build → test → dev-cert self-sign → MSIX → signed .msix + public .cer-ийг deploy/downloads/-д commit → push |
selfhosted job нь signed багцыг deploy/downloads/-руу git-ээр нийтэлдэг
(forced-command SSH тул scp боломжгүй — staging ci-deploy git reset --hard-аар
авч nginx /download/-д үйлчилнэ). Одоо dev-cert-ээр self-signed; жинхэнэ
EV/OV cert ирэхэд New-DevCert.ps1-ийг орлуулна.
Note — build одоо ногоон. Хуучин "macOS/CI дээр build хийгддэггүй" төлөв хоцрогдсон:
/api/*rewire + pipeline адаптаци дууссаны дараа solution нь CI Windows runner дээр бүрэн build + test + MSIX багцлалт хийдэг.
8. Packaging (MSIX / .appinstaller)¶
Бүрэн pipeline: tools/README.md.
Товчхон (tools/*.ps1):
# 1) Машин тус бүрт нэг удаа (admin) — dev код-гарын үсгийн cert үүсгэнэ
.\tools\dev-cert\New-DevCert.ps1
# 2) Build + sign MSIX → artifacts/
.\tools\pack-msix.ps1 -Configuration Release -Platform x64 -Version 0.1.0.0
# 3) Локал суулгах
Add-AppPackage -Path .\artifacts\eid-mongolia-setup.msix
- MSIX багцлалт нь WinApp SDK-ийн single-project MSIX MSBuild tooling-оор
хийгддэг (
dotnet publish -p:EidMsix=true); дотор ньmakeappx/signtool-ийгMicrosoft.Windows.SDK.BuildTools[.MSIX]NuGet багцаас авдаг тул системд Windows SDK суулгах шаардлагагүй.pack-msix.ps1нь signtool-ийг PATH → Windows SDK → NuGet BuildTools дарааллаар resolve хийжsigntool sign /tr <timestamp>-оор гарын үсэг зурна. - Гаралт:
artifacts/eid-mongolia-setup.msix(тогтмол нэр — веб татах линк ба CI-д ашиглагдана) +artifacts/<Name>_<Version>_<Platform>.msix(versioned archive). publish-appinstaller.ps1—.appinstallertemplate-ийг рендэрлэж Windows-native auto-update тохируулна (launch дээр URL шалгаж, гарахад чимээгүй шинэчилдэг).- Publisher DN:
CN=Gerege Systems LLC, O=Gerege Systems LLC, C=MN(Package.appxmanifest<Identity Publisher=…>, MSIX Namemn.eidmongol.desktop). Код-гарын үсгийн cert-ийн subject DN нь үүнтэй яг таарах ёстой —New-DevCert.ps1ижил subject ашигладаг. Production-д Microsoft Trusted Root Program-д багтдаг EV/OV cert (DigiCert / GlobalSign / Sectigo) хэрэглэнэ. PFX-ийг хэзээ ч commit хийхгүй.
9. Татаж суулгах (эцсийн хэрэглэгч)¶
Signed MSIX + итгүүлэх cert нь /download/-д амьд (nginx deploy/nginx/staging.conf
location ^~ /download/ → deploy/downloads/; веб дэх татах товч
web/src/components/landing/DesktopAppSection.tsx, web/src/app/solutions/page.tsx):
| Файл | Веб линк |
|---|---|
| Windows суулгагч | https://eidmongolia.mn/download/eid-mongolia-setup.msix |
| Итгүүлэх public cert | https://eidmongolia.mn/download/eid-mongolia.cer |
Одоогийн
.msixнь dev-cert-ээр self-signed. Суулгахын өмнөeid-mongolia.cer-ийг Trusted People (эсвэл Trusted Root)-д итгүүлнэ:.cer-ийг татаж → давхар товшоод Install Certificate → Local Machine → Trusted People. Дараа.msix-ийг давхар товшиж суулгана. Production-д жинхэнэ EV/OV cert ирэхэд энэ гар итгүүлэлт шаардагдахгүй.
10. Аюулгүй байдлын анхны утгууд¶
SecurityOptions + Infrastructure/Security/*-аас:
| Асуудал | Механизм |
|---|---|
| TLS | TLS 1.3 (pinning идэвхтэй үед зөвхөн 1.3; эсрэг тохиолдолд 1.2+1.3) |
| Cert pinning | SPKI-SHA256 (SpkiPinValidator, CertificateSpkiPins); RequireCertificatePinning (dev=false) |
| Секрет | Windows DPAPI vault (DataProtectionSecretVault) |
| Biometric | Windows Hello (WindowsHelloService), RequireWindowsHello (prod=true) |
| Idle lock | IdleLockService (IdleTimeoutSeconds, default 900с) |
| Clipboard | ClipboardService авто-цэвэрлэлт (ClipboardClearSeconds) |
| RASP | RaspService — debug/tamper probe snapshot |
| Логгинг | Serilog → Console + Debug + rolling file (%LOCALAPPDATA%/eIDMongolia/logs/) |
Note — device-HMAC pipeline-д холбогдоогүй. Хуучин gerege
/mobile/*device-HMAC гарын үсэг (HmacSigner+ тест) кодод хэвээр байгаа боловч first-party загварт HTTP pipeline-д залгагдаагүй (§2.2).
11. Тест¶
tests/eIDMongolia.UnitTests — xUnit + FluentAssertions + NSubstitute.
Одоогоор бодит тест нь Security/HmacSignerTests.cs (+ placeholder) — тестийн
хамрах хүрээ хязгаарлагдмал, апп шинэ болохыг харуулна. CI-д
(windows-app.yml) dotnet test-ээр ажилладаг.
12. Эх код (холбоос)¶
- Windows апп:
https://github.com/gerege-systems/eid-platform-mn/tree/main/desktop/windows-app - Backend интеграцийн буулгалт:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/BACKEND-INTEGRATION.md - HTTP клиент:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Http/BackendApiClient.cs - Иргэний нэвтрэлт:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Auth/CitizenAuthService.cs - PDF гарын үсэг (service):
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Auth/RpAuthService.cs - Dashboard:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Dashboard/DashboardService.cs - Байгууллага:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Org/OrgService.cs - DI / HttpClient тохиргоо:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/DependencyInjection.cs - MSIX manifest:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Client/Package.appxmanifest - PKCS#11 токен:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/src/eIDMongolia.Infrastructure/Tokens/Pkcs11/Pkcs11TokenProvider.cs - Packaging:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/windows-app/tools/README.md - CI workflow:
https://github.com/gerege-systems/eid-platform-mn/blob/main/.github/workflows/windows-app.yml - macOS клиент (зэрэгцүүлэл): DESKTOP_MACOS.md