Files
QSfera/PRODUCTION_READINESS.md
2026-06-13 20:07:01 +03:00

111 lines
23 KiB
Markdown

# QSfera Production Readiness
Date: 2026-06-08.
## Benchmarks Used
- Google Photos Android backup: automatic photo/video backup, selected device folders, backup status, mobile-data limits, roaming controls, and battery optimization guidance.
Source: https://support.google.com/photos/answer/6193313?co=GENIE.Platform%3DAndroid&hl=en
- Google Drive Android offline files and sharing roles.
Sources: https://support.google.com/drive/answer/2375012?co=GENIE.Platform%3DAndroid&hl=en and https://support.google.com/drive/answer/2494822?co=GENIE.Platform%3DAndroid&hl=en
- Google storage quota behavior across Drive and Photos.
Source: https://support.google.com/drive/answer/9312312?hl=en
- Google Drive for desktop: tray entry, multi-account sign-in, sync status, notifications, offline files, pause/resume sync, adding folders, and stream/mirror model.
Sources: https://support.google.com/drive/answer/10838124?co=GENIE.Platform%3DDesktop&hl=en-fm and https://support.google.com/drive/answer/13401938?hl=en
- Yandex Disk Android auto-upload setup, account choice, network choice, source folders, and uploaded/not-uploaded indicators.
Source: https://yandex.com/support/yandex-360/customers/disk/app/android/en/autoupload/setup
- Yandex Disk mobile link sharing controls: view-only, disable downloads, password, expiration, and organization-only links.
Source: https://yandex.com/support/yandex-360/customers/disk/app/android/en/actions/share
- Yandex Disk desktop: Windows/macOS desktop app, selective sync, auto-save folders, automatic update, autorun, and sleep-prevention behavior during sync.
Sources: https://www.yandex.com/support/yandex-360/customers/disk/web/en/desktop, https://www.yandex.com/support/yandex-360/customers/disk/desktop/windows/en/reserve-copy, and https://yandex.com/support/yandex-360/customers/disk/desktop/windows/en/installation
## Completed In This Pass
- Android release builds now fail fast when release signing variables are missing, so unsigned release artifacts cannot be published by accident.
- Android version was raised to `1.3.12` with `versionCode` `40`.
- Android version was raised to `1.3.13` with `versionCode` `41` for the next Argus publication.
- Android version was raised to `1.3.14` with `versionCode` `42` for the updated SSH-only Argus publication flow.
- Android now has QSfera-specific Argus publishing scripts: local upload-keystore generation, signed APK staging, APK metadata verification, debug-certificate rejection, and SSH-only Argus publication through the `/srv/argus-data/argus.db` and `/srv/argus-data/Packages` workflow required by `G:/Repos/Marketplace/ARGUS_PUBLICATION.md`; HTTP `/api/admin/*` publication is not used.
- Android Argus update and publication were realigned with `G:/Repos/Marketplace/ARGUS_PUBLICATION.md` on 2026-06-13: `Android/scripts/publish-release.ps1` now generates and runs the SSH/SQLite/Data/Packages server-side publication block, and `AppUpdateManager` uses only the public read-only manifest contract for update discovery.
- Android now has `Android/scripts/verify-release-signing-material.ps1` to verify local release signing properties, keystore readability, release certificate SHA-256, and optionally the latest published Argus APK certificate without printing signing secrets.
- Android Argus staging now enforces release git provenance by default: the work tree must be clean, the branch must have an upstream, `git fetch` must succeed, and `HEAD` must match `@{u}` before a stable APK is prepared for upload. The generated `argus-publication.json` records the verified repository root, branch, upstream, commit, and timestamp; current monorepo publication can verify the pushed root `QSfera.git` branch by running from `Android/` with `-GitProvenanceRoot ..`.
- Android `1.3.9`/`37` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `53ecc24e-106b-43a5-a39e-ffacd72a43f8`, package size `12226572`, and SHA-256 `cbd2c25328e395ea4da4e0f582feac39dec18d2d47b3e105d344904d8a24f017`.
- Android `1.3.10`/`38` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `8465ba6d-a008-45f5-9c70-e6cd2c94317a`, package size `12228464`, and SHA-256 `ce33a73ce122de6c9c797e7cb552e5493b628c60ddc98fda6a9e230e3c1ed7fe`.
- Android `1.3.11`/`39` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `3dbac5ad-b19c-46e6-b24f-8f22e1c466dd`, package size `12234884`, and SHA-256 `c9eae41bbaae421e1a69721e891d8431ce599684e0f655b71693a422aa83720a`.
- Android `1.3.12`/`40` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `615b58c5-b5f7-4a2a-b6de-03f821eb43a4`, package size `12237552`, and SHA-256 `279d00c6a43072caa0b26269f547fb0dc3268088aae5cd9f4ccfbbb485c35ded`.
- Android `1.3.13`/`41` was published to Argus as `qsfera-mobile` on 2026-06-12. Argus upload returned release id `39072ada-7557-48f3-ab42-ad68703d00b0`, package size `12261408`, and SHA-256 `f065e6584e6a09999e72ba0eeeaae903e93fb8980a0426e4b39b5e2366bf6611`.
- Android `1.3.14`/`42` was published to Argus as `qsfera-mobile` on 2026-06-13 through the SSH/SQLite/Data/Packages workflow from `G:/Repos/Marketplace/ARGUS_PUBLICATION.md`. The server-side publication block returned release id `4394333f-a84a-4a3b-9bc7-6a48d054d4fe`, stored path `qsfera-mobile/20260613170521-1.3.14-4394333FA84A4A3B9BC76A48D054D4FE.apk`, package size `12262908`, and SHA-256 `8ddfbb9d3dbff3f7bc1030024a0f03a7928771b06cb373c8996c96fb22ad5a18`.
- `Android/scripts/verify-release-signing-material.ps1 -VerifyPublishedApk` passed on 2026-06-12: local `Android/argus-signing.local.properties` and `Android/signing/argus-upload.jks` are complete/readable, alias `argus-upload` exists, and both the keystore certificate and the latest published Argus APK report certificate SHA-256 `ea5c304c78ee333f6586f54c933d986c1f865da9c4e3595f8fe0df274c1ab877`.
- Android auto-upload source paths are normalized and new/first source folders start from timestamp `0`, so existing media is eligible for initial backup.
- Android auto-upload validates persisted read permission and `DocumentFile.canRead()` before scanning SAF folders.
- Android immediate auto-upload scheduling no longer blocks the caller while waiting for WorkManager state.
- Android new auto-upload configurations default to Wi-Fi-only transfers for photos and videos.
- Android auto-upload now exposes a no-roaming control for photo and video backup, stores it in Room schema version `50`, and maps mobile backup without roaming to WorkManager `NetworkType.NOT_ROAMING`.
- Android auto-upload now exposes a photo/video daily mobile-data limit, stores it in Room schema version `51`, and stops queuing additional uploads on metered automatic-upload runs after the configured daily byte limit is reached while keeping skipped files eligible for the next scan period.
- Android video backup settings now label the Wi-Fi-only control as "Never use mobile data for videos" and explain that turning it off enables metered-network limits and roaming controls.
- Android photo/video backup settings now show a compact overall backup status: enabled state, selected phone-folder count, network/roaming/charging conditions, and last backup scan.
- Android auto-upload now stores per-source folder sync cursors in Room schema version `52`, keeps the legacy `lastSyncTimestamp` as a migration fallback, starts newly added source folders at timestamp `0`, and preserves per-source cursors when a metered run stops at the daily mobile-data limit. `:qsferaDomain:testDebugUnitTest --tests "eu.qsfera.android.domain.automaticuploads.model.FolderBackUpConfigurationTest"`, `:qsferaApp:testOriginalDebugUnitTest --tests "eu.qsfera.android.workers.AutomaticUploadsWorkerTest"`, and `:qsferaData:compileDebugKotlin` passed on 2026-06-10.
- Android photo/video backup status now shows per-source scan state, missing SAF folder access, and selected-account low-storage warnings in the settings summary. `:qsferaApp:testOriginalDebugUnitTest --tests "eu.qsfera.android.presentation.settings.automaticuploads.AutomaticUploadStatusFormatterTest"` and `:qsferaApp:compileOriginalDebugKotlin` passed on 2026-06-10.
- Android photo/video backup status now also observes automatic-upload transfers live and reports uploading, queued, failed, and paused-by-network/roaming/charging states in the settings summary. `:qsferaApp:testOriginalDebugUnitTest --tests "eu.qsfera.android.presentation.settings.automaticuploads.AutomaticUploadStatusFormatterTest"` and `:qsferaApp:compileOriginalDebugKotlin` passed on 2026-06-10.
- Android public-link rows now show the effective access mode, password state, and expiration state directly in the share list, so users can review link safety without reopening each link. `:qsferaApp:testOriginalDebugUnitTest --tests "eu.qsfera.android.presentation.sharing.shares.PublicShareStatusFormatterTest"` and `:qsferaApp:compileOriginalDebugKotlin` passed on 2026-06-10.
- Android now keeps Graph-style `internal` and `blocksDownload` public links as server-managed custom-access states in the OCS public-link editor: list rows show `Custom access`, the edit dialog explains that the link is managed by the server, and saving is disabled to avoid downgrading organization-only or secure-view protection to a standard OCS link.
- Sharing controls were audited against the Google Drive and Yandex Disk benchmarks in `Server/docs/sharing-controls-production-audit.md`, and `scripts/verify-sharing-controls-coverage.ps1` now checks local Android UI, Graph link types, server link handling, and acceptance-feature coverage for password, expiration, access roles, internal links, secure-view/no-download behavior, and role-based resharing.
- Server `qsfera_full` compose files now require explicit admin, Keycloak, and S3/MinIO secrets instead of demo defaults.
- Server `qsfera_full/env.production.example` provides a tracked production-safe starting point with TLS validation enabled and demo users disabled.
- Server `qsfera_full/validate-production-env.sh` checks production `.env` invariants before compose validation: TLS validation enabled, demo users disabled, pinned production image, required domains, required secrets, and no public-production MinIO.
- Added `scripts/validate-server-production-env.ps1` with the same `qsfera_full` production `.env` checks for the local Windows/PowerShell workflow; it validates required domains, release image pinning, disabled demo/insecure modes, required secrets, optional Keycloak/S3 blocks, `COMPOSE_PATH_SEPARATOR`, selected `COMPOSE_FILE` fragments, and backup-friendly config/data path warnings without printing secret values.
- `scripts/validate-server-production-env.ps1` was verified on 2026-06-12: it rejects `Server/devtools/deployments/qsfera_full/env.production.example` with placeholder values, accepts a temporary filled production-shaped env file with `0 warning(s)`, and rejects a temporary env file where `COMPOSE_FILE` omits the required `qsfera.yml` fragment.
- Server now has a Raspberry Pi 5 Docker path matching the Ikar/Argus deployment style: `Server/docker-compose.rpi5.yml`, `Server/.env.example`, and `Server/docs/raspberry-pi-5-docker.md`.
- Server Docker builds now accept explicit production metadata for `QSFERA_VERSION`, `QSFERA_EDITION`, and `QSFERA_BUILD_DATE`, and the Raspberry Pi 5 compose path defaults to pinned image `qsfera-cloud-server:7.0.0-rpi5-stable`.
- Windows/Desktop update channel defaults now match the build channel: stable builds default to `stable`, beta builds default to `beta`.
- Windows/Desktop CI now validates production release invariants before Windows packaging and requires signing variables for stable tag releases.
- Windows/Desktop now has a configured HTTPS update feed at `https://qsfera.kusoft.xyz/desktop/updates/stable.xml`; the feed returned HTTP `200` on 2026-06-08 and contains the XML elements parsed by `Desktop/src/gui/updater/updateinfo.cpp`.
- Windows local autostart was repaired on 2026-06-10 with `scripts/repair-windows-autostart.ps1 -Launch`: the HKCU Run key `QSfera` points to the Craft runtime `bin\qsfera.exe`, required Qt6 runtime DLLs are present there, and `qsfera.exe` started from that path.
- Windows tray "Show" now brings the settings window to the foreground after restoring it to an available screen. The same `Application::showSettings()` path is used by tray menu actions and `qsfera.exe --show`, and Win32 window enumeration on 2026-06-10 reported `Visible=True`, `Foreground=True`, title `КуСфера`.
- Windows Desktop now has repeatable local packaging and feed-publication scripts. `scripts/package-windows-desktop.ps1` produced a smoke-tested `4.0.0` x64 package with Qt/MSVC runtime files; `scripts/publish-windows-desktop.ps1` generated a valid local `stable.xml` feed for that MSI and refuses remote publication unless the MSI is Authenticode-signed.
- Windows Desktop packaging now fails before ZIP/MSI creation if the staged app is missing critical Qt6/MSVC/OpenSSL DLLs, the Qt platform plugin, QSfera VFS plugins, QML import manifests, or `qt.conf` runtime path entries; it also smoke-starts the staged `qsfera.exe --version` from the staged working directory.
- Added `scripts/verify-production-state.ps1` as a repeatable live production endpoint gate for the public server status, Android Argus manifest/download, and Desktop update feed; the Android gate now pins the stable release id, original APK filename, content type, package size, download path, and SHA-256.
- `scripts/verify-production-state.ps1 -VerifyAndroidDownload` passed on 2026-06-10: server `edition=stable`/`productversion=7.0.0`, Android `1.3.12`/`40`, downloaded APK size `12237552`, APK SHA-256 `279d00c6a43072caa0b26269f547fb0dc3268088aae5cd9f4ccfbbb485c35ded`, and valid Desktop no-update feed.
- `scripts/verify-production-state.ps1 -VerifyAndroidDownload` passed on 2026-06-12: server `edition=stable`/`productversion=7.0.0`, Android release id `39072ada-7557-48f3-ab42-ad68703d00b0`, Android `1.3.13`/`41`, APK filename `qsfera-mobile-41.apk`, content type `application/vnd.android.package-archive`, downloaded APK size `12261408`, APK SHA-256 `f065e6584e6a09999e72ba0eeeaae903e93fb8980a0426e4b39b5e2366bf6611`, and valid Desktop no-update feed.
- `scripts/verify-production-state.ps1 -VerifyAndroidDownload` passed on 2026-06-13 against the current public Argus manifest contract: server `edition=stable`/`productversion=7.0.0`, Android release id `39072ada-7557-48f3-ab42-ad68703d00b0`, Android version `1.3.13`, APK filename `qsfera-mobile-41.apk`, content type `application/vnd.android.package-archive`, downloaded APK size `12261408`, APK SHA-256 `f065e6584e6a09999e72ba0eeeaae903e93fb8980a0426e4b39b5e2366bf6611`, and valid Desktop no-update feed. The live manifest currently omits `androidPackageName` and `androidVersionCode`; the verifier treats both as optional unless strict switches are set.
- `scripts/verify-production-state.ps1 -VerifyAndroidDownload` passed on 2026-06-13 after publishing Android `1.3.14`: server `edition=stable`/`productversion=7.0.0`, Android release id `4394333f-a84a-4a3b-9bc7-6a48d054d4fe`, Android version `1.3.14`, APK filename `qsfera-mobile-42.apk`, content type `application/vnd.android.package-archive`, downloaded APK size `12262908`, APK SHA-256 `8ddfbb9d3dbff3f7bc1030024a0f03a7928771b06cb373c8996c96fb22ad5a18`, and valid Desktop no-update feed.
- Added `Server/scripts/verify-rpi-production.sh`, declared a loopback-only Pi metrics port in `Server/docker-compose.rpi5.yml`, and expanded the Raspberry Pi Docker runbook with local/public status checks, container/image checks, Prometheus metrics verification, backup/restore drill steps, and Docker data-root/retention planning.
- Added systemd monitoring units for the Pi: `Server/systemd/qsfera-rpi-production-check.service`, `Server/systemd/qsfera-rpi-production-check.timer`, `Server/systemd/qsfera-rpi-production-check-alert@.service`, `Server/systemd/rpi-production-check.env.example`, plus installer `Server/scripts/install-rpi-production-monitoring.sh` and alert helper `Server/scripts/alert-rpi-production-check-failure.sh`.
- Deployed the Pi metrics/checker update on 2026-06-09. Before upload, `/mnt/data/qsfera/cloud-server/src/docker-compose.rpi5.yml` was backed up to `/mnt/data/qsfera/backups/docker-compose.rpi5.yml-20260609-181614.bak`; `docker compose -f docker-compose.rpi5.yml up -d` recreated and started `qsfera-cloud-server`; `docker compose ps` reported loopback ports `127.0.0.1:9200->9200/tcp` and `127.0.0.1:9205->9205/tcp`.
- `Server/scripts/verify-rpi-production.sh` passed on the Pi on 2026-06-09 with `PUBLIC_STATUS_URL=https://qsfera.kusoft.xyz/status.php` and `REQUIRE_METRICS=true`: image `qsfera-cloud-server:7.0.0-rpi5-stable`, container `running`/`healthy`, Docker root `/mnt/data/docker`, root free space `43.0 GiB`, data free space `851.5 GiB`, local and public status `stable`/`7.0.0`, and Prometheus metrics reachable.
- Deployed the Pi systemd monitoring timer on 2026-06-09. `systemd-analyze verify` for the three installed unit files returned exit `0`; `systemctl is-enabled qsfera-rpi-production-check.timer` returned `enabled`; `systemctl is-active qsfera-rpi-production-check.timer` returned `active`; `systemctl show qsfera-rpi-production-check.service` returned `Result=success`, `ExecMainStatus=0`, `ActiveState=inactive`; and `journalctl -u qsfera-rpi-production-check.service` recorded `QSfera Raspberry Pi production verification passed: 0 warning(s).`
## Raspberry Pi 5 Check
- Pi target checked on 2026-06-08: `192.168.0.185` reports `aarch64`, 64-bit Debian 13, Docker `29.4.3`, Docker Compose `v5.1.3`, and Docker server architecture `aarch64`.
- Before stable-image deployment, the old Pi server was running as local/dev image `qsfera-cloud-server:rpi5-local`; public `/status.php` reported `edition: dev` and `productversion: 7.0.0`.
- The Pi server now runs through `/mnt/data/qsfera/cloud-server/src/docker-compose.rpi5.yml` with pinned image `qsfera-cloud-server:7.0.0-rpi5-stable`, bind-mounted config/data under `/mnt/data/qsfera/cloud-server`, loopback backend port `127.0.0.1:9200`, loopback metrics port `127.0.0.1:9205`, and Docker root `/mnt/data/docker`.
- Before switching the Pi server to compose, config/data were backed up to `/mnt/data/qsfera/backups/cloud-server-before-compose-20260608-200921.tar.gz`. The archive was created with `sudo tar`, had size `213M`, and `tar -tzf` completed successfully.
- Before switching to the stable image, config/data were backed up to `/mnt/data/qsfera/backups/cloud-server-before-stable-image-20260608-201536.tar.gz`. The archive had size `213M`, and `tar -tzf` completed successfully.
- `docker run --rm --entrypoint /usr/bin/qsfera qsfera-cloud-server:7.0.0-rpi5-stable version --skip-services` returned `Version: 7.0.0`, `Edition: stable`, and `Compiled: 2026-06-08 00:00:00 +0000 UTC`.
- After deployment, `docker compose ps` reported `qsfera-cloud-server` as `Up` and `healthy`; `docker inspect` reported `IMAGE=qsfera-cloud-server:7.0.0-rpi5-stable HEALTH=healthy`.
- Local `https://127.0.0.1:9200/status.php` and public `https://qsfera.kusoft.xyz/status.php` both returned `edition: stable` and `productversion: 7.0.0`; the public request returned HTTP `200`.
- After Docker builder cache cleanup and the stable deployment, `/dev/mmcblk0p2` reported `57G` size, `12G` used, `43G` available, `21%` use. `/mnt/data` reported `916G` size, `18G` used, `852G` available, `3%` use.
## Android Argus Publication Check
- Current public Argus manifest for `https://argus.kusoft.xyz/api/apps/qsfera-mobile/manifest?platform=android&channel=stable` reports release id `4394333f-a84a-4a3b-9bc7-6a48d054d4fe`, version `1.3.14`, channel `stable`, platform `android`, package kind `apk`, original file name `qsfera-mobile-42.apk`, content type `application/vnd.android.package-archive`, package size `12262908`, and SHA-256 `8ddfbb9d3dbff3f7bc1030024a0f03a7928771b06cb373c8996c96fb22ad5a18`. It does not currently include legacy Android-specific root `androidPackageName` or release `androidVersionCode` fields.
- Independent download verification of `https://argus.kusoft.xyz/api/apps/qsfera-mobile/download/latest?platform=android&channel=stable` produced a `12262908` byte APK with the same SHA-256 `8ddfbb9d3dbff3f7bc1030024a0f03a7928771b06cb373c8996c96fb22ad5a18`.
- The previously published `1.3.5`/`33` APK matched Argus SHA-256 `b7d5bdcb49527c00983a15849252fa42189dee4287754803c46cf2f32e2130f3`, but `apksigner` reported `CN=Android Debug` with certificate SHA-256 `b86d745cecb01ca24bda03092fe94ca7801678513b97edd1175bcabfdea4e941`.
- The current production-signed `1.3.14`/`42` APK is signed by `CN=QSfera Argus Upload, O=QSfera, C=RU` with certificate SHA-256 `ea5c304c78ee333f6586f54c933d986c1f865da9c4e3595f8fe0df274c1ab877`.
- `AppUpdateManager` follows the public read-only Argus manifest contract from `G:/Repos/Marketplace/ARGUS_PUBLICATION.md`: HTTP `404` is treated as no update, `release.version` is compared with the installed version, downloads use `ARGUS_BASE_URL + release.downloadPath`, `release.packageSizeBytes` and `release.sha256` are verified before install, and the downloaded APK package name/version/signature are checked against the installed app. Because of that local code path, devices that installed debug-signed `1.3.5` should be treated as needing reinstall or an explicit migration plan before they can move to the current production-signed stable APK.
- The rollout policy for debug-signed `1.3.5` devices is documented in `Android/docs/argus-rollout-policy.md`: the app does not bypass signer mismatch; users must back up unsynced local files, uninstall the debug-signed build, then install the current stable APK from Argus. The Android app now shows a reinstall-required dialog for this certificate-mismatch path.
## Remaining P0
- Back up the gitignored Android release signing files created under `Android/signing/` and `Android/argus-signing.local.properties` in a secure external secret store. Without this key material, future in-place Android updates from the production-signed `1.3.14` cannot be signed with the same certificate.
- Provide CI/Argus signing secrets derived from the same release key: `QSFERA_RELEASE_KEYSTORE`, `QSFERA_RELEASE_KEYSTORE_PASSWORD`, `QSFERA_RELEASE_KEY_ALIAS`, and `QSFERA_RELEASE_KEY_PASSWORD`.
- Fill production domains, ACME email, admin passwords, Keycloak passwords, and storage secrets from a secret manager. Do not commit real values.
- Run `./validate-production-env.sh .env` or `.\scripts\validate-server-production-env.ps1 -EnvPath ...` and then `docker compose config` for `Server/devtools/deployments/qsfera_full` with a filled production `.env`; Docker CLI is not available in this local environment, so compose rendering is not locally verified.
- Provide real Windows signing secrets in CI: `QSFERA_DESKTOP_SIGN_PACKAGE=True` and `QSFERA_DESKTOP_CODESIGN_CERTIFICATE` with the Craft-compatible signing certificate reference. The current local MSI is intentionally not published as a production update because `Get-AuthenticodeSignature` reports `NotSigned` and `Desktop/scripts/validate-production-release.ps1 -RequireSigning` fails until signing is configured.
- Publish the first signed Windows MSI with `scripts/publish-windows-desktop.ps1 -RemoteHost ...` and update `https://qsfera.kusoft.xyz/desktop/updates/stable.xml` from the current no-update XML to a concrete `<version>`, `<versionstring>`, and HTTPS `.msi` `<downloadurl>`.
## Remaining P1
- Configure `QSFERA_ALERT_WEBHOOK_URL` in `/etc/qsfera/rpi-production-check.env` if off-host alert delivery is required; the installed failure service currently logs through `systemd-cat` and journal without an external endpoint.