Commit graph

205 commits

Author SHA1 Message Date
Johannes Millan
73c1848ba9 fix(docs): resolve markdown linting errors in all wiki files
- Add top-level headings to all wiki files (MD041)
- Fix heading spacing and blank line issues (MD022, MD012)
- Convert HTML badges to markdown format (MD033)
- Add alt text to images (MD045)
- Fix list indentation and numbering (MD005, MD029, MD032)
- Remove trailing punctuation from headings (MD026)
- Convert emphasis to proper headings (MD036)
- Fix heading level increments (MD001)
- Add trailing newlines to all files (MD047)

This ensures the wiki-sync.yml GitHub Action will pass linting.
All changes maintain the same visual appearance and functionality.

Fixes the lint failures reported in GitHub Action run #21212863659
2026-01-21 18:03:05 +01:00
Johannes Millan
0b266bebc5 docs: add plan 2026-01-21 14:30:24 +01:00
Johannes Millan
dab0172a48
Merge pull request #6071 from zenoprax/pr/wiki-sync
docs(wiki): initial revision (v0.1)
2026-01-21 12:22:34 +01:00
Johannes Millan
955a3e13c5 docs: add plan 2026-01-20 17:07:23 +01:00
Corey Newton
f9a328022d
docs(wiki): initial revision (v0.1)
Contains both a first-draft of content as well as
a comprehensive GH Action to replicate from
docs/wiki to the `.wiki` sub-repo. The linting is
non-blocking at the moment.

- the existing markdown linking appears reliably
rendered in GH but more testing needed.
- style guide for contributions/expectations needs
to be added to the wiki
- **a significant re-work of the README** to
re-direct users to the Wiki is needed to avoid
doc duplication
- updates to the PR templates and contributor
guidelines to emphasize the importance of adding
documentation is still needed
2026-01-20 03:04:08 -08:00
johannesjo
9d19a481c8 fix(ios): use correct bundle ID com.super-productivity.app 2026-01-16 16:07:33 +01:00
johannesjo
d58f5eb6b7 docs: add iOS App Store build and publish notes 2026-01-16 16:02:13 +01:00
Johannes Millan
038a722ed8 style: apply prettier formatting 2026-01-10 17:09:14 +01:00
Johannes Millan
150fd074ab Merge branch 'master' into feat/operation-logs
Integrate master branch updates into operation-logs feature branch:
- Add heatmap components and scheduled date group pipe
- Add task delete confirmation dialog and tests
- Update Android focus mode and sound configuration
- Add GitLab PAT authentication support
- Merge translation keys and i18n updates
2026-01-09 16:57:25 +01:00
Johannes Millan
af4e622f91 chore(i18n): remove additional 86 orphan translation keys
Remove keys that are duplicates, never implemented, or obsolete:
- F.SAFETY_BACKUP.* (32) - orphan duplicate of F.SYNC.SAFETY_BACKUP
- F.PROCRASTINATION.* (32) - feature never implemented
- GCF.PAST.* (11) - orphan duplicate of GLOBAL_RELATIVE_TIME.PAST
- GCF.TIMELINE.* (10) - settings form never implemented
- WW.HELP_PROCRASTINATION (1) - related to unimplemented feature

Total unused keys reduced: 350 → 264
Remaining 264 keys need case-by-case review (planned features,
dynamic object access patterns, edge cases).

Add docs/unused-translations-analysis.md with detailed findings.
2026-01-09 14:59:05 +01:00
Johannes Millan
9605177fc0 docs: enhance SuperSync vs file-based comparison with detailed diagrams
Add comprehensive comparison diagrams:
- Side-by-side architecture showing upload/download flows
- Key conceptual differences (source of truth, sequencing, etc.)
- Detailed feature comparison table with "winner" column
- Trade-offs visualization (bandwidth, setup, history, etc.)
- Concurrent edit scenario showing gap detection vs piggybacking
2026-01-08 11:31:07 +01:00
Johannes Millan
c7bc9d4978 docs: add detailed master architecture diagram for file-based sync
Replace simple "Complete System Flow" with comprehensive diagram showing:
- Client-side sync loop, download flow, upload flow
- Conflict management with LWW auto-resolution
- First-sync conflict dialog and snapshot hydration
- Piggybacking mechanism on upload
- File provider layer with adapter methods
- Remote storage structure

