docs: record verified native preview and unfinished PWA parity
Android validation / emulator (35) (push) Failing after 4s
Android validation / emulator (37.0) (push) Failing after 6s
Android validation / build (push) Successful in 5m27s
iOS validation / simulator (push) Canceled after 23m26s

Replace current wrapper claims with native architecture/security/build evidence, preserve historical documents, track all thirty pages and explicit release gates, correct brand attribution and provide the twelve-section delivery report without claiming completion. Native signing remains paused and Apple application/device validation outstanding.
This commit is contained in:
Yuvomi Mobile Contributors committed 2026-10-21 09:13:36 +02:00
1 parent 1433e1670a
commit 40a7962307
20 files changed
+813 -277

No files matched your search

+5
View File
@@ -2,6 +2,11 @@
## [Unreleased] ## [Unreleased]
- Begin the genuine native rebuild: Compose/SwiftUI screens, direct HTTPS API,
protected native session storage and initial auth/dashboard/task/note/shopping
flows. Archive the rejected WebView renderer and its tests. Full PWA parity is
explicitly incomplete; see `docs/native-parity.md`.
- Gitea workflows for the maintainer's Ubuntu pool and native M4 Pro runner, - Gitea workflows for the maintainer's Ubuntu pool and native M4 Pro runner,
explicit SDK/KVM prerequisites, SHA-256 checkout and unsigned-only CI release builds. explicit SDK/KVM prerequisites, SHA-256 checkout and unsigned-only CI release builds.
+3 -1
View File
@@ -11,7 +11,9 @@ Add regression tests with security fixes, and demonstrate that the test fails
without the fix. Do not commit build outputs, credentials, private signing keys, without the fix. Do not commit build outputs, credentials, private signing keys,
local servers, generated fixture certificates, or production household data. local servers, generated fixture certificates, or production household data.
Keep platform integrations native and dependencies minimal. The server owns Keep all application screens native (Compose/SwiftUI), not just platform
integrations. No WebView fallback for unfinished modules; use the parity tracker
and never count archived browser tests as native evidence. Keep dependencies minimal. The server owns
business logic, authentication, CSRF, and authorization. Never solve compatibility business logic, authentication, CSRF, and authorization. Never solve compatibility
by disabling TLS, accepting every origin, or copying a browser's session cookies. by disabling TLS, accepting every origin, or copying a browser's session cookies.
Upstream prohibits frontend frameworks, bundlers, and transpilers in its own Upstream prohibits frontend frameworks, bundlers, and transpilers in its own
+14 -8
View File
@@ -1,20 +1,26 @@
# Yuvomi Mobile # Yuvomi Mobile
Independent, community-maintained Android (Kotlin/WebView) and iOS Independent native Android (Kotlin/Jetpack Compose) and iOS (SwiftUI) clients for
(SwiftUI/WKWebView) companions for user-owned [Yuvomi](https://github.com/ulsklyc/yuvomi) user-owned [Yuvomi](https://github.com/ulsklyc/yuvomi) servers. Not affiliated with
servers. Not affiliated with or endorsed by upstream. or endorsed by upstream.
**Development build - not production-ready.** See [validation status](docs/validation.md). **Native rebuild in progress — not full PWA parity or production-ready.**
The previous WebView wrapper did not meet the requested native implementation.
It is archived under `legacy/` and is not the shipped application surface.
Read the [feature-by-feature parity tracker](docs/native-parity.md) and
[current validation](docs/validation.md) before installing a preview.
No mobile backend, advertisements, telemetry, tracking, subscriptions, or mandatory cloud. No mobile backend, advertisements, telemetry, tracking, subscriptions, or mandatory cloud.
The configured server supplies the application and retains responsibility for its own The apps render native controls and call the existing server API. The server owns
integrations, version checks, security and privacy. authorization, recurrence, synchronization and household business rules.
Use a root-hosted HTTPS instance with a valid certificate, including private servers Use a root-hosted HTTPS instance with a valid certificate, including private servers
accessible through your VPN. Custom ports are supported. HTTP and subpath deployments accessible through your VPN. Custom ports are supported. HTTP and subpath deployments
are intentionally rejected. Enter the server on first launch; use Server settings to are intentionally rejected. Enter the server on first launch; use **Mehr** to
reconnect or erase local browsing data and switch installations. remove the native session and switch installations. The native client does not
import a browser/WebView session; sign in again after upgrading the prototype.
- [Architecture](docs/architecture.md) - [Architecture](docs/architecture.md)
- [Native preview delivery report](docs/native-delivery-report.md)
- [Android build](docs/build-android.md) / [iOS build](docs/build-ios.md) - [Android build](docs/build-android.md) / [iOS build](docs/build-ios.md)
- [Compatibility and authentication](docs/server-compatibility.md) - [Compatibility and authentication](docs/server-compatibility.md)
- [Release process](docs/release-process.md) - [Release process](docs/release-process.md)
+53 -1
View File
@@ -1,4 +1,56 @@
# Security policy # Security policy — native rebuild
**The current native client is an incomplete development preview, not an audited
release or full PWA replacement.** See [native parity](docs/native-parity.md).
Report vulnerabilities privately to the repository owner through an established
private channel. No reporting mailbox has been provided; do not invent one or put
cookies, keys, household records or exploit details into public issues. Upstream
server vulnerabilities belong in its private reporting channel.
## Current native security model
- Native Compose/SwiftUI screens call one configured HTTPS origin. System TLS
validation is mandatory and API redirects are refused. No WebView/WKWebView is
shipped as the app surface or used as a missing-feature fallback.
- Passwords and second-factor codes exist transiently for the API request; they
are not saved to preferences, keychains, logs, artifacts or application state
restoration. Server cookies, not passwords, persist the session.
- Android persists allow-listed Secure cookies with their absolute expiry in
AES-GCM ciphertext backed by AndroidKeyStore, with the origin authenticated as
additional data. iOS uses a private ephemeral URLSession jar and origin-scoped
Keychain items protected with WhenUnlockedThisDeviceOnly.
- CSRF tokens follow the upstream API protocol. Only an explicit `csrf_invalid`
rejection allows one refresh/retry. Other permission errors or ambiguous network
failures never cause automatic mutation replay. The server remains authoritative
for authorization, recurrence, reward accounting and cross-module rules.
- Reads are bounded and timed out. There is no offline mutation queue or native
remote push implementation. Missing features are documented, not simulated.
- Logout attempts remote revocation and clears the native local session. Local
server removal is not remote revocation. Old prototype WebKit data is not
imported; use OS app-data removal/reinstall to erase that legacy installation.
- Android screenshots are blocked. The iOS view is obscured while inactive. The
Android integration test temporarily permits a screenshot only of its synthetic
private fixture, restores the restriction and never uses real household data.
- The owner-authorized initial signing key is backed up outside the repository.
Four repository signing secrets exist, but signing/release changes are parked
during the native rebuild. No private key belongs in Git or downloadable CI
artifacts. Protect trusted-branch write access and isolate persistent runners.
## Mandatory gates
Native transports, secure storage, session erasure, actual Apple SDK compilation,
real-device behavior, accessibility, supported authentication flows and dependency
resolution need review and evidence before release. OIDC, file transfers, offline
storage and the remaining modules are not implemented native capabilities yet.
Advisory scans and parser/unit checks are not a guarantee that code is secure.
The superseded prototype policy is retained below as historical context only;
it must not be used to claim capabilities of the native rebuild.
---
## Historical WebView prototype policy
**Development software: no production-readiness claim.** Only the current **Development software: no production-readiness claim.** Only the current
development branch is maintained; no stable mobile release exists yet. development branch is maintained; no stable mobile release exists yet.
+4 -1
View File
@@ -2,7 +2,10 @@
Yuvomi Mobile is an independent community contribution, not an official Yuvomi Yuvomi Mobile is an independent community contribution, not an official Yuvomi
product. Yuvomi's name identifies the server with which it interoperates. product. Yuvomi's name identifies the server with which it interoperates.
Visual color values follow upstream DESIGN.md. No upstream logo is redistributed. Native design-token mappings follow upstream DESIGN.md/styles. The native
three-circle brand mark is redrawn from upstream's published brand geometry and
colors (`public/utils/brand-mark.js` and `docs/logo.svg`); its MIT attribution is
preserved below. No affiliation or trademark ownership is implied.
Upstream Yuvomi: https://github.com/ulsklyc/yuvomi Upstream Yuvomi: https://github.com/ulsklyc/yuvomi
+43 -32
View File
@@ -1,41 +1,52 @@
# Architecture # Native client architecture
Two independent native applications share a policy, not a cross-platform runtime: The requested product is a native reconstruction of Yuvomi's PWA, not a native
window around that website. Both shipping entry points now render native views:
| Responsibility | Android | iOS | | Responsibility | Android | iOS |
| --- | --- | --- | | --- | --- | --- |
| Native entry/configuration | ComponentActivity and platform widgets | SwiftUI | | Screens and navigation | Jetpack Compose / Material 3 primitives, Yuvomi tokens | SwiftUI, Yuvomi tokens |
| Web application | Android WebView | WKWebView | | Application state | AndroidViewModel + Compose state | MainActor ObservableObject |
| Server URL policy | ServerAddress | ServerAddress | | JSON API | HttpsURLConnection, serialized worker | URLSession actor, bounded streaming delegate |
| Reachability/metadata | HttpsURLConnection, no redirects/cookies | Ephemeral URLSession, no redirects/cookies | | Session ownership | Explicit origin-bound secure-cookie jar | Private ephemeral URLSession cookie storage |
| Authentication | Upstream web UI and OS cookie jar | Upstream web UI and WKWebsiteDataStore | | Persistent cookies | AES-GCM, AndroidKeyStore key, origin as authenticated data | Keychain, WhenUnlockedThisDeviceOnly, origin-scoped account |
| OIDC | Separate restricted web view after consent | Separate restricted web view after consent | | Server logic | Existing unmodified Yuvomi API | Existing unmodified Yuvomi API |
| Downloads | Origin-bound streaming / bounded blob capture and pull | WKDownload to temporary protected file, system export |
The client never reimplements a Yuvomi module or introduces a backend. The user There is no WebView/WKWebView in either shipped source tree and no browser
selects the origin. Bootstrap calls check the actual upstream unauthenticated fallback for missing modules. The old implementation and browser tests are
metadata contract before loading the server application. Configuration stores only archived in `legacy/`; their results are historical, not evidence for this client.
the origin and a query/fragment-free last path; auth/reset/invite routes are not
restored. No native password or API-token storage exists.
The current web view remains alive across background/resume and ordinary rotation. The first native slice contains HTTPS onboarding, setup/password/TOTP login,
Android handles orientation changes in the existing activity; process recreation session restoration, a basic dashboard, task/note/shopping interactions, read-only
loads the remembered path and retains OS cookies, not unsaved web form memory. permission handling and local sign-out/server removal. Exact feature and visual
iOS retains a StateObject-owned view; cold launch restores path and persistent parity is tracked in [native parity](native-parity.md), not inferred from a list of
cookies. No resume reload is imposed. Native recovery offers Retry, Keep viewing, navigation destinations. The other modules remain unfinished.
and server management without hiding the route back to configuration.
Server erasure destroys the browsing context and clears all website storage before ## Transport and state boundaries
another server is loaded. The local wipe does not invalidate the remote session;
logout is still a server-side operation. Web application history remains native
web history. External schemes, universal links and app links are not registered
because arbitrary self-hosted domains cannot be verified by this app in advance.
The optional integration build has a different Android package ID and adds trust - One configured root HTTPS origin/port; system certificate validation only.
for a fresh test CA generated outside the repository. It uses the same production - API calls never follow redirects. No cookie or Authorization state is replayed
code and the unmodified pinned upstream server. Neither normal debug nor release to another host. Authentication proxies returning HTML are not treated as APIs.
resources include that CA. Never distribute an integration APK. - JSON reads have byte limits and timeouts. Writes are not queued for offline
replay. Only a server-declared `csrf_invalid` rejection permits one token refresh
and retry; ordinary authorization failures and transport failures do not.
- Server session cookies are persisted, not passwords or TOTP/recovery codes.
Session-only cookies are not converted to long-lived cookies.
- Native UI mutations wait for the server result. Permissions remain enforced
server-side; sparse module/widget permissions also control native affordances.
- Local removal clears the native cookie vault and in-memory records. It is not
remote revocation. Logout attempts server revocation and still erases the local
session if the server cannot be reached.
- A prior WebView prototype installation may retain its old OS website data.
This data is never imported by the native client. Clear app data/reinstall the
prototype before reviewing this native preview if complete legacy erasure is
required; no migration of private household records is performed.
Read-only offline functionality belongs to upstream's service worker. The app ## Explicit gaps
preserves it where the platform engine supports it, but does not promise offline
editing, synchronization, or the same PWA cache behavior on all OS versions. No native OIDC handoff has been implemented. A browser sign-in is not represented
as a successful native API session. Password reset/invitation flows, the remaining
modules, configurable dashboard, uploads/downloads, notification integrations,
offline cache, localization parity and visual baseline comparison remain open.
The signing setup was paused by the maintainer's native-rebuild correction; it
must not be used to label this incomplete client as a finished release.
+21 -3
View File
@@ -2,8 +2,8 @@
Requirements: JDK 17, Android SDK platform 37.0, build-tools 36.0.0, current Requirements: JDK 17, Android SDK platform 37.0, build-tools 36.0.0, current
platform-tools, and accepted Android SDK licenses. Minimum Android 8 (API 26). platform-tools, and accepted Android SDK licenses. Minimum Android 8 (API 26).
Use a maintained Android System WebView. Target API 37. On Android 17, choose The application surface is Jetpack Compose, not Android System WebView. Target
"Allow private-network servers" when using a LAN instance; public HTTPS servers API 37. On Android 17, choose "Zugriff auf private Netzwerke erlauben" for a LAN instance; public HTTPS servers
do not need this optional runtime permission. No discovery/scanning is performed. do not need this optional runtime permission. No discovery/scanning is performed.
```sh ```sh
@@ -13,7 +13,7 @@ bash android/gradlew -p android connectedDebugAndroidTest
``` ```
Gradle 9.6.0 is checksum-verified by the wrapper. AGP 9.4.1 uses built-in Kotlin Gradle 9.6.0 is checksum-verified by the wrapper. AGP 9.4.1 uses built-in Kotlin
with the build-cache fix in KGP 2.4.21. Maven versions are locked. No dynamic with KGP and Compose compiler plugin 2.4.21. Maven versions are locked. No dynamic
application dependencies or private Maven repository is required. application dependencies or private Maven repository is required.
Ordinary outputs: Ordinary outputs:
@@ -23,6 +23,14 @@ Ordinary outputs:
- `android/app/build/outputs/bundle/release/app-release.aab` (unsigned unless signed later) - `android/app/build/outputs/bundle/release/app-release.aab` (unsigned unless signed later)
- `android/app/build/reports/` (lint, JVM and emulator results) - `android/app/build/reports/` (lint, JVM and emulator results)
The Gitea build job uploads `android-native-debug-apk`, containing an installable
development-signed `app-debug.apk`. It is a native preview, not a signed stable
release or a claim of full PWA parity. See `native-parity.md` before testing it.
Native UI tests live in `src/androidTestNative/java` and the real-server native
scenario in `src/androidTestNativeIntegration/java`. The old browser tests and
renderer are archived under `legacy/` and are not compiled into the app/suites.
Immediately after building, collect and inspect the outputs: Immediately after building, collect and inspect the outputs:
```sh ```sh
@@ -60,6 +68,16 @@ Requires Node 22.14+ and a running emulator. The harness creates and removes its
own CA/key/database, uses `adb reverse`, and builds `.integration` with explicit own CA/key/database, uses `adb reverse`, and builds `.integration` with explicit
test trust. It does not weaken production TLS. Test credentials are ephemeral. test trust. It does not weaken production TLS. Test credentials are ephemeral.
Keep test reports private if you customize the harness with real data (not advised). Keep test reports private if you customize the harness with real data (not advised).
The native test calls the real API through native controls and saves a synthetic
dashboard screenshot under `app/build/outputs/native-screenshots/`. It does not
demonstrate native SSO merely because the protocol fixture also tests OIDC.
The test writes through `PlatformTestStorageRegistry`; AGP collects the file in
`outputs/connected_android_test_additional_output/integrationAndroidTest/` before
uninstalling the fixture. The harness rejects missing/ambiguous output and removes
only its known screenshot from previous runs. Do not extract app-private files
after Gradle returns: UTP has already uninstalled the `.integration` package.
Capture uses the native window and asserts nonuniform pixels; a valid PNG header
alone is not sufficient screenshot evidence.
For dependency updates run `:app:dependencies --write-locks`, the full pipeline, For dependency updates run `:app:dependencies --write-locks`, the full pipeline,
and `python3 scripts/check-dependencies.py`. Review lockfile changes. The version and `python3 scripts/check-dependencies.py`. Review lockfile changes. The version
+8 -4
View File
@@ -1,8 +1,8 @@
# iOS development # iOS development
Requires macOS, Xcode 16.4 or newer with an iOS simulator, and XcodeGen 2.44.1. Requires macOS, Xcode 16.4 or newer with an iOS simulator, and XcodeGen 2.44.1.
Minimum iOS/iPadOS 18.4: the auth window must be able to deny native file-panel Minimum iOS/iPadOS 18.4 is retained for this rebuild. The application surface is
requests, which earlier WKWebView delegates cannot do. No CocoaPods or external SwiftUI; the old WKWebView renderer is archived, not shipped. No CocoaPods or external
runtime package is required. For App Store submission use Apple's currently runtime package is required. For App Store submission use Apple's currently
required Xcode/iOS SDK, not merely the oldest compiler that can build the source. required Xcode/iOS SDK, not merely the oldest compiler that can build the source.
@@ -29,11 +29,15 @@ swift test --package-path ios
swiftc -frontend -parse ios/Sources/*.swift ios/Tests/*.swift ios/UITests/*.swift swiftc -frontend -parse ios/Sources/*.swift ios/Tests/*.swift ios/UITests/*.swift
``` ```
These portable tests compile the **production URL/metadata policies only**. These portable tests compile the **production Foundation API transport, contracts
Parsing does not typecheck UIKit, SwiftUI or WebKit APIs. Neither command is and URL/metadata policies**. Parsing does not typecheck SwiftUI or Security APIs. Neither command is
evidence that the iOS application builds. Actual Xcode testing is mandatory. evidence that the iOS application builds. Actual Xcode testing is mandatory.
For a device build select your authorized development team in Xcode, use your own For a device build select your authorized development team in Xcode, use your own
registered bundle identifier, and let Xcode manage development provisioning. registered bundle identifier, and let Xcode manage development provisioning.
Never commit generated profiles or certificates. See `release-process.md` for Never commit generated profiles or certificates. See `release-process.md` for
distribution. A Linux environment cannot produce a validated iOS archive. distribution. A Linux environment cannot produce a validated iOS archive.
The Mac runner now bootstraps checksum-pinned native Node 22 before checkout,
because its JavaScript actions cannot start without Node in the service PATH.
This does not install/choose Xcode, accept its license or create a simulator.
+6
View File
@@ -42,6 +42,12 @@ Xcode 16.4+ (or a current release supporting this project), accept its license a
complete first-launch setup. Install an iOS 18.4+ simulator runtime and create an complete first-launch setup. Install an iOS 18.4+ simulator runtime and create an
available iPhone simulator; `select-simulator.py` chooses the newest eligible one. available iPhone simulator; `select-simulator.py` chooses the newest eligible one.
Before the first JavaScript action, an inline shell bootstrap installs verified
native Node 22.22.1 into a versioned runner tool cache and exports its bin directory
through `GITHUB_PATH`. It needs no Homebrew/root access and remains available to
action post-cleanup. This addresses run #4's `Cannot find: node in PATH` failure;
the modified workflow itself still needs a real Mac run.
XcodeGen's pinned, checksum-verified binary includes native arm64 and x86_64 XcodeGen's pinned, checksum-verified binary includes native arm64 and x86_64
slices; Rosetta/Homebrew installation is not required by the workflow. Build, slices; Rosetta/Homebrew installation is not required by the workflow. Build,
archive and Swift package scratch outputs use `RUNNER_TEMP`. Scratch directories archive and Swift package scratch outputs use `RUNNER_TEMP`. Scratch directories
+52 -1
View File
@@ -1,4 +1,55 @@
# Maintainer continuation # Native rebuild continuation
## Current objective — do not restore the wrapper
The maintainer rejected the browser-wrapper architecture and requested the PWA's
appearance/functions implemented as genuine native screens. Shipping Android now
uses Compose and iOS SwiftUI, calling the existing API directly. `legacy/` holds
the superseded renderers/tests for reference only. No WebView fallback, raw-JSON
module placeholder or old browser-test pass may be represented as native parity.
Read [native parity](native-parity.md), [architecture](architecture.md), and
[current validation](validation.md) first. Initial auth/dashboard/tasks/notes/
shopping slices exist, but **no module has accepted full visual/functional parity**.
The remaining pages/settings/auth/file/offline/notification work is explicit.
The owner-authorized signing key and four Gitea secrets were created before this
correction. Their private backup is `/home/neste/yuvomi-signing/initial-20261021`.
Do not print its contents. Uncommitted signing changes are preserved in Git stash
`wip: owner-authorized signing setup paused for true native rewrite`; do not apply
them blindly or resume signing/publishing the old wrapper.
Current local native outputs/logs:
- `/home/neste/yuvomi-native-build` (use `YUVOMI_BUILD_DIR` on WSL).
- `/home/neste/yuvomi-native-validation.log` and native integration logs.
- `/home/neste/yuvomi-native-swift` and `/home/neste/yuvomi-native-swift.log`.
- The existing persistent SDK, Swift, ktlint, actionlint and unmodified upstream
paths listed below remain usable. Set `ANDROID_SERIAL=emulator-5554`; do not
authorize or deploy to unrelated devices visible in `adb devices`.
Run native JVM/Compose UI tests, the real upstream fixture, portable Swift API/
contract tests, formatting, lint and OSV after changes. Apple UI/Security.framework
is not typechecked by Linux. Gitea's Mac job now bootstraps pinned Node before
checkout, but actual Xcode results still need inspection. Ubuntu emulator jobs
continue requiring accessible KVM; do not weaken or fake that evidence.
Latest local native evidence: 10 JVM tests, 6 API-35 Compose tests, 7 real-server
instrumentation tests (including those same 6), both lint variants and native
debug/unsigned release APK/AAB builds pass. Sixteen portable Swift tests and strict
format/parsing pass. Screenshot export now uses AndroidX test storage/AGP output
collection before uninstall; the PNG was opened and confirmed nonblank. Five new
output-regression tests guard missing, invalid, ambiguous and stale screenshots.
See `validation.md` for logs and limitations. No Apple SDK result is implied.
The next Mac/Ubuntu workflow execution still needs inspection after pushing.
Rebuild/collect from a clean checkout; do not present cached wrapper artifacts as
the native preview. Keep the signing stash parked.
Next work: stabilize/visually compare this slice, close auth/advanced CRUD gaps,
then implement the remaining real native modules against the parity inventory.
## Historical environment and original delivery context
This is a development delivery, not authorization to publish. Preserve the Git This is a development delivery, not authorization to publish. Preserve the Git
history on `feat/yuvomi-mobile`; use Conventional Commits and normal pushes only. history on `feat/yuvomi-mobile`; use Conventional Commits and normal pushes only.
+41
View File
@@ -0,0 +1,41 @@
# Historical architecture — superseded WebView prototype
Two independent native applications share a policy, not a cross-platform runtime:
| Responsibility | Android | iOS |
| --- | --- | --- |
| Native entry/configuration | ComponentActivity and platform widgets | SwiftUI |
| Web application | Android WebView | WKWebView |
| Server URL policy | ServerAddress | ServerAddress |
| Reachability/metadata | HttpsURLConnection, no redirects/cookies | Ephemeral URLSession, no redirects/cookies |
| Authentication | Upstream web UI and OS cookie jar | Upstream web UI and WKWebsiteDataStore |
| OIDC | Separate restricted web view after consent | Separate restricted web view after consent |
| Downloads | Origin-bound streaming / bounded blob capture and pull | WKDownload to temporary protected file, system export |
The client never reimplements a Yuvomi module or introduces a backend. The user
selects the origin. Bootstrap calls check the actual upstream unauthenticated
metadata contract before loading the server application. Configuration stores only
the origin and a query/fragment-free last path; auth/reset/invite routes are not
restored. No native password or API-token storage exists.
The current web view remains alive across background/resume and ordinary rotation.
Android handles orientation changes in the existing activity; process recreation
loads the remembered path and retains OS cookies, not unsaved web form memory.
iOS retains a StateObject-owned view; cold launch restores path and persistent
cookies. No resume reload is imposed. Native recovery offers Retry, Keep viewing,
and server management without hiding the route back to configuration.
Server erasure destroys the browsing context and clears all website storage before
another server is loaded. The local wipe does not invalidate the remote session;
logout is still a server-side operation. Web application history remains native
web history. External schemes, universal links and app links are not registered
because arbitrary self-hosted domains cannot be verified by this app in advance.
The optional integration build has a different Android package ID and adds trust
for a fresh test CA generated outside the repository. It uses the same production
code and the unmodified pinned upstream server. Neither normal debug nor release
resources include that CA. Never distribute an integration APK.
Read-only offline functionality belongs to upstream's service worker. The app
preserves it where the platform engine supports it, but does not promise offline
editing, synchronization, or the same PWA cache behavior on all OS versions.
+70
View File
@@ -0,0 +1,70 @@
# Historical server compatibility — superseded WebView prototype
Source reference: upstream 2.78.0 revision
`10bfef7686ed7303b4c74c7fcc55a5eb16356c9d`. No general version range has been
certified. Anonymous bootstrap metadata deliberately omits the exact version;
native probing verifies shape, not an invented minimum-version comparison.
Use `https://family.example.com` or `https://family.example.com:8443`, backed by a
root-hosted Yuvomi installation and a valid system-trusted certificate. Operators
should set `SESSION_SECURE=true`, correct `BASE_URL`, and correct proxy trust.
Private servers are supported through reachable HTTPS/VPN, not TLS bypasses.
International hostnames should be entered in ASCII/punycode for both platforms.
Root-relative assets, API endpoints, cookie paths, callback redirects and worker
registration mean `/yuvomi/` subpaths are not supported by the inspected source.
Use a dedicated hostname. Reverse proxies must pass `/api/v1/*`, cookies, HTML,
JavaScript and the service worker consistently. An external authentication proxy
that intercepts bootstrap calls is rejected with a connection error; it is not
silently identified as a Yuvomi server.
## Authentication
Password login, TOTP/recovery codes, password reset, invitations, server-side
session expiry/revocation and logout remain upstream web flows. No native code
stores the password, supplies a CSRF bypass, or extends an expired session.
Backgrounding does not clear cookies. Native server erasure does not revoke a
remote session; use Yuvomi logout first or revoke sessions server-side.
### OIDC: local fixture verified; real-provider validation pending
The initiating Yuvomi session contains OIDC state, nonce and PKCE verifier. When
the server redirects to an identity provider, native consent offers an isolated
web sign-in window sharing the OS website cookie store. HTTPS-only redirects
can return to the server callback; the final same-origin page returns to the main
view. No app-capability handlers or JS bridge are installed in that auth window.
Some providers (including configurations that prohibit embedded user agents)
require a system browser. **Those cannot sign the native view in with upstream's
current protocol.** Use Server settings -> Open server in browser, and sign in
there for an independent browser session. Opening only the external authorization
link in a browser does not transfer the initiating cookie. Browser cookies are
never copied into the app, and custom-scheme callbacks are not fabricated.
The API 35 integration suite completes a native isolated-window round trip with
a local test IdP, real RS256 ID tokens, nonce and S256 PKCE. No external
IdP/developer account was supplied. Validate the actual provider, including
cross-site cookies, cancellation, errors, TOTP and account linking, before relying
on that deployment. iOS authentication has not run against Apple SDKs. A native
browser handoff would require an independently reviewed upstream protocol; no
such upstream change is included or published.
Android generated exports require a current WebView supporting document-start
scripts and are capped at 25 MB. A bounded Blob registry preserves a generated
file even when upstream immediately revokes its object URL; it does not fetch
blob URLs or alter the server's CSP. HTTPS file downloads are capped at 100 MB.
Update the system WebView if generated exports are unavailable on an old engine.
## Notifications and offline
Upstream implements Web Push and optional ntfy/Gotify/webhook/email channels.
Embedded web views do not supply a portable native push subscription. This app
has **no native remote-push claim**, no Firebase/APNs secrets, no background
polling service, and no fake notification-registration UI. Configure an existing
optional upstream ntfy/Gotify channel and its client if desired. No centralized
service is required by this application.
Upstream service-worker caching is read-only, allow-listed, and versioned.
Mutations/authentication remain online. Platform-specific cache/resume behavior
and offline account switching still need device validation. Do not rely on offline
editing, synchronization, or closed-app push in this native development build.
+47
View File
@@ -0,0 +1,47 @@
# Historical security review — superseded WebView prototype
This is an engineering review, not an independent penetration test or certification.
Addressed during implementation:
- Canonical, HTTPS-only origin/port comparison; credential, fragment, query,
subpath, scheme, lookalike-host and TLS-downgrade regression cases.
- A main-frame POST can bypass Android's usual navigation callback. The request
interception gate now rejects **all** cross-origin requests, not only subresources.
The emulator regression was observed failing when the main-frame guard was
deliberately removed, then rerun with the fix restored.
- TLS failures cancel by default. Native download redirects are refused before
cookies can be replayed to another origin. Bootstrap requests use neither
cookies nor automatic redirects and have bounded metadata reads/timeouts.
- Files use explicit system selection/content URIs; no filesystem or storage
permission is granted to web content. Blob exports have no native JS bridge.
- Complete Android browsing-data deletion uses WebStorageCompat, with OS app
reset for engines without that capability. WebStorage.deleteAllData alone was
insufficient for service-worker CacheStorage/IndexedDB and is not relied upon.
- iOS server erase replaces the WKWebView, not only its URL, eliminating old
history/back-forward contexts as well as persistent website data.
- iOS metadata uses typed decoding, rejecting numeric values masquerading as booleans.
- Native picker results are bound to the browsing generation/request. Android
rejects stale upload/download selections, revokes camera URI grants, and clears
capture cache on erasure. iOS ignores export-cancellation callbacks from an old
download. Apple-specific callback regressions have not run without Xcode.
- KGP was upgraded after OSV identified CVE-2026-53914 in the original build tool.
The final OSV scan found no listed advisories across 213 locked/direct Maven
coordinates and build plugins. Rescan after resolution/lock updates.
- The inspected release APK is not debuggable, rejects cleartext, disables backup
and trusts system CAs only. AndroidX's exported profile installer receiver is
protected by the system DUMP permission; app providers are not exported.
Release gates still requiring evidence:
- Actual Xcode typecheck, simulator, archive, physical-device and accessibility review.
- End-to-end IdP flows, especially providers that prohibit embedded agents; no
browser-to-native session transfer is supported by the current upstream protocol.
- iOS unknown-length download disk-pressure behavior; a post-download size check
is not a streaming quota. Verify file picker/camera cancellation on real devices.
- Android file-picker/camera/rotation while a picker is outstanding; cancellation
on process death intentionally loses the selection, never silently retries it.
- Offline sign-out/account switching, stale-worker updates and server erasure on
current WebView/WKWebView versions. Upstream tests are not native device proof.
- Reverify final signed release manifests, signing, store ownership, private vulnerability contact
and applicable privacy disclosures. No claim that all vulnerabilities are absent.
+146
View File
@@ -0,0 +1,146 @@
# Historical validation — superseded WebView prototype
These results describe the old wrapper, not the current native implementation.
See [current native validation](validation.md).
**Partially verified development software, not production-ready.**
Local validation date: 2026-10-21. No unexecuted iOS application test, hosted CI
run, production signing, or store publication is represented as completed.
## Executed evidence
- Android debug APK and unsigned release APK/AAB built successfully; both
`lintDebug` and `lintRelease` passed. JVM security/picker-policy tests: **7 passed,
zero failures/skips**.
- Android API 35 regular emulator suite: **6 passed, zero failures/skips**.
This covers onboarding, invalid URLs, background/resume, hardened settings,
cross-origin POST/subresource isolation, opaque-page Blob rejection and activity
recreation. Its permission test checks that the API 37 permission UI is absent;
it is **not** evidence of API 37 permission grant/denial behavior.
- Unmodified pinned upstream ran behind a temporary HTTPS reverse proxy.
Bootstrap metadata, password login, Secure/HttpOnly/SameSite cookies, persistent
session, CSRF rejection, logout/revocation passed in the harness.
- Android's extended real-server scenario exercises login, HttpOnly cookies,
document upload via upstream fetch, native FileTransfer output, bounded Blob
export, background/resume, recreation, CSRF-protected logout, revocation,
recovery, isolated OIDC return and back navigation. It does not automate the OS
camera/document destination picker or prove every upstream module works.
Two consecutive API 35 runs have **7 passed, zero failures/skips each**, including the
six regular security/onboarding cases and this real-server scenario.
- The cross-origin POST regression was deliberately observed failing without its
guard. The production guard was restored and the API 35 regression passes.
- Portable Swift production-policy tests: 7 passed (URL and typed metadata).
All Swift sources passed format lint and parsing with Swift 6.2.1 on Linux.
The portable package excludes BrowserModelTests. This does not build iOS.
- Selected upstream suites ran successfully: cookie-secure-flag (4),
session-sliding (8), two-factor (22), password-reset (24), sw-api-cache (42).
Upstream's plain OIDC script also exited successfully. These are upstream tests,
not evidence of a provider round-trip or native push support.
- The HTTPS harness independently passed upstream OIDC discovery, RS256 ID-token
signature/nonce validation, S256 PKCE exchange and session creation with a local
test IdP. The fixture checks single-use codes, exact redirect URIs and OAuth
Basic client authentication. Loopback hosts/ports and automatic fixture consent
are not representative of every provider's cross-site/embedded-agent policies.
- OSV: no listed advisories for **213** locked/direct Maven coordinates and build
plugins at scan time. This is not a guarantee that no vulnerabilities exist.
- ktlint 1.7.1, actionlint 1.7.7, repository invariants, Python compilation,
Node syntax and shell syntax checks passed. JUnit-verifier regression suite:
**8 passed**, including rejection of absent, empty, malformed, insufficient,
failed, errored and skipped evidence.
- Release APK inspection confirms API 26 minimum / API 37 target, no debuggable
flag, backups and cleartext disabled, system-only certificate trust. Exported
entry points are the launcher and AndroidX ProfileInstallReceiver protected by
the system DUMP permission; file/startup providers are not exported. Debug APK
signature verifies; release APK/AAB are unsigned. Collected archives contain no
fixture CA/key or signing-key files.
## Emulator limitations and retry evidence
Earlier extended-suite attempts crashed the host emulator with SIGSEGV under
software/off GPU modes. Host-GPU mode remained running and produced actionable
JUnit failures: an OIDC-return synchronization timeout, followed by an outage
assertion that incorrectly assumed the upstream worker could not serve its cached
app shell, then a document-readiness race after recreation. Tests now verify the
loaded JavaScript origin, wait for the sign-in window to close, retry only a probe
lost to document replacement and exercise recovery through uncached metadata.
The completed retry passes all seven tests without disabling the worker or
weakening production TLS/CSP/origin policies.
API 37 attempts encountered broken package/activity services and locked
credential-encrypted storage. **No API 37 runtime pass is claimed.**
Local logs and reports live in `/home/neste/yuvomi-build/`; test XML is under
`app/outputs/androidTest-results/connected/`. Earlier failing JUnit snapshots are
preserved as `integration-host-gpu-oidc-race.xml` and
`integration-host-gpu-sw-cache.xml`, plus
`integration-host-gpu-document-ready.xml`. Passing evidence is preserved in
`integration-api35-first-pass.xml` and `integration-api35-repeat-pass.xml`; logs are
`upstream-integration-final-retry.log` and `upstream-integration-repeat.log`.
These files are local evidence, not published
artifacts or a hosted CI result.
Test environment: JDK 17.0.20.1, Node 22.22.1, Python 3.14.4, Android Emulator
37.2.12, API 35 x86_64 Google APIs image, WebView 124.0.6367.219. The old bundled
WebView is useful compatibility evidence, not a recommendation to use an unpatched
engine; a current engine/device run remains mandatory.
## Artifacts
Ignored `artifacts/android/` contains the development-signed debug APK and unsigned
release APK/AAB, `SHA256SUMS`, `build-info.json` and manifest/policy/signature
inspection evidence. No integration APK is collected. No IPA or Apple archive
exists. See [Android build](build-android.md) for repeatable collection commands.
## Commands
```sh
bash android/gradlew -p android testDebugUnitTest lintDebug lintRelease assembleDebug assembleRelease bundleRelease connectedDebugAndroidTest
python3 scripts/test-integration.py /path/to/pinned-upstream --android
swift test --package-path ios
swiftc -frontend -parse ios/Sources/*.swift ios/Tests/*.swift ios/UITests/*.swift
python3 scripts/check-repository.py
python3 scripts/check-dependencies.py
python3 scripts/test-verifier.py
python3 scripts/collect-android-artifacts.py
git diff --check
```
This environment is Linux/WSL. Android SDK and a KVM emulator were installed for
real execution. Build output relocation was needed because the Windows mount
rejects some Gradle POSIX metadata operations. Swift's Linux toolchain was
installed for portable tests. No macOS, Xcode, Apple simulator or physical device
is available; no iOS build/archive/UI result is claimed.
## Mandatory remaining evidence
- Run Xcode build, XCTest/UI tests and unsigned archive on macOS; fix any compiler
or runtime issues. The app has **not** yet been typechecked against Apple SDKs.
- Validate real IdPs: cancellation, account linking, TOTP after OIDC, and embedded
provider policy. Browser-only providers have no native-session handoff upstream.
- Real-device camera/document picker cancellation, file MIME cases, rotation with
an outstanding picker, network loss/reconnect, process kill/memory pressure,
session expiry/revocation, OS backup, and offline account isolation.
- Android 17 private-network permission grant/denial/revocation on a matching OS.
Also run minimum-supported API 26 and a current OS/WebView device matrix.
- VoiceOver/TalkBack, large text, contrast, keyboard/safe areas, reduced motion,
light/dark appearance and responsive web modules on actual supported devices.
- Successful CI runs on the configured Gitea runners, private security
reporting contact, human security review, store ownership and production signing.
No Firebase/APNs registration, closed-app push, offline editing, App Store approval,
signing, publication or physical-device validation is claimed. See `SECURITY.md`
and `docs/security-review.md` for residual limitations.
Resume work using [maintainer continuation](continuation.md).
## Gitea runner adaptation
Workflows now live in `.gitea/workflows/` and target the supplied `ubuntu-latest`
pool and native `macos-arm64` M4 Pro runner. Local workflow grammar lint (Gitea URL
syntax normalized), repository invariants, shell/Python syntax, ten mocked runner
preflight regressions and eight evidence-verifier regressions pass. SHA-256 Git
initialization/fetch was exercised locally; the pinned XcodeGen archive's checksum
and native arm64 slice were verified. These are not remote runner execution or
Apple SDK results. See [runner requirements](ci-runners.md); the local emulator
preflight currently rejects unavailable/inaccessible KVM as designed. Original
application build evidence above remains historical and is not a rebuild of the
workflow-only changes.
+81
View File
@@ -0,0 +1,81 @@
# YUVOMI MOBILE – FINAL DELIVERY REPORT
**Delivery status: incomplete native preview, not final product acceptance.**
This report closes the initial native rebuild slice, not the requested complete
1:1 replacement. No module has accepted functional and visual parity.
## 1. Outcome
The rejected embedded-browser application has been replaced by genuine native
shipping entry points. Missing modules do not fall back to a WebView.
## 2. Upstream reference
Unmodified Yuvomi 2.78.0, commit
`10bfef7686ed7303b4c74c7fcc55a5eb16356c9d`. No upstream change or push.
## 3. Android
Kotlin/Compose onboarding, setup/password/TOTP interfaces, basic dashboard,
tasks/notes/shopping and session management. Version `0.2.0-native-preview`,
code 2. This is a tested initial slice, not full module coverage.
## 4. iOS
SwiftUI equivalents, native API models/transport and Keychain storage implemented.
Sixteen portable tests pass; Apple SDK application/UI/Keychain validation remains
unverified locally. No installable IPA or App Store-ready archive is claimed.
## 5. Design parity
Upstream colors, spacing/radii and brand geometry guide native controls. One real
synthetic Android dashboard screenshot was inspected. No side-by-side full PWA
visual acceptance, complete adaptive-layout or accessibility approval yet.
## 6. API and architecture
Direct same-origin HTTPS/JSON, native controls and state; server-owned business
logic and authorization. Superseded browser code/tests are preserved in `legacy/`
but excluded from shipping builds and native test counts.
## 7. Security
System TLS, redirect refusal, bounded reads, upstream CSRF and protected native
cookies. AndroidKeyStore/AES-GCM and iOS Keychain/private URLSession storage; no
stored passwords, test CA in ordinary builds or hidden browser session import.
Human security review and additional device/role/error-path coverage remain open.
## 8. Executed validation
10 JVM tests, 6 native API-35 UI tests, the 7-test real-server suite (including
those same 6), both Android lint variants and package builds pass. Swift has 16
portable passes plus strict formatting/parsing. 23 script regressions, repository
checks, workflow lint and the 266-coordinate OSV scan pass. See
[validation](validation.md) for evidence, failures corrected and exact limitations.
## 9. Artifacts
The local collector replaces old wrapper outputs in ignored `artifacts/android/`
with native debug APK and unsigned release APK/AAB, checksums and manifest/network/
signature inspection. `build-info.json` records exact source identity and dirty
state. Only the development-signed debug APK is installable as provided.
## 10. Gitea CI
The Android workflow offers `android-native-debug-apk`; the Mac workflow bootstraps
verified ARM64 Node before checkout. Actual artifact publication and Apple build
results require remote-run inspection. Remote Ubuntu emulator jobs need KVM access;
local emulator success is not a substitute for a passing remote matrix.
## 11. Signing and publication
Initial owner-authorized signing material/secrets are preserved privately; signing
workflow changes remain parked in the named stash. No production-signed native
release, store submission or claim of completed product delivery.
## 12. Remaining work
Native SSO/recovery/invitations, advanced first-slice controls, the remaining
modules/settings, files, offline, notifications, Apple/device validation and 1:1
visual/behavior acceptance remain substantial work. The complete page inventory
and acceptance criteria are in [native parity](native-parity.md).
+91
View File
@@ -0,0 +1,91 @@
# Native parity tracker
## Acceptance contract
**Native means native screens, forms, lists, navigation and interaction.** Android
uses Kotlin/Compose; iOS uses SwiftUI. PWA HTML/JavaScript is not embedded as the
application surface. The PWA is the design/behavior reference, not the renderer.
Reference: unmodified upstream 2.78.0,
`10bfef7686ed7303b4c74c7fcc55a5eb16356c9d`. The scope includes its thirty page
modules, settings leaves, permissions, dashboard customization and cross-module
workflows. Third-party web extensions need an explicit native integration design;
arbitrary server-installed HTML cannot silently become a native module.
Use these states honestly:
- **Slice**: real native UI/API interaction exists, with the listed limitations.
- **Pending**: not implemented. No fake screen, raw-JSON editor or WebView fallback.
- **Parity accepted**: requires functional and visual evidence on both platforms.
**No module currently has this status.**
## Page inventory
| Upstream page | Native status | Work still required before parity |
| --- | --- | --- |
| login | Slice: password + TOTP/recovery step | OIDC/provider policy, recovery/error UX, localization, visual comparison |
| setup | Slice: initial household creation | Full onboarding and configuration workflow |
| forgot-password | Pending | Native recovery request and server-capability UX |
| reset-password | Pending | Native reset token/confirmation flow |
| join | Pending | Invitations, account setup, validation |
| dashboard | Slice: task/calendar/shopping/note previews | All remaining widgets, user layout/options, timezone-aware presentation, quick add, wall mode |
| tasks | Slice: list/search/status filter/create/edit/complete/delete | Category/assignee/tag/visibility controls, recurrence, subtasks, attachments, comments, archive/history, lock UX, bulk/sync controls |
| shopping | Slice: lists/items/create/edit/check/delete | List rename/duplicate, categories, ordering, suggestions, live versions, prices/stores, imports/send/undo |
| notes | Slice: list/search/create/edit/pin/delete | Markdown/checklists, categories, note colors, ordering and concurrent edit UX |
| calendar | Dashboard preview only; module pending | Agenda/week/month, editing, recurrence/scopes, source calendars, timezone behavior, attachments/sync |
| schedule | Pending | Shift grid, roster, creation and settings |
| birthdays | Pending | Date/age privacy, contacts, reminders/import |
| contacts | Pending | Address book, editing, links/import/export |
| documents | Pending | Browse/search, metadata, native upload/camera/export and lifecycle tests |
| inventory | Pending | Items, locations, guarantees, attachments and reminders |
| meals | Pending | Meal plan, ingredients, household options and shopping transfer |
| recipes | Pending | Search/detail/editor, source import, images, pantry/shopping cross-links |
| pantry | Pending | Stock, batches/expiry, locations, quantity controls and shopping transfer |
| budget | Pending | Accounts, entries, categories, currency/amount handling and attachments |
| budget-plans | Pending | Plan editor, period/category matching and forecasts |
| budget-stats | Pending | Native charts and filters matching upstream calculations |
| subscriptions | Pending | Lifecycle, billing, recurrence and linked budget behavior |
| split-expenses | Pending | Groups, guests, balances, settlement and permission distinctions |
| housekeeping | Pending | Visits, tasks, time and role-gated payment data |
| waste | Pending | Schedules, pickup display/import and reminders |
| rewards | Pending | Catalog, ledger, goals, approvals and pocket-money precision |
| health | Pending | Medication, cycle, nutrition and capability-gated private views |
| health-fasting | Pending | Clock, history, capabilities and lifecycle behavior |
| settings | Slice: native logout/server removal only | Every relevant personal/admin settings leaf and native permissions |
| pair-display | Pending | Pairing protocol, display role and shared-surface privacy |
## Design reference
Native primitives reuse the upstream warm palette (`#F5F3ED` / `#191816`), opaque
surfaces (`#FFFFFF` / `#2B2825`), violet accent, 16-point cards, 26-point auth panel,
pill navigation/buttons and the exact three-circle brand geometry. These are
initial design-token mappings, **not proof of 1:1 visuals**. Material/SF symbols,
typography, sheets, adaptive layouts and module details still need comparison.
Every accepted module needs side-by-side screenshots from matching server data,
light/dark and small/large text coverage, keyboard/safe-area/back behavior,
read-only/denied permission cases and real API success/error tests. Android's
integration test exports a synthetic-fixture native dashboard screenshot; it is
not a production screenshot or a visual-parity approval.
## Implementation order
1. Stabilize the current native authentication/dashboard/tasks/notes/shopping
slice, including Apple SDK compilation and actual native tests.
2. Close auth/recovery/SSO and first-slice feature gaps, then accept those screens
against PWA visual/behavior baselines rather than adding empty menu entries.
3. Implement calendar/family/contacts, kitchen/pantry, documents/inventory, then
finance, household, rewards and health as separately tested native slices.
4. Complete settings, extensions/cross-module flows, accessibility, offline and
notifications. Audit every upstream route/settings leaf before claiming parity.
## Signing work retained, not released
The owner-authorized initial signing key and four Gitea repository secrets were
created before the native correction. The private backup remains outside Git at
`/home/neste/yuvomi-signing/initial-20261021`. Never print/commit its contents.
The uncommitted signing-workflow changes were preserved in Git stash
`wip: owner-authorized signing setup paused for true native rewrite`; do not apply
it blindly to the new workflows. No signing-secret values belong in screenshots,
test fixtures, documentation, commits or artifacts. Native signing/release work
remains paused until this preview's scope is explicitly accepted.
+8 -5
View File
@@ -10,14 +10,14 @@ to either store. A debug APK or unsigned archive is not a production release.
a provisional community identifier, not proof of registry/store ownership. a provisional community identifier, not proof of registry/store ownership.
Confirm use of the Yuvomi name, required attribution and independent status. Confirm use of the Yuvomi name, required attribution and independent status.
3. Bump version/name/code in Android and the iOS project spec; update CHANGELOG. 3. Bump version/name/code in Android and the iOS project spec; update CHANGELOG.
4. Run JVM/emulator/real-upstream HTTPS tests, portable Swift and Xcode simulator 4. Run native JVM/Compose/emulator/real-upstream HTTPS tests, portable Swift and Xcode simulator
tests, static checks, OSV scan, release builds and artifact inspection. Record tests, static checks, OSV scan, release builds and artifact inspection. Record
upstream revision, app SHA, OS/WebView/Xcode versions, results and checksums. upstream revision, app SHA, OS/SDK/Xcode versions, results and checksums.
5. Produce signed artifacts only using an authorized maintainer's credentials. 5. Produce signed artifacts only using an authorized maintainer's credentials.
6. Review store privacy/Data Safety answers for the app AND selected-server model, 6. Review store privacy/Data Safety answers for the app AND selected-server model,
permission rationale, encryption/export compliance, screenshots, support URL, permission rationale, encryption/export compliance, screenshots, support URL,
vulnerability contact, and device demonstrations. A functional web companion vulnerability contact, and device demonstrations. An incomplete native preview
is not guaranteed to pass Apple's minimum-functionality review. is not a finished PWA replacement or App Store approval.
7. Create a release/tag and publish artifacts/store submissions manually only 7. Create a release/tag and publish artifacts/store submissions manually only
after explicit maintainer authorization. Never force-push or rewrite history. after explicit maintainer authorization. Never force-push or rewrite history.
@@ -31,7 +31,10 @@ An authorized maintainer must separately sign the verified APK/AAB using their
own keystore, alias and passwords with `zipalign`, `apksigner`, and `jarsigner`. own keystore, alias and passwords with `zipalign`, `apksigner`, and `jarsigner`.
Keep private material in a protected signing environment, never on the general Keep private material in a protected signing environment, never on the general
validation runners. Securely back up the real upload key and use Play App Signing validation runners. Securely back up the real upload key and use Play App Signing
according to the owner's policy. No production key is generated here. according to the owner's policy. An initial owner-authorized key and repository
secrets already exist outside Git; signing workflow changes are parked during
the native rebuild. Preserve the key, do not generate a replacement or apply the
old signing stash without review. See `native-parity.md`.
Manually verify APK/AAB signatures and inspect Manually verify APK/AAB signatures and inspect
the final manifest: no debuggable flag, HTTP/test CA or unintended exported component. the final manifest: no debuggable flag, HTTP/test CA or unintended exported component.
Never distribute an `.integration` package or include its generated test resources. Never distribute an `.integration` package or include its generated test resources.
+31 -42
View File
@@ -1,47 +1,36 @@
# Security review record # Native security review record
This is an engineering review, not an independent penetration test or certification. This is an engineering checklist, not an independent audit or release approval.
The wrapper review is retained separately as
[historical evidence](legacy-wrapper-security-review.md).
Addressed during implementation: ## Implemented controls to exercise
- Canonical, HTTPS-only origin/port comparison; credential, fragment, query, - Origin-bound HTTPS API transports, no redirects, no production test-CA bypass.
subpath, scheme, lookalike-host and TLS-downgrade regression cases. - Server-owned session and CSRF semantics, no persistent password storage.
- A main-frame POST can bypass Android's usual navigation callback. The request - Origin-bound AES-GCM/AndroidKeyStore cookies; iOS Keychain protected
interception gate now rejects **all** cross-origin requests, not only subresources. WhenUnlockedThisDeviceOnly with a private ephemeral URLSession jar.
The emulator regression was observed failing when the main-frame guard was - Bounded JSON reads, deadlines and no offline/ambiguous-write retry queue.
deliberately removed, then rerun with the fix restored. - Native permission-aware controls, destructive-action confirmation and server
- TLS failures cancel by default. Native download redirects are refused before validation; partial update payloads preserve fields the native editor cannot edit.
cookies can be replayed to another origin. Bootstrap requests use neither - No WebView/WKWebView in either shipping source tree. Legacy tests do not prove
cookies nor automatic redirects and have bounded metadata reads/timeouts. this client's behavior; explicit native tests and source invariants are required.
- Files use explicit system selection/content URIs; no filesystem or storage
permission is granted to web content. Blob exports have no native JS bridge.
- Complete Android browsing-data deletion uses WebStorageCompat, with OS app
reset for engines without that capability. WebStorage.deleteAllData alone was
insufficient for service-worker CacheStorage/IndexedDB and is not relied upon.
- iOS server erase replaces the WKWebView, not only its URL, eliminating old
history/back-forward contexts as well as persistent website data.
- iOS metadata uses typed decoding, rejecting numeric values masquerading as booleans.
- Native picker results are bound to the browsing generation/request. Android
rejects stale upload/download selections, revokes camera URI grants, and clears
capture cache on erasure. iOS ignores export-cancellation callbacks from an old
download. Apple-specific callback regressions have not run without Xcode.
- KGP was upgraded after OSV identified CVE-2026-53914 in the original build tool.
The final OSV scan found no listed advisories across 213 locked/direct Maven
coordinates and build plugins. Rescan after resolution/lock updates.
- The inspected release APK is not debuggable, rejects cleartext, disables backup
and trusts system CAs only. AndroidX's exported profile installer receiver is
protected by the system DUMP permission; app providers are not exported.
Release gates still requiring evidence: ## Remaining gates
- Actual Xcode typecheck, simulator, archive, physical-device and accessibility review. 1. Run Apple SDK typechecking, simulator/XCTest/UI and device checks; portable
- End-to-end IdP flows, especially providers that prohibit embedded agents; no Foundation transport compilation is not SwiftUI/Security.framework validation.
browser-to-native session transfer is supported by the current upstream protocol. 2. Review persistent-cookie expiry/removal, TLS failures, redirects, CSRF refresh,
- iOS unknown-length download disk-pressure behavior; a post-download size check expired sessions, background/recreation and stale-response isolation on devices.
is not a streaming quota. Verify file picker/camera cancellation on real devices. 3. Review migrations from the prototype: browser sessions are never imported and
- Android file-picker/camera/rotation while a picker is outstanding; cancellation OS website data may remain until app-data reset/reinstall.
on process death intentionally loses the selection, never silently retries it. 4. Complete missing auth/provider protocols and permission/role scenarios before
- Offline sign-out/account switching, stale-worker updates and server erasure on claiming native authentication parity, particularly SSO-only deployments.
current WebView/WKWebView versions. Upstream tests are not native device proof. 5. Implement/review files, notifications, offline storage and additional modules
- Reverify final signed release manifests, signing, store ownership, private vulnerability contact as actual native capabilities; do not restore the wrapper as a shortcut.
and applicable privacy disclosures. No claim that all vulnerabilities are absent. 6. Independently review the new UI/state/transport code, actual signed artifacts,
accessibility, supported OS matrix and runner trust before distribution.
The owner-authorized signing configuration exists privately but its workflow
changes are intentionally parked. A signed incomplete client is not a completed
native product. Refer to [native parity](native-parity.md) before release work.
+31 -60
View File
@@ -1,70 +1,41 @@
# Server compatibility and authentication # Native server compatibility
Source reference: upstream 2.78.0 revision Reference: Yuvomi 2.78.0 at
`10bfef7686ed7303b4c74c7fcc55a5eb16356c9d`. No general version range has been `10bfef7686ed7303b4c74c7fcc55a5eb16356c9d`. No general server-version range or
certified. Anonymous bootstrap metadata deliberately omits the exact version; complete native feature compatibility is certified.
native probing verifies shape, not an invented minimum-version comparison.
Use `https://family.example.com` or `https://family.example.com:8443`, backed by a Enter a root HTTPS hostname, optionally with a custom port. HTTP, embedded
root-hosted Yuvomi installation and a valid system-trusted certificate. Operators credentials, query/fragment configuration and subpath deployments are rejected.
should set `SESSION_SECURE=true`, correct `BASE_URL`, and correct proxy trust. Use a valid system-trusted TLS certificate and reachable VPN/LAN connectivity.
Private servers are supported through reachable HTTPS/VPN, not TLS bypasses. No certificate bypass or proprietary mobile service is introduced.
International hostnames should be entered in ASCII/punycode for both platforms.
Root-relative assets, API endpoints, cookie paths, callback redirects and worker The native client probes the real public `/api/v1/version` and
registration mean `/yuvomi/` subpaths are not supported by the inspected source. `/api/v1/auth/oidc/config` shapes. Anonymous metadata does not disclose the server
Use a dedicated hostname. Reverse proxies must pass `/api/v1/*`, cookies, HTML, version. An authentication proxy returning redirects/HTML instead of those JSON
JavaScript and the service worker consistently. An external authentication proxy contracts is unsupported, not silently treated as a valid Yuvomi API.
that intercepts bootstrap calls is rejected with a connection error; it is not
silently identified as a Yuvomi server.
## Authentication ## Authentication
Password login, TOTP/recovery codes, password reset, invitations, server-side The implemented native slice uses setup/password login, the existing two-factor
session expiry/revocation and logout remain upstream web flows. No native code verification endpoint, `/auth/me`, session cookies, CSRF headers and logout.
stores the password, supplies a CSRF bypass, or extends an expired session. Passwords are not stored. Native session persistence is separate from browser
Backgrounding does not clear cookies. Native server erasure does not revoke a storage and protected by the platform keystore/keychain.
remote session; use Yuvomi logout first or revoke sessions server-side.
### OIDC: local fixture verified; real-provider validation pending **Native SSO is not implemented yet.** The old wrapper's local-IdP WebView result
does not apply to this client. The existing server protocol does not provide a
generic browser-to-native session exchange for arbitrary self-hosted hosts; a
reviewed solution is required. SSO-only deployments cannot currently use this
native preview. Reset and invitation workflows also remain pending.
The initiating Yuvomi session contains OIDC state, nonce and PKCE verifier. When Only initial dashboard/tasks/notes/shopping capabilities are implemented; see the
the server redirects to an identity provider, native consent offers an isolated [parity tracker](native-parity.md) for precise gaps. The complete server UI is not
web sign-in window sharing the OS website cookie store. HTTPS-only redirects embedded to fill those gaps. Read-only and denied module permissions affect
can return to the server callback; the final same-origin page returns to the main native controls; the server remains the enforcement point.
view. No app-capability handlers or JS bridge are installed in that auth window.
Some providers (including configurations that prohibit embedded user agents) ## Notifications, files and offline
require a system browser. **Those cannot sign the native view in with upstream's
current protocol.** Use Server settings -> Open server in browser, and sign in
there for an independent browser session. Opening only the external authorization
link in a browser does not transfer the initiating cookie. Browser cookies are
never copied into the app, and custom-scheme callbacks are not fabricated.
The API 35 integration suite completes a native isolated-window round trip with Native remote push, uploads/camera/download/export and offline caching/editing
a local test IdP, real RS256 ID tokens, nonce and S256 PKCE. No external are not current native capabilities. The old wrapper's file handlers and the
IdP/developer account was supplied. Validate the actual provider, including PWA service worker are not part of the native renderer. No fake registration or
cross-site cookies, cancellation, errors, TOTP and account linking, before relying automatic offline mutation replay is supplied. Optional existing server-side
on that deployment. iOS authentication has not run against Apple SDKs. A native ntfy/Gotify/email integrations remain independently operated upstream features.
browser handoff would require an independently reviewed upstream protocol; no
such upstream change is included or published.
Android generated exports require a current WebView supporting document-start
scripts and are capped at 25 MB. A bounded Blob registry preserves a generated
file even when upstream immediately revokes its object URL; it does not fetch
blob URLs or alter the server's CSP. HTTPS file downloads are capped at 100 MB.
Update the system WebView if generated exports are unavailable on an old engine.
## Notifications and offline
Upstream implements Web Push and optional ntfy/Gotify/webhook/email channels.
Embedded web views do not supply a portable native push subscription. This app
has **no native remote-push claim**, no Firebase/APNs secrets, no background
polling service, and no fake notification-registration UI. Configure an existing
optional upstream ntfy/Gotify channel and its client if desired. No centralized
service is required by this application.
Upstream service-worker caching is read-only, allow-listed, and versioned.
Mutations/authentication remain online. Platform-specific cache/resume behavior
and offline account switching still need device validation. Do not rely on offline
editing, synchronization, or closed-app push in this native development build.
+58 -119
View File
@@ -1,92 +1,60 @@
# Validation status # Native validation
**Partially verified development software, not production-ready.** **Incomplete native rebuild; not full PWA parity and not production-ready.**
Local validation date: 2026-10-21. No unexecuted iOS application test, hosted CI The old browser results are [historical only](legacy-wrapper-validation.md).
run, production signing, or store publication is represented as completed.
## Executed evidence The new shipping surfaces are Android Compose and iOS SwiftUI. Current native
tests are in `androidTestNative` / `androidTestNativeIntegration`, JVM native policy
tests, and Swift production contracts/transport tests. Archived WebView tests are
not included in the native suites or counted toward native completion.
- Android debug APK and unsigned release APK/AAB built successfully; both ## Executed locally — 2026-10-21
`lintDebug` and `lintRelease` passed. JVM security/picker-policy tests: **7 passed,
zero failures/skips**.
- Android API 35 regular emulator suite: **6 passed, zero failures/skips**.
This covers onboarding, invalid URLs, background/resume, hardened settings,
cross-origin POST/subresource isolation, opaque-page Blob rejection and activity
recreation. Its permission test checks that the API 37 permission UI is absent;
it is **not** evidence of API 37 permission grant/denial behavior.
- Unmodified pinned upstream ran behind a temporary HTTPS reverse proxy.
Bootstrap metadata, password login, Secure/HttpOnly/SameSite cookies, persistent
session, CSRF rejection, logout/revocation passed in the harness.
- Android's extended real-server scenario exercises login, HttpOnly cookies,
document upload via upstream fetch, native FileTransfer output, bounded Blob
export, background/resume, recreation, CSRF-protected logout, revocation,
recovery, isolated OIDC return and back navigation. It does not automate the OS
camera/document destination picker or prove every upstream module works.
Two consecutive API 35 runs have **7 passed, zero failures/skips each**, including the
six regular security/onboarding cases and this real-server scenario.
- The cross-origin POST regression was deliberately observed failing without its
guard. The production guard was restored and the API 35 regression passes.
- Portable Swift production-policy tests: 7 passed (URL and typed metadata).
All Swift sources passed format lint and parsing with Swift 6.2.1 on Linux.
The portable package excludes BrowserModelTests. This does not build iOS.
- Selected upstream suites ran successfully: cookie-secure-flag (4),
session-sliding (8), two-factor (22), password-reset (24), sw-api-cache (42).
Upstream's plain OIDC script also exited successfully. These are upstream tests,
not evidence of a provider round-trip or native push support.
- The HTTPS harness independently passed upstream OIDC discovery, RS256 ID-token
signature/nonce validation, S256 PKCE exchange and session creation with a local
test IdP. The fixture checks single-use codes, exact redirect URIs and OAuth
Basic client authentication. Loopback hosts/ports and automatic fixture consent
are not representative of every provider's cross-site/embedded-agent policies.
- OSV: no listed advisories for **213** locked/direct Maven coordinates and build
plugins at scan time. This is not a guarantee that no vulnerabilities exist.
- ktlint 1.7.1, actionlint 1.7.7, repository invariants, Python compilation,
Node syntax and shell syntax checks passed. JUnit-verifier regression suite:
**8 passed**, including rejection of absent, empty, malformed, insufficient,
failed, errored and skipped evidence.
- Release APK inspection confirms API 26 minimum / API 37 target, no debuggable
flag, backups and cleartext disabled, system-only certificate trust. Exported
entry points are the launcher and AndroidX ProfileInstallReceiver protected by
the system DUMP permission; file/startup providers are not exported. Debug APK
signature verifies; release APK/AAB are unsigned. Collected archives contain no
fixture CA/key or signing-key files.
## Emulator limitations and retry evidence | Check | Observed result |
| --- | --- |
| Android JVM tests | 10 passed; zero failed/skipped |
| Native Compose UI/security suite, API 35 emulator | 6 passed; zero failed/skipped |
| Real-upstream native integration suite | 7 passed; zero failed/skipped (the same 6 onboarding tests plus 1 multi-step real-server scenario) |
| Native scenario | Login/dashboard, activity recreation, task/note/list/item creation and API readback, redirect refusal, logout and vault erasure |
| Integration screenshot export | Successful AGP test-output collection; native dashboard PNG opened and visually confirmed nonblank, synthetic data only |
| Android lint | Debug and release passed; newer-version recommendations remain informational |
| Android packages | Debug APK and unsigned release APK/AAB built successfully |
| Swift portable suite | 16 passed; actual Foundation transport/contracts and URL/metadata policy, not Apple UI/Keychain execution |
| Swift formatting/parsing | Strict format lint and parsing passed, including Apple-only sources |
| Script regressions | 10 runner-preflight, 8 JUnit-evidence and 5 screenshot-output tests passed |
| Repository/workflows | Source invariants, Python compilation, diff whitespace and actionlint for all 3 Gitea workflows passed |
| OSV | No listed advisories for 266 locked/direct Maven coordinates and plugins; not a security audit |
Earlier extended-suite attempts crashed the host emulator with SIGSEGV under Environment: Linux/WSL, OpenJDK 17.0.20.1, Gradle 9.6.0, AGP 9.4.1,
software/off GPU modes. Host-GPU mode remained running and produced actionable Kotlin/Compose compiler 2.4.21, API 35 `sdk_gphone64_x86_64` emulator, Swift 6.2.1.
JUnit failures: an OIDC-return synchronization timeout, followed by an outage The host-only Tink upgrade to 1.15.0 removes the observed obsolete generated
assertion that incorrectly assumed the upstream worker could not serve its cached protobuf warning; the final Android validation log has no such warning.
app shell, then a document-readiness race after recreation. Tests now verify the
loaded JavaScript origin, wait for the sign-in window to close, retry only a probe
lost to document replacement and exercise recovery through uncached metadata.
The completed retry passes all seven tests without disabling the worker or
weakening production TLS/CSP/origin policies.
API 37 attempts encountered broken package/activity services and locked The first integration attempt missed the scrolled-off login button. Later,
credential-encrypted storage. **No API 37 runtime pass is claimed.** post-uninstall screenshot retrieval failed, and the first compositor capture was
black. The final test scrolls the real button, uses native-window capture with a
nonblank-pixel assertion and writes through `PlatformTestStorageRegistry` for AGP
collection. The latest complete harness exits successfully, not just its JUnit
subprocess. Ordinary builds retain screenshot protection.
Local logs and reports live in `/home/neste/yuvomi-build/`; test XML is under The unmodified pinned upstream HTTPS/OIDC protocol fixture also passed TLS trust,
`app/outputs/androidTest-results/connected/`. Earlier failing JUnit snapshots are metadata, cookies, CSRF and remote revocation checks. Its IdP exchange is **not
preserved as `integration-host-gpu-oidc-race.xml` and native SSO**: the native client refuses the redirect and SSO remains pending.
`integration-host-gpu-sw-cache.xml`, plus
`integration-host-gpu-document-ready.xml`. Passing evidence is preserved in
`integration-api35-first-pass.xml` and `integration-api35-repeat-pass.xml`; logs are
`upstream-integration-final-retry.log` and `upstream-integration-repeat.log`.
These files are local evidence, not published
artifacts or a hosted CI result.
Test environment: JDK 17.0.20.1, Node 22.22.1, Python 3.14.4, Android Emulator Evidence paths in the local external build directory:
37.2.12, API 35 x86_64 Google APIs image, WebView 124.0.6367.219. The old bundled
WebView is useful compatibility evidence, not a recommendation to use an unpatched
engine; a current engine/device run remains mandatory.
## Artifacts - `app/test-results/testDebugUnitTest/`
- `app/outputs/androidTest-results/connected/{debug,integration}/`
- `app/outputs/native-screenshots/native-dashboard.png`
- `app/reports/lint-results-{debug,release}.html`
- Logs: `/home/neste/yuvomi-native-validation.log`,
`/home/neste/yuvomi-native-integration-final.log`,
`/home/neste/yuvomi-native-swift.log`.
Ignored `artifacts/android/` contains the development-signed debug APK and unsigned No Apple SDK application build, simulator UI test or physical-device result is
release APK/AAB, `SHA256SUMS`, `build-info.json` and manifest/policy/signature claimed from these Linux runs. The modified Mac bootstrap and remote APK upload
inspection evidence. No integration APK is collected. No IPA or Apple archive still require inspection of the next actual Gitea run. Prior remote emulator jobs
exists. See [Android build](build-android.md) for repeatable collection commands. failed KVM preflight; the prerequisite was not weakened or counted as passing.
## Commands ## Commands
@@ -97,47 +65,18 @@ swift test --package-path ios
swiftc -frontend -parse ios/Sources/*.swift ios/Tests/*.swift ios/UITests/*.swift swiftc -frontend -parse ios/Sources/*.swift ios/Tests/*.swift ios/UITests/*.swift
python3 scripts/check-repository.py python3 scripts/check-repository.py
python3 scripts/check-dependencies.py python3 scripts/check-dependencies.py
python3 scripts/test-verifier.py python3 scripts/lint-workflows.py /path/to/actionlint
python3 scripts/collect-android-artifacts.py
git diff --check
``` ```
This environment is Linux/WSL. Android SDK and a KVM emulator were installed for On the local WSL checkout use `YUVOMI_BUILD_DIR=/home/neste/yuvomi-native-build`
real execution. Build output relocation was needed because the Windows mount and the SDK paths from [continuation](continuation.md). Select only the authorized
rejects some Gradle POSIX metadata operations. Swift's Linux toolchain was test emulator with `ANDROID_SERIAL=emulator-5554`; unrelated/unauthorized attached
installed for portable tests. No macOS, Xcode, Apple simulator or physical device devices are not test targets.
is available; no iOS build/archive/UI result is claimed.
## Mandatory remaining evidence ## External gates
- Run Xcode build, XCTest/UI tests and unsigned archive on macOS; fix any compiler Apple SDK builds, real supported devices/current engines, Android API 37, native
or runtime issues. The app has **not** yet been typechecked against Apple SDKs. SSO/reset/invite protocols, permission/role cases, files/offline/notifications,
- Validate real IdPs: cancellation, account linking, TOTP after OIDC, and embedded complete module and settings parity, visual baselines, accessibility and human
provider policy. Browser-only providers have no native-session handoff upstream. security review remain open. Missing modules are listed in
- Real-device camera/document picker cancellation, file MIME cases, rotation with [native parity](native-parity.md), not silently represented as working.
an outstanding picker, network loss/reconnect, process kill/memory pressure,
session expiry/revocation, OS backup, and offline account isolation.
- Android 17 private-network permission grant/denial/revocation on a matching OS.
Also run minimum-supported API 26 and a current OS/WebView device matrix.
- VoiceOver/TalkBack, large text, contrast, keyboard/safe areas, reduced motion,
light/dark appearance and responsive web modules on actual supported devices.
- Successful CI runs on the configured Gitea runners, private security
reporting contact, human security review, store ownership and production signing.
No Firebase/APNs registration, closed-app push, offline editing, App Store approval,
signing, publication or physical-device validation is claimed. See `SECURITY.md`
and `docs/security-review.md` for residual limitations.
Resume work using [maintainer continuation](continuation.md).
## Gitea runner adaptation
Workflows now live in `.gitea/workflows/` and target the supplied `ubuntu-latest`
pool and native `macos-arm64` M4 Pro runner. Local workflow grammar lint (Gitea URL
syntax normalized), repository invariants, shell/Python syntax, ten mocked runner
preflight regressions and eight evidence-verifier regressions pass. SHA-256 Git
initialization/fetch was exercised locally; the pinned XcodeGen archive's checksum
and native arm64 slice were verified. These are not remote runner execution or
Apple SDK results. See [runner requirements](ci-runners.md); the local emulator
preflight currently rejects unavailable/inaccessible KVM as designed. Original
application build evidence above remains historical and is not a rebuild of the
workflow-only changes.