@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
# Offline-First Architecture — Architecture Expertise Module
|
|
2
|
+
|
|
3
|
+
> Offline-first design treats network connectivity as an enhancement, not a requirement. The app works fully offline with local data, syncing when connectivity is available. Critical for mobile apps in areas with poor connectivity, field service apps, and any app where users expect instant response regardless of network state.
|
|
4
|
+
|
|
5
|
+
> **Category:** Mobile Architecture
|
|
6
|
+
> **Complexity:** Complex
|
|
7
|
+
> **Applies when:** Mobile or web apps that must work without network connectivity, apps used in low-connectivity environments, apps where instant response time is critical
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What This Is (and What It Isn't)
|
|
12
|
+
|
|
13
|
+
### The Spectrum of Offline Support
|
|
14
|
+
|
|
15
|
+
Offline capability is not binary. There is a spectrum of approaches, each with different tradeoffs, and conflating them is the root of most confusion in this space.
|
|
16
|
+
|
|
17
|
+
**Online-with-cache** — The app is designed for online use. A cache (in-memory, disk, or HTTP cache) stores recent responses so that if the network disappears briefly, the user sees stale data instead of a blank screen. The cache is a performance optimization, not a design principle. Writes fail when offline. Examples: most REST-consuming mobile apps with HTTP caching enabled, apps that show "last loaded 5 minutes ago" banners.
|
|
18
|
+
|
|
19
|
+
**Offline-capable** — The app is designed for online use but has explicit provisions for offline scenarios. A local database stores a working subset of data. Writes are queued when offline and replayed when connectivity returns. The local store is not the source of truth — the server is. The local store is a staging area. Conflict resolution is typically simple (last-write-wins or server-wins). Examples: Gmail offline mode, Slack's message queue, most enterprise mobile apps.
|
|
20
|
+
|
|
21
|
+
**Offline-first** — The app is designed to work without a network from day one. The local database is the primary source of truth for the user's device. The network is a synchronization mechanism, not a data source. Reads and writes always hit the local store first. Sync happens in the background when connectivity is available. The UI never shows a loading spinner for local data. Conflict resolution is a first-class architectural concern, not an edge case. Examples: Notion (post-2024 offline rewrite), Linear, Apple Notes, Things 3.
|
|
22
|
+
|
|
23
|
+
**Local-first** — A philosophical extension of offline-first articulated by Ink & Switch in their 2019 essay "Local-first software: you own your data, in spite of the cloud." Local-first adds ownership and longevity guarantees: the user's data is stored in open formats on their device, collaboration happens via CRDTs or similar mechanisms, and the software's death does not mean the data's death. The seven ideals defined by Ink & Switch are: no spinners (instant reads/writes against local DB), data is not trapped (exportable, open formats), network is optional (full functionality offline), seamless collaboration (real-time co-editing when online), long-term preservation (data survives the software), security and privacy (encryption at rest on client), and user control (you decide what syncs and when). Examples: Obsidian, Excalidraw, Linear, apps built on Automerge or Yjs.
|
|
24
|
+
|
|
25
|
+
### Core Mechanisms
|
|
26
|
+
|
|
27
|
+
**Local storage as primary.** Every read and write operation hits the local database first. The UI renders from local state. There is no "loading" state for local data — it is always available, always fast. The local database is SQLite, Isar, Hive, Realm, IndexedDB, or a similar embedded store. The choice matters — see the Technology Landscape section.
|
|
28
|
+
|
|
29
|
+
**Sync queue.** Writes made while offline (or while online, for optimistic UI) are recorded in a persistent queue. Each entry captures the operation type, the entity, the payload, a timestamp, and a unique operation ID. When connectivity returns, the queue is drained in order. Failed syncs are retried with exponential backoff. The queue must survive app restarts — it is persisted alongside the local database.
|
|
30
|
+
|
|
31
|
+
**Conflict resolution.** When two devices (or a device and the server) modify the same entity independently, the system must decide what the final state is. This is the hardest problem in offline-first architecture by a wide margin. Strategies range from trivially simple (last-write-wins) to mathematically guaranteed (CRDTs). The right choice depends on the domain. See "How It Works" for a detailed breakdown.
|
|
32
|
+
|
|
33
|
+
**Optimistic UI.** The user interface reflects local changes immediately, before sync confirms them. The user taps "complete task" and sees it completed instantly. If sync later fails or reveals a conflict, the UI updates to reflect the resolved state. This requires careful design: the user must understand that what they see is the local state, and that corrections may occur. Poorly implemented optimistic UI creates a sense of unreliability when the UI "jumps" after sync.
|
|
34
|
+
|
|
35
|
+
**Connectivity awareness.** The app monitors network state and adjusts behavior accordingly. Online: sync immediately, prefetch data, resolve pending conflicts. Offline: queue writes, serve from local store, suppress features that require the network (e.g., sharing, invitations). Transitioning: drain the sync queue, reconcile state, update UI with any server-side changes.
|
|
36
|
+
|
|
37
|
+
### What It Is Not
|
|
38
|
+
|
|
39
|
+
**Not a caching strategy.** Caching is a performance optimization that stores copies of server data. Offline-first is an architectural paradigm where the local store is the authority. The distinction matters: a cache can be invalidated and rebuilt from the server. An offline-first local store contains data that may not exist anywhere else until sync completes.
|
|
40
|
+
|
|
41
|
+
**Not just "save to local DB."** Storing data locally is necessary but insufficient. Without a sync queue, conflict resolution, connectivity awareness, and careful state management, you have offline storage, not offline-first architecture.
|
|
42
|
+
|
|
43
|
+
**Not free of server-side concerns.** The server still matters. It is the reconciliation point, the backup, the collaboration hub, and the access control authority. Offline-first shifts complexity from "how do I make the server respond fast" to "how do I reconcile divergent states."
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## When to Use It
|
|
48
|
+
|
|
49
|
+
### The Qualifying Conditions
|
|
50
|
+
|
|
51
|
+
Apply offline-first architecture when **two or more** of these conditions hold:
|
|
52
|
+
|
|
53
|
+
**Users operate in unreliable network environments.** Field service workers, construction site inspectors, healthcare professionals in rural clinics, delivery drivers, agricultural workers — anyone who routinely enters areas with no cellular coverage or unreliable Wi-Fi. An online-only app is a non-functional app for these users. A 2024 study found that globally, 37% of mobile usage occurs in environments with intermittent or absent connectivity.
|
|
54
|
+
|
|
55
|
+
**Instant response time is a hard requirement.** Users expect sub-100ms response to every interaction. Network round-trips — even on fast connections — add 50-300ms of latency. Offline-first eliminates this entirely for reads and writes against local data. Linear's IndexedDB-first architecture achieves consistent sub-100ms interaction times because every operation hits local storage before the network.
|
|
56
|
+
|
|
57
|
+
**Data must not be lost due to connectivity failure.** A field inspector completes a 30-minute inspection form, walks to a basement with no signal, and taps "submit." If that data is lost, the inspection must be repeated. Offline-first guarantees that writes are persisted locally first and synced later. The sync queue ensures no operation is lost.
|
|
58
|
+
|
|
59
|
+
**The app is used across multiple devices.** A user edits a document on their laptop, closes it, and opens it on their phone. Both devices have local copies. Offline-first with proper sync ensures both devices converge to the same state. Without offline-first, one device's changes may overwrite the other's.
|
|
60
|
+
|
|
61
|
+
**Users work in long sessions without connectivity.** A researcher on a ship, a geologist in the field, a pilot on a long-haul flight. These users need full app functionality for hours or days without connectivity, then sync when they return.
|
|
62
|
+
|
|
63
|
+
### Real-World Examples
|
|
64
|
+
|
|
65
|
+
**Notion's offline rewrite (2024).** Notion spent years building offline support because their data model — a graph of interconnected blocks rather than simple page documents — made offline exceptionally complex. Their solution uses SQLite to cache records locally, a push-based update system wired into their page version snapshot mechanism, and a forest of offline page trees that track why each page is kept available offline. Each client tracks a `lastDownloadedTimestamp` per page, and on reconnect, only pages where the server version is newer are fetched. This is a textbook example of how online-first apps can be retrofitted with offline support, and how painful that retrofit is — it took years.
|
|
66
|
+
|
|
67
|
+
**Linear's local-first architecture.** Linear stores all data in IndexedDB on the client. The UI renders from local state. Changes are synced to the server via a custom sync protocol. The result is an app that feels instantaneous regardless of network conditions. Linear's architecture influenced the broader local-first movement and demonstrated that complex project management tools could work offline without sacrificing collaboration.
|
|
68
|
+
|
|
69
|
+
**Google Docs offline mode.** Google Docs uses a combination of Service Workers and IndexedDB to enable offline editing. Changes made offline are stored as operations (operational transformation) and replayed when connectivity returns. The conflict resolution uses operational transformation (OT) rather than CRDTs, which requires a central server to linearize operations — making it offline-capable rather than truly offline-first.
|
|
70
|
+
|
|
71
|
+
**Apple Notes and Reminders.** Apple's built-in apps are fully offline-first. Data is stored in a local SQLite database. iCloud sync happens in the background using CloudKit. Conflict resolution uses a combination of last-write-wins for simple fields and field-level merging for structured data. The user never sees a loading spinner for their own notes.
|
|
72
|
+
|
|
73
|
+
**Field service applications.** Apps like ServiceMax, Salesforce Field Service, and custom-built inspection tools are canonical offline-first use cases. A field technician downloads their work orders before leaving the office, completes inspections in areas with no connectivity, captures photos and signatures, and syncs everything when they return to coverage. The sync queue for these apps can contain hundreds of operations accumulated over an 8-hour shift.
|
|
74
|
+
|
|
75
|
+
**PouchDB/CouchDB ecosystem.** PouchDB (a JavaScript clone of CouchDB) popularized offline-first in web apps. The bidirectional replication protocol between PouchDB (client) and CouchDB (server) handles sync automatically: continuous, live replication propagates changes as they occur, and the retry option handles network failures gracefully. Healthcare apps, logistics tools, and humanitarian aid apps in developing regions have used this stack extensively.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## When NOT to Use It
|
|
80
|
+
|
|
81
|
+
This section is equally important. Offline-first architecture introduces substantial complexity that is unjustified for many applications. Most teams underestimate the cost of conflict resolution by a factor of 10.
|
|
82
|
+
|
|
83
|
+
### The Disqualifying Conditions
|
|
84
|
+
|
|
85
|
+
**Real-time collaborative editing without CRDTs is nearly impossible offline.** If your app requires multiple users to simultaneously edit the same document or entity in real-time, and you are not using CRDTs or Operational Transformation, offline-first will produce irreconcilable conflicts. Two users editing the same paragraph offline will produce two divergent versions that no simple merge strategy can reconcile without data loss. Even with CRDTs, real-time collaborative text editing is a deeply specialized problem — Yjs and Automerge exist precisely because building this from scratch takes years. If you are building a collaborative editor, use an existing CRDT library or do not go offline-first.
|
|
86
|
+
|
|
87
|
+
**Financial transactions requiring strong consistency.** A payment processor cannot optimistically accept a payment offline and hope it succeeds later. Double-spending, insufficient funds, and fraud detection all require server-side validation before the transaction is confirmed. Banking apps can show cached balances offline, but they cannot process transfers. Offline-first is wrong for the write path of financial transactions. Showing stale read data with clear "as of" timestamps is acceptable; executing financial operations offline is not.
|
|
88
|
+
|
|
89
|
+
**Applications with tiny datasets that change infrequently.** A settings screen with 5 toggles does not need offline-first architecture. A simple HTTP cache or SharedPreferences/UserDefaults store is sufficient. If the entire dataset fits in a single API response and changes once a week, the machinery of sync queues, conflict resolution, and connectivity monitoring is pure overhead.
|
|
90
|
+
|
|
91
|
+
**Apps where server-side validation is the core value.** A fraud detection dashboard, a real-time stock trading platform, a live auction system — these apps exist to show the latest server-side state. Showing stale data is worse than showing nothing. Offline-first would display data that is not just stale but actively misleading.
|
|
92
|
+
|
|
93
|
+
**Applications where data sovereignty prevents local storage.** Some healthcare, government, and financial applications are subject to regulations that prohibit storing sensitive data on user devices. HIPAA, GDPR right-to-deletion, and certain defense regulations may conflict with local-first data storage. If the data cannot legally reside on the device, offline-first is not an option without additional encryption and compliance infrastructure.
|
|
94
|
+
|
|
95
|
+
**Teams without the engineering capacity for conflict resolution.** This is the most honest disqualifying condition. Conflict resolution is hard. Not "we'll figure it out" hard — "this is a research-level problem that PhD theses are written about" hard. Cinapse, a note-taking app, publicly documented why they moved away from CRDTs for sync: the complexity of handling edge cases, the difficulty of debugging merge behavior, and the cognitive overhead of reasoning about concurrent state were unsustainable for their team size. If your team cannot dedicate sustained engineering effort to conflict resolution, use an offline-capable approach (server-wins, simple queue) rather than a full offline-first architecture.
|
|
96
|
+
|
|
97
|
+
**Prototypes and MVPs.** The complexity overhead of offline-first can triple development time for early-stage products. Validate the product idea online-first, then add offline support when the product-market fit is proven and the offline use case is validated with real users.
|
|
98
|
+
|
|
99
|
+
### The Complexity Tax
|
|
100
|
+
|
|
101
|
+
Teams consistently underestimate the ongoing cost of offline-first:
|
|
102
|
+
|
|
103
|
+
- **Testing is exponentially harder.** You must test every feature in online mode, offline mode, transitioning-to-offline mode, transitioning-to-online mode, and conflicting-edits mode. The test matrix grows multiplicatively.
|
|
104
|
+
- **Debugging is non-local.** A bug may only manifest when device A edits offline, device B edits online, device A comes back online, and the merge produces an unexpected state. Reproducing this requires multi-device orchestration.
|
|
105
|
+
- **Schema migrations affect both server and client.** Adding a field requires migrating the server database, the client database, the sync protocol, and the conflict resolution logic. A single schema change touches four subsystems.
|
|
106
|
+
- **User expectations are hard to set.** Users do not naturally understand that their device may show a different state than another device. "I edited this on my phone but my laptop shows the old version" is a support ticket that no amount of UI design fully prevents.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## How It Works
|
|
111
|
+
|
|
112
|
+
### Architecture Overview
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
┌─────────────────────────────────────────────────┐
|
|
116
|
+
│ UI Layer │
|
|
117
|
+
│ (renders from local state only) │
|
|
118
|
+
└──────────────────────┬──────────────────────────┘
|
|
119
|
+
│ reads / writes
|
|
120
|
+
▼
|
|
121
|
+
┌─────────────────────────────────────────────────┐
|
|
122
|
+
│ Repository Layer │
|
|
123
|
+
│ (abstracts local + remote data sources) │
|
|
124
|
+
└────────┬─────────────────────────────┬──────────┘
|
|
125
|
+
│ │
|
|
126
|
+
▼ ▼
|
|
127
|
+
┌─────────────────┐ ┌─────────────────────┐
|
|
128
|
+
│ Local Database │ │ Sync Engine │
|
|
129
|
+
│ (SQLite/Isar/ │ │ ┌───────────────┐ │
|
|
130
|
+
│ Hive/IndexedDB)│ │ │ Sync Queue │ │
|
|
131
|
+
│ │◄────────►│ │ (persistent) │ │
|
|
132
|
+
│ Source of truth │ │ └───────────────┘ │
|
|
133
|
+
│ for this device │ │ ┌───────────────┐ │
|
|
134
|
+
│ │ │ │ Conflict │ │
|
|
135
|
+
│ │ │ │ Resolver │ │
|
|
136
|
+
│ │ │ └───────────────┘ │
|
|
137
|
+
│ │ │ ┌───────────────┐ │
|
|
138
|
+
│ │ │ │ Connectivity │ │
|
|
139
|
+
│ │ │ │ Monitor │ │
|
|
140
|
+
│ │ │ └───────────────┘ │
|
|
141
|
+
└─────────────────┘ └──────────┬──────────┘
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
┌─────────────────────┐
|
|
145
|
+
│ Remote Server │
|
|
146
|
+
│ (API + Database) │
|
|
147
|
+
│ Central truth for │
|
|
148
|
+
│ reconciliation │
|
|
149
|
+
└─────────────────────┘
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Local Storage Layer
|
|
153
|
+
|
|
154
|
+
The local database is the foundation. Every read and write hits this store first. The choice of database technology depends on the platform and data complexity.
|
|
155
|
+
|
|
156
|
+
**SQLite** is the most battle-tested option. It handles relational data, complex queries, transactions, and large datasets. On mobile, it is the default for structured data (via `sqflite` on Flutter, Room on Android, Core Data on iOS). On web, it runs via WebAssembly (sql.js, wa-sqlite). Drift (formerly Moor) provides a type-safe Dart wrapper for SQLite on Flutter with reactive streams, migrations, and compile-time query verification.
|
|
157
|
+
|
|
158
|
+
**Isar** is a high-performance NoSQL database for Flutter. It supports complex queries, indexes, full-text search, and relationships. Writing 1000 objects in a batch takes approximately 8ms. Isar is ideal for apps with complex query patterns that do not need relational integrity. Note: as of 2025, Isar's maintenance status should be verified before adoption in new projects.
|
|
159
|
+
|
|
160
|
+
**Hive** is a lightweight pure-Dart key-value store. It is fast for simple reads/writes, has minimal setup, and works across all Flutter platforms. It is not suitable for complex queries, relationships, or large datasets. Use Hive for user preferences, session data, and cached API responses — not as the primary store for a complex offline-first app.
|
|
161
|
+
|
|
162
|
+
**IndexedDB** is the standard for web-based offline-first apps. Linear, Notion, and Figma all use IndexedDB as their local store. It is a NoSQL object store with indexes, transactions, and large storage capacity (typically hundreds of MB to GB depending on browser). The API is callback-based and awkward; libraries like Dexie.js provide a cleaner interface.
|
|
163
|
+
|
|
164
|
+
**A hybrid approach** is common in production: use SQLite/Drift for structured business data and Hive for lightweight caches and preferences. This avoids forcing all data through a single storage paradigm.
|
|
165
|
+
|
|
166
|
+
### Sync Queue
|
|
167
|
+
|
|
168
|
+
The sync queue is the mechanism that ensures no write is lost. Every mutation (create, update, delete) is recorded in the queue before being applied to the local database.
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
┌──────────────────────────────────────────────────────┐
|
|
172
|
+
│ Sync Queue Entry │
|
|
173
|
+
├──────────────────────────────────────────────────────┤
|
|
174
|
+
│ id: UUID (unique per operation) │
|
|
175
|
+
│ entity_type: "task" | "document" | "inspection" │
|
|
176
|
+
│ entity_id: UUID of the affected entity │
|
|
177
|
+
│ operation: "create" | "update" | "delete" │
|
|
178
|
+
│ payload: JSON of the changed fields │
|
|
179
|
+
│ timestamp: ISO 8601 with device clock │
|
|
180
|
+
│ logical_clock: Lamport timestamp or vector clock │
|
|
181
|
+
│ retry_count: Number of failed sync attempts │
|
|
182
|
+
│ status: "pending" | "syncing" | "failed" │
|
|
183
|
+
│ device_id: UUID identifying this device │
|
|
184
|
+
└──────────────────────────────────────────────────────┘
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Queue processing rules:**
|
|
188
|
+
|
|
189
|
+
1. Operations are processed in FIFO order per entity. Global ordering is not required — operations on unrelated entities can sync in parallel.
|
|
190
|
+
2. Failed operations are retried with exponential backoff: 1s, 2s, 4s, 8s, up to a maximum of 5 minutes.
|
|
191
|
+
3. After N consecutive failures (typically 5-10), the operation is moved to a "dead letter" queue for manual resolution or user notification.
|
|
192
|
+
4. The queue must be durable — persisted in the same local database as the application data, within a transaction. If the app crashes after a write but before queuing the sync, the write and the queue entry must both roll back.
|
|
193
|
+
5. Compaction: if the same entity is updated multiple times while offline, the queue can compact intermediate updates into a single operation. This reduces sync traffic but loses the intermediate history. Whether to compact depends on whether the server needs the full operation history.
|
|
194
|
+
|
|
195
|
+
### Conflict Resolution Strategies
|
|
196
|
+
|
|
197
|
+
This is the core intellectual challenge of offline-first. When two devices modify the same entity independently, the system must produce a deterministic, sensible result.
|
|
198
|
+
|
|
199
|
+
**Last-Write-Wins (LWW).** The operation with the latest timestamp wins. Simple to implement, easy to reason about, and acceptable for many use cases. The critical weakness: it silently discards data. If device A sets a task title to "Fix bug" and device B sets it to "Fix critical bug," LWW keeps one and discards the other without telling anyone. LWW requires synchronized clocks — if device A's clock is 5 minutes ahead, its writes always win regardless of actual chronology. Use LWW for non-critical fields where the latest value is genuinely the "right" one: user preferences, status fields, toggles.
|
|
200
|
+
|
|
201
|
+
**Server-Wins.** The server's version always takes precedence. Client changes are proposals, not facts. Simple, predictable, and appropriate when the server has more context (e.g., it has validated the data, applied business rules, or received input from an authoritative source). The downside: the user's local changes may be silently overwritten after sync, which feels like data loss.
|
|
202
|
+
|
|
203
|
+
**Client-Wins.** The local change always takes precedence. The server accepts whatever the client sends. Simple, but dangerous in multi-device scenarios — the last device to sync wins, which may not be the device with the most recent intentional edit.
|
|
204
|
+
|
|
205
|
+
**Field-Level Merge.** Instead of treating the entire entity as the unit of conflict, merge at the field level. If device A changes the title and device B changes the description, keep both changes. Conflicts only arise when two devices change the same field. This is substantially better than entity-level LWW and is the sweet spot for most business applications. Implementation: track changed fields per operation in the sync queue, and merge non-conflicting field changes on the server.
|
|
206
|
+
|
|
207
|
+
**Three-Way Merge.** Compare both versions against the common ancestor (the last synced state). Changes relative to the ancestor are identified for each device, and non-conflicting changes are merged automatically. Conflicting changes (same field changed differently relative to ancestor) are flagged for manual resolution or LWW fallback. This is how Git merge works, and it is effective for structured data.
|
|
208
|
+
|
|
209
|
+
**CRDTs (Conflict-Free Replicated Data Types).** CRDTs are data structures that are mathematically guaranteed to converge to the same state when all operations are applied, regardless of the order in which they are received. No central coordinator is required. Operations can be applied offline, concurrently, out of order — convergence is guaranteed by the data structure's algebraic properties.
|
|
210
|
+
|
|
211
|
+
Key CRDT types:
|
|
212
|
+
- **G-Counter** (Grow-only counter): Each node maintains its own counter; the merged value is the sum of all nodes. Used for: view counts, like counts.
|
|
213
|
+
- **PN-Counter** (Positive-Negative counter): Two G-Counters — one for increments, one for decrements. The value is the difference. Used for: inventory counts, vote tallies.
|
|
214
|
+
- **LWW-Register**: A single value with a timestamp. Last write wins. The simplest CRDT — effectively LWW with formal guarantees.
|
|
215
|
+
- **OR-Set** (Observed-Remove Set): Elements can be added and removed. Concurrent add and remove of the same element resolves in favor of the add (add-wins semantics). Used for: tag sets, member lists.
|
|
216
|
+
- **RGA** (Replicated Growable Array): An ordered sequence that supports concurrent inserts and deletes. Used by Automerge for text and list data.
|
|
217
|
+
- **YATA** (Yet Another Transformation Approach): Yjs's algorithm for concurrent text editing. Each peer maintains its own monotonically incrementing counter. More performant than RGA for large documents.
|
|
218
|
+
|
|
219
|
+
CRDT tradeoffs: guaranteed convergence, no coordination required, works offline and in peer-to-peer scenarios. But: increased memory usage (metadata overhead), eventual consistency only (no strong consistency), limited data model expressiveness (not everything maps naturally to a CRDT), and debugging merged state is notoriously difficult.
|
|
220
|
+
|
|
221
|
+
**Manual resolution.** Present conflicting versions to the user and let them choose. This is the "nuclear option" — it always produces the correct result (the user decides) but destroys the seamless experience. Use manual resolution as the fallback when automated strategies cannot produce a sensible result, typically for high-value entities like documents or contracts.
|
|
222
|
+
|
|
223
|
+
### Sync Protocols
|
|
224
|
+
|
|
225
|
+
**Full sync** downloads the entire dataset on every sync. Simple, but scales poorly. Acceptable for small datasets (< 1000 entities, < 1MB total) or initial sync after a fresh install.
|
|
226
|
+
|
|
227
|
+
**Delta sync** only transfers changes since the last sync point. The client sends its last sync timestamp or version token; the server returns only entities modified after that point. This is the standard approach for production offline-first apps. Implementation requires the server to track modification timestamps or version numbers for every entity, and to support a "changes since" query pattern. AWS AppSync's Delta Sync is a well-documented implementation of this pattern.
|
|
228
|
+
|
|
229
|
+
**Version vectors** extend delta sync to multi-device scenarios. Instead of a single timestamp, each device maintains a vector of `{device_id: last_known_version}` pairs. When syncing, the device sends its version vector; the server computes the set difference and returns only the operations the device has not seen. Version vectors correctly handle the case where device A syncs with the server, then device B syncs, then device A syncs again — each device receives exactly the updates it missed.
|
|
230
|
+
|
|
231
|
+
**Operation-based sync (event sourcing)** transmits the operations themselves rather than the resulting state. The client sends "user changed title from X to Y" rather than "the current title is Y." The server applies operations in causal order to reconstruct the state. This preserves the full history of changes and enables sophisticated conflict resolution (e.g., rebasing operations on top of concurrent changes). This is how Google Docs (via OT), Figma, and Linear handle sync. The tradeoff is increased complexity and storage for the operation log.
|
|
232
|
+
|
|
233
|
+
**CouchDB replication protocol** is a purpose-built sync protocol for offline-first. PouchDB implements it in JavaScript. The protocol handles bidirectional, continuous, incremental replication between a local and remote database. It tracks changes via a sequence number, transfers only modified documents, and includes built-in conflict detection (though resolution is left to the application). It is the most mature, battle-tested offline sync protocol available.
|
|
234
|
+
|
|
235
|
+
### Connectivity Monitoring
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
enum ConnectivityState {
|
|
239
|
+
online, // Full connectivity, sync active
|
|
240
|
+
degraded, // Connectivity present but slow/unreliable
|
|
241
|
+
offline, // No connectivity, queuing all writes
|
|
242
|
+
syncing, // Transitioning: draining sync queue
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The app must react to connectivity changes:
|
|
247
|
+
|
|
248
|
+
- **Online -> Offline:** Stop sync attempts, begin queuing writes, notify the user (subtly — a small banner, not a modal).
|
|
249
|
+
- **Offline -> Online:** Begin draining the sync queue, fetch server-side changes, reconcile state, update UI reactively.
|
|
250
|
+
- **Degraded:** Reduce sync frequency, increase timeouts, prioritize critical operations (creates over updates).
|
|
251
|
+
|
|
252
|
+
Platform-specific connectivity APIs: `connectivity_plus` on Flutter, `ConnectivityManager` on Android, `NWPathMonitor` on iOS, `navigator.onLine` + Service Worker on web. None of these are fully reliable — `navigator.onLine` only detects whether the device has a network interface, not whether it can reach the server. Production apps should supplement platform APIs with periodic server pings.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Trade-Offs Matrix
|
|
257
|
+
|
|
258
|
+
| Dimension | Offline-First Benefit | Offline-First Cost |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| **Response time** | Sub-100ms for all local operations. No spinners for reads/writes. | Initial sync can be slow for large datasets (seconds to minutes). |
|
|
261
|
+
| **Data availability** | Full functionality without network. Users are never blocked. | Local storage limits (mobile typically 50-500MB practical). Stale data risk. |
|
|
262
|
+
| **User experience** | Instant, responsive, feels "native." No network-dependent loading states. | Optimistic UI can "jump" when sync corrects local state. Confusing for users. |
|
|
263
|
+
| **Data integrity** | Writes never lost — persisted locally before sync. | Conflict resolution can silently discard data (LWW) or produce unexpected merges. |
|
|
264
|
+
| **Development cost** | Forces clean data layer separation. Repository pattern is well-structured. | 2-5x development time vs. online-only. Sync engine is a product unto itself. |
|
|
265
|
+
| **Testing burden** | Local-only tests are fast and deterministic. | Must test online/offline/transitioning/conflicting states. Test matrix explodes. |
|
|
266
|
+
| **Server simplicity** | Server can be simpler (no real-time requirements if sync is batch). | Server must support delta queries, conflict detection, version tracking. |
|
|
267
|
+
| **Multi-device sync** | Seamless cross-device experience when working. | Conflicts between devices are the hardest class of bugs to debug. |
|
|
268
|
+
| **Scalability** | Reduced server load — clients self-serve from local store. | Each client carries storage and compute overhead. Migration affects all clients. |
|
|
269
|
+
| **Schema evolution** | N/A | Schema changes require coordinated migration of server DB, client DB, sync protocol, and conflict resolution logic. |
|
|
270
|
+
| **Onboarding** | N/A | New developers must understand the sync engine, conflict resolution, and dual-database architecture. Steep learning curve. |
|
|
271
|
+
| **Operational cost** | Fewer API calls reduce server infrastructure cost. | Client-side bugs are harder to diagnose remotely. Sync failures require per-device investigation. |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Evolution Path
|
|
276
|
+
|
|
277
|
+
### Level 0: Online-Only
|
|
278
|
+
|
|
279
|
+
The app requires network for all operations. No local persistence beyond ephemeral in-memory state.
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
UI → API → Server DB
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Move to Level 1 when: users complain about loading times, the app is unusable on slow networks, or basic data should be viewable without waiting.
|
|
286
|
+
|
|
287
|
+
### Level 1: Cache Layer
|
|
288
|
+
|
|
289
|
+
Add HTTP caching or a simple local cache. Reads can be served from cache when the network is unavailable. Writes still require the network.
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
UI → Cache? → API → Server DB
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Technologies: HTTP cache headers, `shared_preferences`, simple key-value stores.
|
|
296
|
+
Move to Level 2 when: users need to perform writes offline, or the cache invalidation logic becomes complex.
|
|
297
|
+
|
|
298
|
+
### Level 2: Offline-Capable (Queue + Simple Sync)
|
|
299
|
+
|
|
300
|
+
Add a local database and a sync queue. Reads and writes hit the local store. Writes are queued and synced when online. Conflict resolution is server-wins or LWW.
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
UI → Repository → Local DB ←→ Sync Queue ←→ API → Server DB
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Technologies: SQLite/Drift, simple REST-based sync, timestamp-based delta queries.
|
|
307
|
+
Move to Level 3 when: multi-device conflicts are causing data loss, users need guaranteed convergence, or the app requires real-time collaboration.
|
|
308
|
+
|
|
309
|
+
### Level 3: Offline-First (Full Sync Engine)
|
|
310
|
+
|
|
311
|
+
A dedicated sync engine handles bidirectional synchronization, field-level conflict resolution, version vectors, and optimistic UI with rollback. The local database is the device's source of truth.
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
UI → Repository → Local DB ←→ Sync Engine (queue + resolver + monitor) ←→ API → Server DB
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Technologies: Custom sync engine or PowerSync/ElectricSQL, field-level merge, three-way merge.
|
|
318
|
+
Move to Level 4 when: peer-to-peer sync is needed, the app requires guaranteed convergence without a central server, or the team wants to eliminate server-side conflict resolution entirely.
|
|
319
|
+
|
|
320
|
+
### Level 4: Local-First (CRDTs + P2P)
|
|
321
|
+
|
|
322
|
+
Data is stored in CRDT structures that merge automatically. Sync can happen peer-to-peer or via a server. No central authority is required for conflict resolution. The server is optional — it serves as a backup and relay, not an authority.
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
UI → CRDT Document → Local Store ←→ Sync (P2P / Server) ←→ Other Devices
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Technologies: Automerge, Yjs, Loro, custom CRDTs.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Failure Modes
|
|
333
|
+
|
|
334
|
+
These are the ways offline-first architectures break in production. Each is drawn from real incidents.
|
|
335
|
+
|
|
336
|
+
### Sync Conflicts Destroying Data
|
|
337
|
+
|
|
338
|
+
**The scenario:** Two users edit the same entity offline. User A changes the title to "Q4 Report." User B changes the title to "Q4 Report — FINAL." Both sync. LWW keeps one title and silently discards the other. Neither user is notified. User A's title disappears.
|
|
339
|
+
|
|
340
|
+
**Why it happens:** LWW is the default because it is easy to implement. Teams ship it intending to "improve later" and never do.
|
|
341
|
+
|
|
342
|
+
**Mitigation:** Use field-level merge at minimum. Log all conflict resolutions with both versions. Provide a conflict history UI for high-value entities. Alert users when their changes were overridden by a sync.
|
|
343
|
+
|
|
344
|
+
### Storage Limits Exceeded
|
|
345
|
+
|
|
346
|
+
**The scenario:** A field worker's app downloads inspection templates, photos, and historical data. After two weeks of offline use, the local database exceeds the device's storage allocation. The app crashes. Queued sync operations are lost if the queue was in the same database that exceeded its limit.
|
|
347
|
+
|
|
348
|
+
**Why it happens:** Mobile storage is finite. iOS aggressively reclaims storage from apps under pressure. Browser storage (IndexedDB) has quotas that vary by browser and can be evicted without warning in some contexts (Safari private browsing).
|
|
349
|
+
|
|
350
|
+
**Mitigation:** Implement storage budgets with LRU eviction for non-essential data. Separate the sync queue from the main data store so that sync operations survive data eviction. Monitor storage usage and warn users before limits are reached. Compress stored data. Implement a tiered storage strategy: keep critical data (sync queue, active entities) in guaranteed storage; keep historical data in evictable cache.
|
|
351
|
+
|
|
352
|
+
### Stale Data Displayed as Current
|
|
353
|
+
|
|
354
|
+
**The scenario:** A user checks inventory levels offline. The display shows 50 units available — but another user sold 40 units while the first user was offline. The first user promises a customer 45 units. The inventory is actually 10.
|
|
355
|
+
|
|
356
|
+
**Why it happens:** Offline-first shows local state, which may be arbitrarily stale. The staleness is invisible unless the UI explicitly communicates it.
|
|
357
|
+
|
|
358
|
+
**Mitigation:** Always display "last synced" timestamps on data that may be stale. For critical data (inventory, pricing, availability), add prominent staleness warnings when data is older than a threshold. Consider making some data read-only when offline to prevent decisions based on stale information.
|
|
359
|
+
|
|
360
|
+
### Sync Queue Growing Unbounded
|
|
361
|
+
|
|
362
|
+
**The scenario:** A user works offline for a week. The sync queue accumulates 10,000 operations. When connectivity returns, draining the queue takes 30 minutes, during which the app is sluggish. Some operations fail because the server-side entities they reference were deleted by another user. The failure handling for 500 operations blocks the queue.
|
|
363
|
+
|
|
364
|
+
**Why it happens:** Sync queues are designed for minutes of offline use, not days. Queue processing is typically sequential and blocking.
|
|
365
|
+
|
|
366
|
+
**Mitigation:** Implement queue compaction (collapse multiple updates to the same entity into one). Process the queue in parallel for independent entities. Set queue size limits with user warnings. Implement "soft delete" on the server so that references to deleted entities can be resolved gracefully. Add progress UI for long sync operations.
|
|
367
|
+
|
|
368
|
+
### Clock Skew Breaking LWW
|
|
369
|
+
|
|
370
|
+
**The scenario:** Device A's clock is set 10 minutes into the future (common on devices with manual clock settings or poor NTP sync). Every edit from device A appears to be 10 minutes "newer" than edits from device B, even when device B's edit was made later in real time. Device A's edits always win in LWW resolution.
|
|
371
|
+
|
|
372
|
+
**Why it happens:** LWW depends on synchronized clocks. Device clocks are not reliably synchronized.
|
|
373
|
+
|
|
374
|
+
**Mitigation:** Use logical clocks (Lamport timestamps or vector clocks) instead of wall-clock time. If wall-clock time is used, include a server-assigned timestamp as a tiebreaker. Hybrid Logical Clocks (HLCs) combine wall-clock time with logical counters to provide causally consistent ordering even with clock skew.
|
|
375
|
+
|
|
376
|
+
### Partial Sync Corruption
|
|
377
|
+
|
|
378
|
+
**The scenario:** The sync process is interrupted (app backgrounded, network drops mid-request). Half of the pending operations were synced; half were not. The server has a partially updated state. The client retries, but some operations are now duplicates.
|
|
379
|
+
|
|
380
|
+
**Why it happens:** Sync operations are not atomic across client and server.
|
|
381
|
+
|
|
382
|
+
**Mitigation:** Make every sync operation idempotent using unique operation IDs. The server must check whether an operation ID has already been processed before applying it. Use transactional batches when possible — sync a group of related operations atomically. Implement a sync checkpoint that both client and server agree on, so that interrupted syncs can resume from the last checkpoint.
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Technology Landscape
|
|
387
|
+
|
|
388
|
+
### Local Databases
|
|
389
|
+
|
|
390
|
+
| Technology | Platform | Type | Query Capability | Best For |
|
|
391
|
+
|---|---|---|---|---|
|
|
392
|
+
| **SQLite** (via sqflite/Drift) | Flutter, Android, iOS, Web (WASM) | Relational | Full SQL, joins, transactions | Complex relational data, large datasets |
|
|
393
|
+
| **Isar** | Flutter | NoSQL | Indexes, composite queries, full-text search | High-performance Flutter apps with complex queries |
|
|
394
|
+
| **Hive** | Flutter (pure Dart) | Key-Value | Basic key lookup | Preferences, caches, simple data |
|
|
395
|
+
| **Realm** | Android, iOS, React Native, Flutter | Object DB | Object queries, relationships | Cross-platform apps needing built-in sync (MongoDB Atlas) |
|
|
396
|
+
| **IndexedDB** | Web | NoSQL Object Store | Indexes, key ranges, cursors | Web offline-first (Linear, Notion, Figma) |
|
|
397
|
+
| **WatermelonDB** | React Native | Relational (SQLite-backed) | Reactive queries, lazy loading | Large React Native apps with 10K+ records |
|
|
398
|
+
| **ObjectBox** | Flutter, Android, iOS | Object DB | Dart/Kotlin/Swift-native queries | Edge/IoT apps, very fast CRUD |
|
|
399
|
+
|
|
400
|
+
### Sync Frameworks
|
|
401
|
+
|
|
402
|
+
| Technology | Approach | Conflict Resolution | Platform | Maturity |
|
|
403
|
+
|---|---|---|---|---|
|
|
404
|
+
| **PowerSync** | Server-authoritative, SQLite on client | Server-wins with client rollback | Flutter, React Native, Web | Production (2024+) |
|
|
405
|
+
| **ElectricSQL** | Local-first, Postgres-backed | CRDT-inspired (finality of writes) | Web, React Native | Rebuilt in 2024, growing |
|
|
406
|
+
| **CouchDB/PouchDB** | Multi-master replication | Revision tree, app-defined resolution | Web, Node.js | Mature (10+ years) |
|
|
407
|
+
| **Realm Sync** (MongoDB Atlas) | Object-level sync | Field-level LWW | React Native, Flutter, native | Mature |
|
|
408
|
+
| **Firebase/Firestore** | Cloud-first with offline cache | Server-wins (last write) | Flutter, Web, native | Mature but not truly offline-first |
|
|
409
|
+
| **Supabase** | Postgres with real-time | No built-in offline sync (community solutions) | Web, Flutter | Limited offline support |
|
|
410
|
+
|
|
411
|
+
### CRDT Libraries
|
|
412
|
+
|
|
413
|
+
| Library | Language | Algorithm | Best For | Considerations |
|
|
414
|
+
|---|---|---|---|---|
|
|
415
|
+
| **Automerge** | JS/Rust (WASM) | RGA for sequences, LWW for registers | Document-based apps, JSON data | WASM memory limits for very large docs |
|
|
416
|
+
| **Yjs** | JavaScript | YATA for text, custom for other types | Collaborative text editing, real-time | Fastest for text; large community |
|
|
417
|
+
| **Loro** | Rust (WASM) | Fugue for text, custom for tree/list | Next-gen CRDT apps, rich data structures | Newer but promising (2024+) |
|
|
418
|
+
| **Synk** | Kotlin Multiplatform | Various | Kotlin/Android offline-first | Emerging, KMP-focused |
|
|
419
|
+
| **RxDB** | JavaScript | CRDT plugin + various backends | Reactive web/mobile apps | Combines CRDT with reactive queries |
|
|
420
|
+
| **diamond-types** | Rust | Fugue | High-performance text editing | Research-quality, very fast |
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Decision Tree
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
START: Does your app need to work without network?
|
|
428
|
+
│
|
|
429
|
+
├─ NO → Do users complain about latency?
|
|
430
|
+
│ ├─ NO → Stay online-only (Level 0)
|
|
431
|
+
│ └─ YES → Add a cache layer (Level 1)
|
|
432
|
+
│
|
|
433
|
+
└─ YES → Do users need to WRITE data offline?
|
|
434
|
+
│
|
|
435
|
+
├─ NO → Cache layer + stale data UI (Level 1)
|
|
436
|
+
│
|
|
437
|
+
└─ YES → How many users edit the same entity?
|
|
438
|
+
│
|
|
439
|
+
├─ ONE (single user, multiple devices)
|
|
440
|
+
│ → Do conflicts need per-field resolution?
|
|
441
|
+
│ ├─ NO → LWW + sync queue (Level 2)
|
|
442
|
+
│ └─ YES → Field-level merge + sync engine (Level 3)
|
|
443
|
+
│
|
|
444
|
+
└─ MULTIPLE (collaborative editing)
|
|
445
|
+
→ Is real-time co-editing required?
|
|
446
|
+
│
|
|
447
|
+
├─ NO → Field-level merge + sync engine (Level 3)
|
|
448
|
+
│ (resolve on next sync, not in real-time)
|
|
449
|
+
│
|
|
450
|
+
└─ YES → Does your team have CRDT expertise?
|
|
451
|
+
│
|
|
452
|
+
├─ NO → Use Yjs/Automerge (Level 4)
|
|
453
|
+
│ (do NOT build your own CRDT)
|
|
454
|
+
│
|
|
455
|
+
└─ YES → Custom CRDT or existing library
|
|
456
|
+
based on data model (Level 4)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Secondary decision: choosing a sync framework.**
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
What is your backend database?
|
|
463
|
+
│
|
|
464
|
+
├─ PostgreSQL → PowerSync or ElectricSQL
|
|
465
|
+
│ PowerSync: server-authoritative, schemaless client, wider platform support
|
|
466
|
+
│ ElectricSQL: local-first with write finality, Postgres-native
|
|
467
|
+
│
|
|
468
|
+
├─ CouchDB → PouchDB (built-in replication protocol)
|
|
469
|
+
│
|
|
470
|
+
├─ MongoDB → Realm Sync (Atlas Device Sync)
|
|
471
|
+
│
|
|
472
|
+
├─ Firebase/Firestore → Built-in offline (but server-authoritative, not truly offline-first)
|
|
473
|
+
│
|
|
474
|
+
└─ Custom backend → Build sync engine with:
|
|
475
|
+
- Delta sync (timestamp or version-based)
|
|
476
|
+
- Idempotent operations (UUID per operation)
|
|
477
|
+
- Conflict resolution (field-level merge recommended)
|
|
478
|
+
- Queue with exponential backoff
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Implementation Sketch
|
|
484
|
+
|
|
485
|
+
### Flutter: Local DB + Sync Queue + Conflict Resolver
|
|
486
|
+
|
|
487
|
+
This sketch demonstrates the core pattern using Drift (SQLite) for local storage. The pattern is portable to any platform and database.
|
|
488
|
+
|
|
489
|
+
```dart
|
|
490
|
+
// ── Local Database (Drift/SQLite) ──────────────────────────────
|
|
491
|
+
|
|
492
|
+
@DriftDatabase(tables: [Tasks, SyncQueue])
|
|
493
|
+
class AppDatabase extends _$AppDatabase {
|
|
494
|
+
AppDatabase(QueryExecutor e) : super(e);
|
|
495
|
+
|
|
496
|
+
@override
|
|
497
|
+
int get schemaVersion => 1;
|
|
498
|
+
|
|
499
|
+
// Read from local DB — always instant, never hits network
|
|
500
|
+
Stream<List<Task>> watchTasks() => select(tasks).watch();
|
|
501
|
+
|
|
502
|
+
Future<Task?> getTask(String id) =>
|
|
503
|
+
(select(tasks)..where((t) => t.id.equals(id))).getSingleOrNull();
|
|
504
|
+
|
|
505
|
+
// Write to local DB + enqueue sync operation (single transaction)
|
|
506
|
+
Future<void> upsertTaskWithSync(TaskCompanion task, String operationType) {
|
|
507
|
+
return transaction(() async {
|
|
508
|
+
await into(tasks).insertOnConflictUpdate(task);
|
|
509
|
+
await into(syncQueue).insert(SyncQueueCompanion(
|
|
510
|
+
id: Value(const Uuid().v4()),
|
|
511
|
+
entityType: const Value('task'),
|
|
512
|
+
entityId: task.id,
|
|
513
|
+
operation: Value(operationType),
|
|
514
|
+
payload: Value(jsonEncode(task.toJson())),
|
|
515
|
+
timestamp: Value(DateTime.now().toIso8601String()),
|
|
516
|
+
retryCount: const Value(0),
|
|
517
|
+
status: const Value('pending'),
|
|
518
|
+
));
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Get pending sync operations in FIFO order
|
|
523
|
+
Future<List<SyncQueueEntry>> getPendingOperations() =>
|
|
524
|
+
(select(syncQueue)
|
|
525
|
+
..where((s) => s.status.equals('pending'))
|
|
526
|
+
..orderBy([(s) => OrderingTerm.asc(s.timestamp)]))
|
|
527
|
+
.get();
|
|
528
|
+
|
|
529
|
+
// Mark operation as synced and remove from queue
|
|
530
|
+
Future<void> markSynced(String operationId) =>
|
|
531
|
+
(delete(syncQueue)..where((s) => s.id.equals(operationId))).go();
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
```dart
|
|
536
|
+
// ── Sync Engine ────────────────────────────────────────────────
|
|
537
|
+
|
|
538
|
+
class SyncEngine {
|
|
539
|
+
final AppDatabase _db;
|
|
540
|
+
final ApiClient _api;
|
|
541
|
+
final ConnectivityMonitor _connectivity;
|
|
542
|
+
Timer? _syncTimer;
|
|
543
|
+
|
|
544
|
+
SyncEngine(this._db, this._api, this._connectivity);
|
|
545
|
+
|
|
546
|
+
void start() {
|
|
547
|
+
// React to connectivity changes
|
|
548
|
+
_connectivity.onStatusChange.listen((status) {
|
|
549
|
+
if (status == ConnectivityState.online) {
|
|
550
|
+
_drainQueue();
|
|
551
|
+
_pullServerChanges();
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Periodic sync when online (every 30 seconds)
|
|
556
|
+
_syncTimer = Timer.periodic(
|
|
557
|
+
const Duration(seconds: 30),
|
|
558
|
+
(_) {
|
|
559
|
+
if (_connectivity.isOnline) {
|
|
560
|
+
_drainQueue();
|
|
561
|
+
_pullServerChanges();
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
Future<void> _drainQueue() async {
|
|
568
|
+
final pending = await _db.getPendingOperations();
|
|
569
|
+
|
|
570
|
+
for (final op in pending) {
|
|
571
|
+
try {
|
|
572
|
+
final serverResponse = await _api.sync(op);
|
|
573
|
+
|
|
574
|
+
if (serverResponse.hasConflict) {
|
|
575
|
+
await _resolveConflict(op, serverResponse.serverVersion);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
await _db.markSynced(op.id);
|
|
579
|
+
} on NetworkException {
|
|
580
|
+
// Will retry on next cycle — operation stays in queue
|
|
581
|
+
break; // Stop processing queue, network is down
|
|
582
|
+
} on ServerException catch (e) {
|
|
583
|
+
await _handleSyncFailure(op, e);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
Future<void> _pullServerChanges() async {
|
|
589
|
+
final lastSync = await _db.getLastSyncTimestamp();
|
|
590
|
+
final changes = await _api.getChangesSince(lastSync);
|
|
591
|
+
|
|
592
|
+
for (final change in changes) {
|
|
593
|
+
await _db.applyServerChange(change);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
await _db.setLastSyncTimestamp(DateTime.now());
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
void dispose() {
|
|
600
|
+
_syncTimer?.cancel();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
```dart
|
|
606
|
+
// ── Conflict Resolver ──────────────────────────────────────────
|
|
607
|
+
|
|
608
|
+
class ConflictResolver {
|
|
609
|
+
/// Field-level merge: compare local and server versions against
|
|
610
|
+
/// the common ancestor to determine non-conflicting changes.
|
|
611
|
+
TaskData resolve(TaskData ancestor, TaskData local, TaskData server) {
|
|
612
|
+
return TaskData(
|
|
613
|
+
id: local.id,
|
|
614
|
+
title: _mergeField(ancestor.title, local.title, server.title),
|
|
615
|
+
description: _mergeField(
|
|
616
|
+
ancestor.description, local.description, server.description),
|
|
617
|
+
status: _mergeField(ancestor.status, local.status, server.status),
|
|
618
|
+
updatedAt: DateTime.now(),
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/// Three-way merge for a single field:
|
|
623
|
+
/// - If only one side changed, take that change.
|
|
624
|
+
/// - If both sides changed to the same value, take it.
|
|
625
|
+
/// - If both sides changed to different values, apply LWW as fallback.
|
|
626
|
+
T _mergeField<T>(T ancestor, T local, T server) {
|
|
627
|
+
final localChanged = local != ancestor;
|
|
628
|
+
final serverChanged = server != ancestor;
|
|
629
|
+
|
|
630
|
+
if (!localChanged && !serverChanged) return ancestor; // No change
|
|
631
|
+
if (localChanged && !serverChanged) return local; // Only local changed
|
|
632
|
+
if (!localChanged && serverChanged) return server; // Only server changed
|
|
633
|
+
// Both changed — true conflict. Fallback to server-wins for safety.
|
|
634
|
+
// Log this for monitoring.
|
|
635
|
+
_logConflict(ancestor, local, server);
|
|
636
|
+
return server;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
```dart
|
|
642
|
+
// ── Repository (single access point for UI) ───────────────────
|
|
643
|
+
|
|
644
|
+
class TaskRepository {
|
|
645
|
+
final AppDatabase _db;
|
|
646
|
+
final SyncEngine _syncEngine;
|
|
647
|
+
|
|
648
|
+
TaskRepository(this._db, this._syncEngine);
|
|
649
|
+
|
|
650
|
+
/// UI calls this — always returns instantly from local DB.
|
|
651
|
+
Stream<List<Task>> watchAllTasks() => _db.watchTasks();
|
|
652
|
+
|
|
653
|
+
/// UI calls this — writes to local DB + enqueues sync.
|
|
654
|
+
/// Returns immediately. Sync happens in background.
|
|
655
|
+
Future<void> createTask(String title, String description) async {
|
|
656
|
+
final task = TaskCompanion(
|
|
657
|
+
id: Value(const Uuid().v4()),
|
|
658
|
+
title: Value(title),
|
|
659
|
+
description: Value(description),
|
|
660
|
+
status: const Value('todo'),
|
|
661
|
+
createdAt: Value(DateTime.now()),
|
|
662
|
+
updatedAt: Value(DateTime.now()),
|
|
663
|
+
syncVersion: const Value(0),
|
|
664
|
+
);
|
|
665
|
+
await _db.upsertTaskWithSync(task, 'create');
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/// UI calls this — updates local DB + enqueues sync.
|
|
669
|
+
Future<void> updateTask(String id, {String? title, String? status}) async {
|
|
670
|
+
final existing = await _db.getTask(id);
|
|
671
|
+
if (existing == null) return;
|
|
672
|
+
|
|
673
|
+
final updated = TaskCompanion(
|
|
674
|
+
id: Value(id),
|
|
675
|
+
title: Value(title ?? existing.title),
|
|
676
|
+
status: Value(status ?? existing.status),
|
|
677
|
+
updatedAt: Value(DateTime.now()),
|
|
678
|
+
syncVersion: Value(existing.syncVersion + 1),
|
|
679
|
+
);
|
|
680
|
+
await _db.upsertTaskWithSync(updated, 'update');
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Key Implementation Principles
|
|
686
|
+
|
|
687
|
+
1. **Every write is a transaction** that includes both the local DB mutation and the sync queue entry. If either fails, both roll back.
|
|
688
|
+
2. **The UI only reads from the local DB.** It never makes network calls directly. The Repository exposes streams (reactive) from the local database.
|
|
689
|
+
3. **The sync engine runs independently** of the UI. It drains the queue when online, pulls server changes, and resolves conflicts — all in the background.
|
|
690
|
+
4. **Conflict resolution is a separate, testable component.** The `ConflictResolver` is a pure function with no dependencies. It takes three versions (ancestor, local, server) and returns the merged result. Test it exhaustively.
|
|
691
|
+
5. **Idempotency is enforced via operation IDs.** Every sync queue entry has a UUID. The server must check this UUID before applying the operation. If the UUID already exists, the operation is skipped (not re-applied).
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
## Cross-References
|
|
696
|
+
|
|
697
|
+
- **[mobile-app-architecture](../mobile-architecture/)** — Offline-first is one of several architectural concerns for mobile apps. The broader mobile architecture module covers navigation, state management, and platform integration.
|
|
698
|
+
- **[push-and-sync](../../integration/)** — Push notifications can trigger sync operations, reducing the need for polling and ensuring timely data updates across devices.
|
|
699
|
+
- **[data-consistency](../../data/)** — Offline-first relaxes consistency guarantees from strong (linearizable) to eventual. Understanding the consistency spectrum is essential for choosing the right conflict resolution strategy.
|
|
700
|
+
- **[mobile-backend-for-frontend](../../integration/)** — A BFF can simplify offline-first by providing a sync-optimized API that returns delta payloads, handles conflict resolution server-side, and provides compact data formats for mobile bandwidth constraints.
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## Sources
|
|
705
|
+
|
|
706
|
+
- [Ink & Switch — Local-first software: You own your data, in spite of the cloud](https://www.inkandswitch.com/local-first/)
|
|
707
|
+
- [Flutter Docs — Offline-first support](https://docs.flutter.dev/app-architecture/design-patterns/offline-first)
|
|
708
|
+
- [Android Developers — Build an offline-first app](https://developer.android.com/topic/architecture/data-layer/offline-first)
|
|
709
|
+
- [Notion — How we made Notion available offline](https://www.notion.com/blog/how-we-made-notion-available-offline)
|
|
710
|
+
- [PowerSync — Local-First Software: Origins and Evolution](https://www.powersync.com/blog/local-first-software-origins-and-evolution)
|
|
711
|
+
- [PowerSync — Why Cinapse Moved Away from CRDTs for Sync](https://www.powersync.com/blog/why-cinapse-moved-away-from-crdts-for-sync)
|
|
712
|
+
- [PowerSync — ElectricSQL vs PowerSync](https://www.powersync.com/blog/electricsql-vs-powersync)
|
|
713
|
+
- [Neighbourhoodie — Offline-First with CouchDB and PouchDB in 2025](https://neighbourhood.ie/blog/2025/03/26/offline-first-with-couchdb-and-pouchdb-in-2025)
|
|
714
|
+
- [PouchDB — Replication Guide](https://pouchdb.com/guides/replication.html)
|
|
715
|
+
- [Yjs Documentation](https://docs.yjs.dev/)
|
|
716
|
+
- [GeekyAnts — Offline-First Flutter: Implementation Blueprint](https://geekyants.com/blog/offline-first-flutter-implementation-blueprint-for-real-world-apps)
|
|
717
|
+
- [droidcon — The Complete Guide to Offline-First Architecture in Android](https://www.droidcon.com/2025/12/16/the-complete-guide-to-offline-first-architecture-in-android/)
|
|
718
|
+
- [ElectricSQL — Alternatives](https://electric-sql.com/docs/reference/alternatives)
|
|
719
|
+
- [CRDT.tech — Implementations](https://crdt.tech/implementations)
|