Also add Quick Reference Tables for:
- File operations (download/upload methods)
- sync-data.json structure
- Key implementation details
2026-01-08 11:12:42 +01:00
Johannes Millan
9f0adbb95c docs: fix outdated file paths and types in diagrams
- Fix FileBasedSyncData type: remove non-existent lastSeq, add clientId
- Fix file paths: op-log/processing → op-log/apply
- Fix file paths: features/time-tracking → features/archive
- Fix file path: super-sync not supersync
- Fix vector-clock path: now in op-log/sync/
- Remove non-existent state-capture.meta-reducer.ts reference
- Remove pfapi-migration.service.ts (no longer exists)

docs: remove outdated .bak file references from diagrams

The backup file (sync-data.json.bak) is no longer created during
upload. It's only deleted as cleanup from legacy implementations.

docs: add sync comparison and simple sync flow diagrams

- Add 07-supersync-vs-file-based.md comparing the two sync approaches
- Add 08-sync-flow-explained.md with step-by-step sync explanation
- Remove consolidated unified-oplog-sync-diagrams.md
- Update diagrams README with new entries

docs(sync): reorganize diagrams into subfolder and update for unified architecture

- Create docs/sync-and-op-log/diagrams/ with topic-based diagram files
- Remove outdated PFAPI Legacy Bridge references from diagrams
- Update archive diagrams to use generic "Archive Database" naming
- Fix file paths from sync/providers/ to sync-providers/
- Update quick-reference Area 12 to show unified file-based sync
- Update README to reference new diagram locations

docs: update architecture docs to reflect PFAPI elimination

- Delete obsolete PFAPI documentation:
  - docs/sync-and-op-log/pfapi-sync-persistence-architecture.md
  - docs/sync-and-op-log/pfapi-sync-overview.md
  - docs/plans/pfapi-elimination-status.md

- Update sync-and-op-log/README.md:
  - Describe unified operation log architecture
  - Document file-based sync (Part B) and server sync (Part C)
  - Update file structure to reflect sync-providers location

- Update operation-log-architecture.md:
  - Rewrite Part B from "Legacy Sync Bridge" to "File-Based Sync"
  - Remove all PFAPI code examples and references
  - Update IndexedDB structure diagram (single SUP_OPS database)
  - Update architecture overview to show current provider structure
  - Add notes about PFAPI elimination (January 2026)

- Mark completed implementation plans:
  - replace-pfapi-with-oplog-plan.md - marked as COMPLETED
  - file-based-oplog-sync-implementation-plan.md - marked as COMPLETED

Also includes fix for file-based sync gap detection to handle
snapshot replacement (when "Use Local" is chosen in conflict resolution).
2026-01-08 11:10:29 +01:00
Johannes Millan
4e49628eca chore: clean up leftover pfapi references after refactor
- Remove dead test:shard:pfapi script from package.json
- Update AGENTS.md persistence layer path to op-log and sync
- Update documentation file paths in secure-storage.md,
  vector-clocks.md, and quick-reference.md
2026-01-07 21:10:38 +01:00
Johannes Millan
db990b7018 refactor: integrate pfapi into oplog 2 2026-01-07 13:37:05 +01:00
Johannes Millan
1f8fe61c84 refactor: integrate pfapi into oplog 1 2026-01-07 13:30:09 +01:00
Johannes Millan
a42c8a4cee Merge branch 'master' into feat/operation-logs
* master:
  refactor(dialog): remove unused OnDestroy implementation from DialogAddNoteComponent
  fix(calendar): poll all calendar tasks and prevent auto-move of existing tasks
  docs: add info about how to translate stuff #5893
  refactor(calendar): replace deprecated toPromise with firstValueFrom
  build: update links to match our new organization
  add QuestArc to community plugins list
  feat(calendar): implement polling for calendar task updates and enhance data retrieval logic
  fix(heatmap): use app theme class instead of prefers-color-scheme
  fix(focus-mode): start break from banner when manual break start enabled
  feat(i18n): connect Finnish and Swedish translation files
  refactor(focus-mode): split sessionComplete$ and breakComplete$ into single-responsibility effects
  Fixing Plugin API doc on persistence

