Skip to content

Add offline-mode init via DITTO_OFFLINE_LICENSE_TOKEN across quickstart apps#282

Open
Brian Plattenburg (bplattenburg) wants to merge 7 commits into
mainfrom
add-offline-mode-license-token
Open

Add offline-mode init via DITTO_OFFLINE_LICENSE_TOKEN across quickstart apps#282
Brian Plattenburg (bplattenburg) wants to merge 7 commits into
mainfrom
add-offline-mode-license-token

Conversation

@bplattenburg
Copy link
Copy Markdown
Member

Summary

Adds an optional DITTO_OFFLINE_LICENSE_TOKEN at the repo root. Non-empty after trim → app initializes Ditto in offline-only mode (SmallPeersOnly connect on v5 / OfflinePlayground identity on v4) and calls setOfflineOnlyLicenseToken. Empty/unset → existing Online Playground flow, unchanged.

Scope

17 quickstarts:

  • v5 (10): dotnet-maui, dotnet-tui, dotnet-winforms, electron, go-tui, java-server, javascript-tui, javascript-web, kotlin-multiplatform, swift.
  • v4 (7): android-java*, android-kotlin, dotnet-winforms-net48, flutter_app, react-native, react-native-expo, rust-tui.

* android-java's lockfile looked v4 but the artifact is actually com.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.md has the per-PR recipe.

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.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_TOKEN support 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.

Comment thread rust-tui/src/bin/main.rs
Comment thread flutter_app/lib/main.dart
Comment thread dotnet-maui/DittoMauiTasksApp/MauiProgram.cs
Comment thread dotnet-winforms/TasksApp/TasksPeer.cs
Comment thread dotnet-tui/DittoDotNetTasksConsole/TasksPeer.cs
Comment thread android-kotlin/QuickStartTasks/app/build.gradle.kts
Comment thread android-java/app/build.gradle.kts
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.
@bplattenburg
Copy link
Copy Markdown
Member Author

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 mode == OnlinePlayground && (token | authUrl | websocketUrl) is empty at startup and throw with a clear message listing the missing vars and pointing at DITTO_OFFLINE_LICENSE_TOKEN as the alternative. Mirrors the pattern already in electron (assertEnv) and dotnet-winforms-net48 (AppConfiguration.Load).

Android .env-missing fallbackandroid-kotlin and android-java build.gradle.kts now require DITTO_APP_ID always, and the playground/auth/websocket trio only when DITTO_OFFLINE_LICENSE_TOKEN is unset. Offline-only Android builds work with just DITTO_APP_ID + DITTO_OFFLINE_LICENSE_TOKEN in the environment.

Local validation re-run after the fix: rust-tui cargo test, flutter test, dotnet-tui/dotnet-winforms/dotnet-maui builds, dotnet-maui dotnet format --verify-no-changes, android-kotlin + android-java ./gradlew testDebugUnitTest — all clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants