Add offline-mode init via DITTO_OFFLINE_LICENSE_TOKEN across quickstart apps#282
Add offline-mode init via DITTO_OFFLINE_LICENSE_TOKEN across quickstart apps#282Brian Plattenburg (bplattenburg) wants to merge 7 commits into
Conversation
Every quickstart now reads DITTO_OFFLINE_LICENSE_TOKEN from the repo-root .env. When the value is non-empty (after trimming whitespace), the app initializes Ditto in offline-only mode using the SDK's SmallPeersOnly / OfflinePlayground identity and calls setOfflineOnlyLicenseToken with the trimmed value. When empty/unset, the existing Online Playground flow runs unchanged. Touched 17 apps (10 v5, 7 v4). v4 apps keep disableSyncWithV3() and the DQL strict-mode ALTER SYSTEM call in both branches; v5 SDKs no longer expose those, so they don't appear. cpp-tui is intentionally skipped (handed off separately). android-cpp and java-spring have no committed source and are out of scope. Unit tests for the mode-selection contract added in the 7 apps that already have a unit-test runner wired up: android-java, android-kotlin, dotnet-tui, flutter_app, java-server, react-native, rust-tui. Tests exercise null / empty / whitespace-only / non-empty token cases. The other 10 apps either have no test runner today or have integration / UI test scaffolds rather than unit test ones; adding test infrastructure for those is out of scope. A docs/offline-mode-followup-for-v5-migration-prs.md note describes how to re-apply this branch on top of @biozal's four open v5 migration PRs (#239 Android Kotlin, #237 Flutter, #267 React Native, #242 Rust TUI), since those PRs rewrite the init files this change touches.
Three follow-ups to the previous commit, surfaced by running the native builds locally: - java-server and kotlin-multiplatform's secrets generators emit DittoSecretsConfiguration fields from .env keys. If the user's local .env predates this PR and lacks DITTO_OFFLINE_LICENSE_TOKEN, the generated class is missing the field and the consuming code fails to compile. Both generators now default the key to "" when absent. - swift/buildEnv.sh has the same shape: it generates Env.swift from .env lines, so a pre-existing .env without DITTO_OFFLINE_LICENSE_TOKEN produces an Env type without the property. The script now emits an empty default when the key is missing. - The v5 Java SDK's Ditto.setOfflineOnlyLicenseToken(String) is declared to throw DittoException. java-server's call now wraps it and rethrows as RuntimeException to keep the Spring DittoService constructor signature unchanged. Verified locally after these fixes: swift xcodebuild, java-server tests, kotlin-multiplatform commonMain + Android target compile.
The DittoDotNetTasksConsole project has <Nullable>disable</Nullable>, so the 'string?' parameter triggered CS8632 on .NET 10. IsNullOrWhiteSpace handles null safely; drop the '?' to keep the project's nullable mode consistent.
This is a transient planning doc; once all four migrations have the offline branch re-applied, the file itself should be deleted in the final follow-up PR. Document that explicitly so a future reader doesn't take a stale plan as durable docs.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in offline-only initialization path across the repository’s quickstart apps, toggled by DITTO_OFFLINE_LICENSE_TOKEN. When set (non-empty after trimming), apps initialize Ditto in offline-only mode and activate via setOfflineOnlyLicenseToken; otherwise they retain the existing Online Playground flow.
Changes:
- Introduces
DITTO_OFFLINE_LICENSE_TOKENsupport and per-app mode selection across v4/v5 quickstarts. - Updates transport/auth setup to be online-only (websocket + auth refresh) vs offline-only (small-peers + license token).
- Adds/updates README guidance plus lightweight unit tests for mode selection in a subset of apps.
Reviewed changes
Copilot reviewed 63 out of 65 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
.env.sample |
Documents the new optional DITTO_OFFLINE_LICENSE_TOKEN variable. |
README.md |
Adds top-level offline-only mode documentation and behavior contract. |
CLAUDE.md |
Documents offline-mode contract for contributors/automation. |
docs/offline-mode-followup-for-v5-migration-prs.md |
Coordination notes for re-applying offline logic after pending v5 migrations. |
swift/Tasks/DittoManager.swift |
Branches DittoConfig/connect and activation based on offline token. |
swift/buildEnv.sh |
Ensures DITTO_OFFLINE_LICENSE_TOKEN is always emitted for compilation. |
swift/README.md |
Adds offline-only mode instructions for Swift quickstart. |
rust-tui/src/bin/main.rs |
Adds offline token flag/env, selects identity mode, and updates transport/auth accordingly. |
rust-tui/README.md |
Adds offline-only mode instructions for Rust TUI. |
react-native/types/env.d.ts |
Adds DITTO_OFFLINE_LICENSE_TOKEN to env typings. |
react-native/dittoMode.ts |
Adds shared helper for online/offline mode selection. |
react-native/App.tsx |
Branches config/auth/websocket vs offline license activation. |
react-native/__tests__/dittoMode.test.ts |
Adds unit tests for mode selection helper. |
react-native/README.md |
Adds offline-only mode instructions for React Native. |
react-native-expo/types/env.d.ts |
Adds missing env typings including offline token. |
react-native-expo/App.tsx |
Adds offline/online branching and offline license activation. |
react-native-expo/README.md |
Adds offline-only mode instructions for Expo app. |
kotlin-multiplatform/composeApp/src/commonMain/kotlin/com/ditto/quickstart/ditto/DittoManager.kt |
Adds offline/online config branching and offline license activation. |
kotlin-multiplatform/build-logic/src/main/kotlin/quickstart-conventions.gradle.kts |
Defaults offline token to empty in generated secrets to keep builds compiling. |
kotlin-multiplatform/README.md |
Adds offline-only mode instructions for KMP quickstart. |
javascript-web/src/App.tsx |
Adds offline/online config branching and online-only auth/websocket setup. |
javascript-web/README.md |
Adds offline-only mode instructions for web quickstart. |
javascript-tui/source/cli.js |
Adds offline token parsing and offline-only activation. |
javascript-tui/README.md |
Adds offline-only mode instructions for JS TUI. |
java-server/src/main/java/com/ditto/example/spring/quickstart/configuration/DittoMode.java |
Adds mode enum + selector. |
java-server/src/main/java/com/ditto/example/spring/quickstart/service/DittoService.java |
Branches config/auth/websocket vs offline activation. |
java-server/src/test/java/com/ditto/example/spring/quickstart/configuration/DittoModeTest.java |
Adds unit tests for mode selection. |
java-server/buildSrc/src/main/kotlin/quickstart-conventions.gradle.kts |
Defaults offline token to empty in generated secrets. |
java-server/README.md |
Adds offline-only mode instructions for Java server. |
go-tui/main.go |
Adds offline token parsing and offline-only activation. |
go-tui/README.md |
Adds offline-only mode instructions for Go TUI. |
flutter_app/lib/ditto_mode.dart |
Adds mode enum + selector helper. |
flutter_app/lib/main.dart |
Adds offline identity path + offline license activation; makes some env reads optional. |
flutter_app/test/ditto_mode_test.dart |
Adds unit tests for mode selection helper. |
flutter_app/README.md |
Adds offline-only mode instructions for Flutter app. |
electron/src/main/env.ts |
Adds mode selection + offline token parsing and adjusts env validation. |
electron/src/main/ditto.ts |
Branches connect/auth/websocket vs offline activation. |
electron/README.md |
Adds offline-only mode instructions for Electron app. |
dotnet-winforms/TasksApp/DittoMode.cs |
Adds mode enum + selector helper. |
dotnet-winforms/TasksApp/Program.cs |
Reads offline token and threads it into peer creation; makes online vars optional. |
dotnet-winforms/TasksApp/TasksPeer.cs |
Branches connect/auth vs offline activation using mode. |
dotnet-winforms/README.md |
Adds offline-only mode instructions for WinForms app. |
dotnet-tui/DittoDotNetTasksConsole/DittoMode.cs |
Adds mode enum + selector helper. |
dotnet-tui/DittoDotNetTasksConsole/Program.cs |
Reads offline token and threads it into peer creation; makes online vars optional. |
dotnet-tui/DittoDotNetTasksConsole/TasksPeer.cs |
Branches connect/auth vs offline activation using mode. |
dotnet-tui/DittoDotNetTasksConsole.Tests/DittoModeTests.cs |
Adds unit tests for mode selection helper. |
dotnet-tui/README.md |
Adds offline-only mode instructions for .NET TUI app. |
dotnet-maui/DittoMauiTasksApp/MauiProgram.cs |
Adds offline branching and offline license activation to MAUI init. |
dotnet-maui/README.md |
Adds offline-only mode instructions for MAUI app. |
dotnet-winforms-net48/TasksPeerService.cs |
Threads offline token through initialization path. |
dotnet-winforms-net48/TasksPeer.cs |
Adds offline activation path and switches connect type accordingly. |
dotnet-winforms-net48/AppConfiguration.cs |
Adds offline token config + online-only validation of playground/auth. |
dotnet-winforms-net48/Program.cs |
Threads offline token into initialization. |
dotnet-winforms-net48/README.md |
Adds offline-only mode instructions for .NET 4.8 app. |
android-kotlin/QuickStartTasks/app/src/main/java/live/ditto/quickstart/tasks/DittoMode.kt |
Adds mode enum + selector helper. |
android-kotlin/QuickStartTasks/app/src/test/java/live/ditto/quickstart/tasks/DittoModeTest.kt |
Adds unit tests for mode selection. |
android-kotlin/QuickStartTasks/app/src/main/java/live/ditto/quickstart/tasks/TasksApplication.kt |
Branches identity, websocket config, and offline activation. |
android-kotlin/QuickStartTasks/app/build.gradle.kts |
Adds BuildConfig field for offline token and defaults it when missing. |
android-kotlin/README.md |
Adds offline-only mode instructions for Android Kotlin app. |
android-java/app/src/main/java/com/example/dittotasks/DittoMode.java |
Adds mode enum + selector helper. |
android-java/app/src/test/java/com/example/dittotasks/DittoModeTest.java |
Adds unit tests for mode selection. |
android-java/app/src/main/java/com/example/dittotasks/DittoHelper.kt |
Adds offline Ditto factory helper that activates offline token. |
android-java/app/src/main/java/com/example/dittotasks/MainActivity.java |
Branches init/auth behavior based on selected mode. |
android-java/app/build.gradle.kts |
Adds BuildConfig field for offline token. |
android-java/README.md |
Adds offline-only mode instructions for Android Java app. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI Lint runs `dotnet format --verify-no-changes` and flagged the indentation of the LoginAsync arguments after the previous change wrapped the call in an if/else. `dotnet format` reformats them; commit the result.
IntegrationTest.csproj pulls in TasksApp/TasksPeer.cs via a <Compile Include> link. TasksPeer.cs now references DittoMode, which lives in TasksApp/DittoMode.cs and wasn't being linked. The main TasksApp built fine because it picks up everything in its own folder; only IntegrationTest broke, and only on Windows (the only platform that builds the net10.0-windows target).
…line-only builds Two themes from the bot review on PR #282: A) Online-mode init didn't validate that the playground/auth/websocket values were non-empty before using them, so a misconfigured online run would fail with an opaque UriFormatException or similar instead of a clear "DITTO_AUTH_URL is required" message. Added a validation block at the start of init in: - rust-tui (anyhow::bail with the list of missing vars) - flutter_app (Exception with the list of missing vars) - dotnet-tui, dotnet-winforms, dotnet-maui (InvalidOperationException) The pattern mirrors what electron and dotnet-winforms-net48 already do. B) The Android build.gradle.kts loadEnvProperties fallback (used when .env is absent) unconditionally required all playground vars from the process env, blocking offline-only build configurations. Both android-java and android-kotlin now only require DITTO_APP_ID, plus either DITTO_OFFLINE_LICENSE_TOKEN or the full playground trio. Also picked up dotnet format whitespace on MauiProgram.cs after the SetupDitto edit; auto-fixed.
|
copilot-pull-request-reviewer thanks for the review — all 7 comments addressed in c7d5fc1. Two themes, both fixed: Online-mode validation gap — the 5 init paths (rust-tui, flutter_app, dotnet-tui, dotnet-winforms, dotnet-maui) now check Android Local validation re-run after the fix: rust-tui cargo test, flutter test, dotnet-tui/dotnet-winforms/dotnet-maui builds, dotnet-maui |
Summary
Adds an optional
DITTO_OFFLINE_LICENSE_TOKENat the repo root. Non-empty after trim → app initializes Ditto in offline-only mode (SmallPeersOnlyconnect on v5 /OfflinePlaygroundidentity on v4) and callssetOfflineOnlyLicenseToken. Empty/unset → existing Online Playground flow, unchanged.Scope
17 quickstarts:
*android-java's lockfile looked v4 but the artifact is actuallycom.ditto:ditto-kotlin-android:5.0.0; applied the v5 pattern.Out of scope: cpp-tui (handing off separately), android-cpp + java-spring (no committed source).
Unit tests (7 apps)
android-java,android-kotlin,dotnet-tui,flutter_app,java-server,react-native,rust-tui. Three cases each: empty / whitespace / non-empty token. No SDK mocking. The other 10 apps don't have a unit-test runner wired up today; adding test infra is a separate effort.Coordination with Aaron LaBeau (@biozal)'s open v5 migration PRs
#239 (Android Kotlin), #237 (Flutter), #267 (React Native), #242 (Rust TUI) rewrite the init files this PR edits. After they merge, the offline branch needs to be re-applied.
docs/offline-mode-followup-for-v5-migration-prs.mdhas the per-PR recipe.