# Conflicts:
#	src/app/features/issue/store/poll-issue-updates.effects.ts
#	src/app/t.const.ts
2026-01-05 19:12:46 +01:00
Johannes Millan
08971fce47 docs: add info about how to translate stuff #5893 2026-01-05 18:05:45 +01:00
Johannes Millan
24d3fb6fab Merge remote-tracking branch 'origin/master'
* origin/master:
  add QuestArc to community plugins list
  Fixing Plugin API doc on persistence
2026-01-05 17:36:12 +01:00
Johannes Millan
1a79592aca build: update links to match our new organization 2026-01-05 14:45:06 +01:00
Florian Bachmann
3bc5bb28cd
Fixing Plugin API doc on persistence 2026-01-05 08:03:07 +01:00
Johannes Millan
936f374bde refactor(sync): address code review findings for operation log
Phase 1 - Fix archive circular dependency:
- Create ArchiveDbAdapter for direct IndexedDB access to archive data
- Update ArchiveOperationHandler to use ArchiveDbAdapter instead of
  lazy-injecting PfapiService, breaking the circular dependency chain
- Update tests to mock ArchiveDbAdapter

Phase 2 - Address testing gaps:
- Add 3 timeout exhaustion tests for compaction service (25s timeout)
- Add 5 clock skew edge case tests for conflict resolution:
  - Far future/past timestamps
  - Zero and negative timestamps
  - Client ID tie-breaker for identical timestamps

Phase 3 - Document cross-version migration (A.7.11):
- Add comprehensive implementation guide in architecture doc covering:
  - When to bump CURRENT_SCHEMA_VERSION
  - Operation transformation strategy
  - Conflict detection across versions
  - Backward compatibility guarantees
  - Migration rollout strategy
  - Example migration and testing requirements

All 1399 op-log tests pass.
2026-01-02 13:48:22 +01:00
Johannes Millan
1c85979792 docs: add plan 2025-12-28 13:17:16 +01:00
Johannes Millan
c29913aeac test(time-tracking): add comprehensive tests and documentation
Add extensive tests and documentation for TimeTrackingState sync:

Unit tests (~22 new):
- merge-time-tracking-states.spec.ts: partial data merging, null
  handling, merge priority, scale testing
- sort-data-to-flush.spec.ts: flush completeness, LWW behavior,
  date boundaries
- time-tracking.reducer.spec.ts: context preservation, immutability

Integration tests (9 new):
- time-tracking-sync.integration.spec.ts: operation creation,
  SYNC_IMPORT with timeTracking, multi-client sync, archive flush

Server tests (18 new):
- time-tracking-operations.spec.ts: operation upload, conflict
  detection, snapshot generation, LWW semantics

Documentation:
- JSDoc for merge-time-tracking-states.ts and sort-data-to-flush.ts
- Section E.6 "Time Tracking Sync Semantics" in architecture docs
2025-12-28 13:16:07 +01:00
Johannes Millan
402c67a5b1 docs: add TODAY_TAG architecture and improve timing constants docs
- Create docs/ai/today-tag-architecture.md documenting the virtual tag
  pattern, explaining why TODAY_TAG must never be in task.tagIds and
  how membership is determined by task.dueDay instead
- Add reference section to operation-log.const.ts pointing to related
  timing constants in other domain files (sync.const.ts, meta-sync)

This addresses documentation gaps identified in the maintainability review.
2025-12-27 19:13:56 +01:00
Johannes Millan
d6dcc86ca0 refactor: reorganize operation-log files into src/app/op-log/
Move operation-log from src/app/core/persistence/operation-log/ to
src/app/op-log/ with improved subdirectory organization:

- core/: Types, constants, errors, entity registry
- capture/: Write path (Actions → Operations)
  - operation-capture.meta-reducer.ts
  - operation-capture.service.ts
  - operation-log.effects.ts
- apply/: Read path (Operations → State)
  - bulk-hydration.action.ts/.meta-reducer.ts
  - operation-applier.service.ts
  - operation-converter.util.ts
  - hydration-state.service.ts
  - archive-operation-handler.service.ts/.effects.ts
- store/: IndexedDB persistence
  - operation-log-store.service.ts
  - operation-log-hydrator.service.ts
  - operation-log-compaction.service.ts
  - schema-migration.service.ts
- sync/: Server sync (SuperSync)
  - operation-log-sync.service.ts
  - operation-log-upload.service.ts
  - operation-log-download.service.ts
  - conflict-resolution.service.ts
  - sync-import-filter.service.ts
  - vector-clock.service.ts
  - operation-encryption.service.ts
- validation/: State validation
  - validate-state.service.ts
  - validate-operation-payload.ts
- util/: Shared utilities
  - entity-key.util.ts
  - client-id.provider.ts
- testing/: Integration tests and benchmarks

This reorganization:
- Places op-log at the same level as pfapi for better visibility
- Groups files by responsibility (write path vs read path)
- Makes the sync architecture more discoverable
- Improves navigation for developers new to the codebase
2025-12-27 17:52:11 +01:00
Johannes Millan
693e71deeb docs(sync): complete quick-reference with Areas 8-12
Add visual summaries for remaining areas:
- Area 8: Meta-Reducers (atomic multi-entity changes)
- Area 9: Compaction (snapshot + garbage collection)
- Area 10: Bulk Application (single dispatch optimization)
- Area 11: Encryption (AES-256-GCM + Argon2id)
- Area 12: Legacy PFAPI Bridge (WebDAV/Dropbox)

Removes "Coming Soon" placeholder. All 12 areas now documented.
2025-12-27 17:10:18 +01:00
Johannes Millan
30fa62b9a4 test(sync): add canary test for entity type coverage
Add safeguards to prevent silent failures when new entity types are
added without proper registration:

- Add canary test that fails if EntityType union grows but test arrays
  aren't updated (verifies count matches expected 20 types)
- Create developer checklist at docs/ai/adding-new-entity-type-checklist.md
  documenting all required changes when adding new entity types
2025-12-27 17:08:53 +01:00
Johannes Millan
e221afe81c docs(sync): add quick-reference.md with visual summaries
Create a quick reference document with ASCII diagrams summarizing:
- Area 1: Write Path
- Area 2: Read Path (Hydration)
- Area 3: Server Sync (SuperSync)
- Area 4: Conflict Detection
- Area 5: Conflict Resolution (LWW)
- Area 6: SYNC_IMPORT Filtering
- Area 7: Archive Handling

Includes decision tables, key invariants, and file references.
Areas 8-12 placeholders for future additions.
2025-12-27 16:42:38 +01:00
Johannes Millan
330ecce2c9 docs(sync): fix LWW timestamp and SYNC_IMPORT filtering sections
- C.5: Correct "Current timestamp" to "Preserved maximum timestamp
  from local ops" - critical for correct LWW semantics
- C.7: Rewrite "Late-Joiner Replay" as "SYNC_IMPORT Filtering" to
  reflect actual "Clean Slate Semantics" implementation:
  - CONCURRENT ops are now DROPPED, not replayed
  - Uses SyncImportFilterService, not removed _replayLocalSyncedOpsAfterImport()
  - Vector clock comparison determines causality, not UUIDv7 timestamps
2025-12-27 15:59:00 +01:00
Johannes Millan
8fff1325b4 docs: fix SYNC_IMPORT filtering section to match implementation
- Fix CLAUDE.md path reference from docs/op-log/ to docs/sync-and-op-log/
- Rewrite section 2c in architecture diagrams to reflect actual implementation:
  - Was: UUIDv7 timestamp replay (removed feature)
  - Now: Vector clock filtering with clean slate semantics
- Update service reference from removed _replayLocalSyncedOpsAfterImport()
  to SyncImportFilterService.filterOpsInvalidatedBySyncImport()
- Clarify that CONCURRENT ops are dropped, not replayed
2025-12-27 13:15:47 +01:00
Johannes Millan
c1575d7669 docs(sync): update operation-log docs to reflect current implementation
- Update README.md with correct links and implementation status
- Add E2EE to implementation status (now complete)
- Update e2e-encryption-plan.md to mark as implemented
- Update operation-log-architecture.md:
  - Add missing service files to file reference section
  - Add E2EE to Part C and "Recently Completed" sections
  - Fix broken references, update last modified date
- Remove reference to non-existent tiered-archive-proposal.md
2025-12-27 11:44:30 +01:00
Johannes Millan
39d6e15446 docs: improve structure 2025-12-27 11:32:54 +01:00
Johannes Millan
7227749ec4 test(sync): add tests for bulk dispatch edge cases
Add comprehensive tests for operation applier testing gaps:

- Partial archive failure: verifies that when archive handling fails
  midway through a batch, previously processed ops are reported
  as successful while remaining ops are not applied
- Effects isolation: confirms that only bulkApplyOperations action
  is dispatched (not individual action types), which is the key
  architectural benefit preventing effects from firing on remote ops
- Multiple archive-affecting ops: ensures remoteArchiveDataApplied
  is dispatched exactly once when batch contains multiple
  archive-affecting operations

Total: 4 new test cases, 24 tests now passing
2025-12-27 11:31:51 +01:00
Johannes Millan
b4ce9d5da6 docs: reorganize sync and operation-log documentation
Move scattered architecture docs into centralized locations:

- Move operation-log docs from src/app/core/persistence/operation-log/docs/
  to docs/op-log/
- Flatten docs/sync/sync/ nested structure to docs/sync/
- Move supersync-encryption-architecture.md from docs/ai/ to docs/sync/
- Copy pfapi sync README to docs/sync/pfapi-sync-overview.md
- Update all cross-references to use new paths

This improves discoverability and keeps architecture documentation
separate from source code.
2025-12-27 10:54:13 +01:00
Johannes Millan
5138b46546 fix(sync): temporarily disable data repair to debug archive subtask loss
Disable automatic data repair during sync/hydration to:
- Expose validation failures instead of silently "fixing" them
- Prevent REPAIR operations from propagating potentially corrupted data
- Help identify the root cause of archived tasks with subtasks being lost

When validation fails, an alert is now shown with error details.
The original repair logic is preserved in comments for easy re-enabling.
2025-12-26 10:54:54 +01:00
Johannes Millan
064c2452ca fix(sync): handle piggyback limit in high-volume sync
When syncing 100+ operations, the server's piggyback limit (100 ops)
was causing Client B to miss operations. The server returned 100 piggybacked
ops but latestSeq was set to the actual server sequence (e.g., 199).
Client B then updated lastServerSeq to 199, so subsequent download got 0 ops.

Changes:
- Server: Add hasMorePiggyback flag to UploadOpsResponse when piggyback limit
  is reached and more ops exist
- Client: When hasMorePiggyback is true, store lastServerSeq as the max
  piggybacked op's serverSeq instead of latestSeq, ensuring subsequent
  download fetches remaining ops
- Effects: Change switchMap to mergeMap in autoAddTodayTagOnMarkAsDone to
  ensure ALL mark-as-done actions trigger planTasksForToday
- Flush service: Implement two-phase wait strategy (poll queue + acquire lock)
  to ensure all pending writes complete before upload
- Add diagnostic logging for operation counts at key stages

Test: High volume sync with 50 tasks + 49 mark-as-done (197 ops total)
now correctly syncs all done states via piggyback (100) + download (97).
2025-12-22 20:31:35 +01:00
Johannes Millan
c4cc32da29 feat(sync): add encryption password change feature for SuperSync
Implements the ability to change the encryption password by deleting
all server data and uploading a fresh snapshot with the new password.

Server changes:
- Add DELETE /api/sync/data endpoint to delete all user sync data
- Add deleteAllUserData() method to SyncService

Client changes:
- Add deleteAllData() to OperationSyncCapable interface
- Implement deleteAllData() in SuperSync provider
- Add EncryptionPasswordChangeService to orchestrate password change
- Add DialogChangeEncryptionPasswordComponent with validation
- Add "Change Encryption Password" button to sync settings (visible
  when encryption is enabled)
- Add translations for all new UI strings

Testing:
- Add 10 unit tests for EncryptionPasswordChangeService
- Add 14 unit tests for DialogChangeEncryptionPasswordComponent
- Add 5 E2E tests for complete password change flow
- Add changeEncryptionPassword() helper to SuperSyncPage

Also fixes:
- Add missing deleteAllData() to MockOperationSyncProvider
- Fix typo S_FINISH_DAY_SYNC_ERROR -> FINISH_DAY_SYNC_ERROR
2025-12-22 13:31:19 +01:00
Johannes Millan
155e03b84c docs: remove outdated sync documentation and deprecated file
- Delete docs/ai/sync/server-sync-architecture.md which incorrectly
  stated "Status: Not Started" when server sync is fully implemented
- Delete deprecated src/app/features/time-tracking/store/archive.effects.ts
  which was empty and marked for removal
- Update local-actions.token.ts comment to reference the correct
  archive-operation-handler.effects.ts file
2025-12-20 11:09:49 +01:00
Johannes Millan
f928e6b18e docs: cleanup 2025-12-16 12:28:04 +01:00
Johannes Millan
642f8d3984 docs(sync): improve and consolidate operation log documentation
- Update hybrid-manifest-architecture.md status to Implemented
- Add documentation index (README.md) for operation-log docs
- Add index for docs/ai/sync/ with document categorization
- Replace duplicate docs with redirect stubs to canonical locations
- Add effect rules, multi-entity rules, and config constants to operation-rules.md
- Update vector-clocks.md with operation log integration section
- Expand super-sync-server README with API details and security features
- Update pfapi sync README with operation log integration info
2025-12-16 10:45:20 +01:00
Johannes Millan
04043e3964 docs(sync): document late-joiner replay with vector clock dominance
- Add Section 2c to architecture diagrams with mermaid diagrams
  explaining the late-joiner problem and solution
- Add Section C.7 to main architecture doc with code examples
- Add Example 4 to vector-clocks.md explaining dominance filtering
- Update Last Updated dates to December 12, 2025
2025-12-12 20:48:40 +01:00
Johannes Millan
37a5101da3 docs(sync): remove separate review doc, architecture documented in code 2025-12-12 20:48:40 +01:00
Johannes Millan
e647455114 docs(sync): restore E2E encryption review document 2025-12-12 20:48:40 +01:00
Johannes Millan
f47e9496a9 docs(sync): add architecture context to operation log services
Add class-level documentation explaining:
- Only SuperSync uses operation log sync (API-based)
- Legacy providers skip operation log sync entirely
- File-based methods exist for future extensibility but are never called
- If file-based sync is ever enabled, encryption/decryption must be added
2025-12-12 20:48:40 +01:00
Johannes Millan
c79144c4b8 docs(sync): update E2E encryption review with correct architecture
The original review incorrectly identified file-based operation log sync
as having "critical gaps". This update corrects the analysis:

- File-based operation log sync is dead code, never called
- Only SuperSync uses operation log sync (API-based)
- Legacy providers skip operation log sync entirely and use pfapi LWW

Status changed from "NEEDS FIXES" to "PRODUCTION READY"
2025-12-12 20:48:40 +01:00
Johannes Millan
37b5e94525 docs(sync): update implementation review - all test gaps now covered
Mark all previously identified test gaps as resolved:
- Race conditions: 6 new tests in compaction service
- Schema migration: 6 version mismatch tests in hydrator service
- Download retry: 4 tests now passing (was pending())
- All critical and missing scenarios now have test coverage
2025-12-12 20:48:40 +01:00
Johannes Millan
d6e8fe9ced docs(sync): update implementation review and E2E test plan
- Add December 11 security review results (all issues verified as addressed)
- Document newly added tests (3-way conflict, delete vs update, large conflict sets)
- Update test coverage status table with completed items
- Mark supersync E2E test plan as IMPLEMENTED
- List all test files and their coverage areas
2025-12-12 20:48:40 +01:00
Johannes Millan
7ad78333be docs(sync): update architecture docs for recent refactors
Update documentation to reflect recent simplifications:
- OperationCaptureService now uses simple FIFO queue
- OperationApplierService uses fail-fast approach
- ArchiveOperationHandler is unified for local and remote operations
- Update diagrams and file references
- Add "Recently Completed" items for December 2025
2025-12-12 20:48:13 +01:00
Johannes Millan
5ffc109943 refactor(sync): unify archive operation handling
Consolidate all archive-affecting operations into ArchiveOperationHandler
to eliminate duplicate code and create a single source of truth.

Changes:
- Add ArchiveOperationHandlerEffects for local operations
- Rename handleRemoteOperation to handleOperation
- Add isArchiveAffectingAction helper for action filtering
- Make isIgnoreDBLock conditional on action.meta.isRemote
- Remove archive logic from individual effect files
- Update feature-stores.module.ts to use new effect
- Add archive write points documentation

This ensures archive operations (moveToArchive, restoreTask,
flushYoungToOld, deleteProject, deleteTag, deleteTaskRepeatCfg,
deleteIssueProvider) all go through one handler for both local
and remote operations.
2025-12-12 20:48:12 +01:00