Skip to content

MS33834/taskflow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

205 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TaskFlow

A local-first, end-to-end-encrypted, cross-platform task workflow. Your to-do list doesn't have to live on someone else's server.

OpenSSF Scorecard OpenSSF Best Practices

English · 中文

Project intro · Report a bug · Request a feature


Why this exists

Most task managers pick one of two lanes: pure-local (great on one device, useless the moment you switch laptops) or cloud-synced (convenient, but every plan, habit and note you keep ends up on someone else's disk). TaskFlow takes the third path — data lives on your devices by default, and when it syncs across devices it's end-to-end encrypted. The relay server only ever sees ciphertext frames.

I started it because I wanted a todo app I could actually use on the train (no network), on my laptop (full keyboard), and on my phone (swipe to dismiss) without maintaining three codebases or mailing my life to a SaaS. The interesting parts, in my opinion:

  • Four components, one data model. Mobile (React Native), desktop (Electron), an optional Python backend, and a self-hostable relay. Every persisted object carries updatedAt, so the same record flows across all of them.
  • E2EE sync that's actually byte-compatible cross-platform. Mobile uses Web Crypto, desktop uses Node crypto. The wire format differs in one place (AES-GCM authTag position) so we reshuffle it explicitly — a frame encrypted on mobile decrypts on desktop, and vice versa. 668 tests include bidirectional interop cases.
  • Local-first by default, not as a marketing tag. No TaskFlow account exists. The relay can't read your data. If you never pair a second device, nothing ever leaves the first one.
  • AI suggestions are statistical, not magical. They look at your completion history (which hours you actually work in, which category you default to, which tasks you keep deferring) and surface that as a one-tap action. No model weights, no API, no network.

What's in the box

Component Stack What it does
Mobile React Native 0.86 · Expo 56 · TypeScript 6 · Zustand 5 15 screens, 6 views, focus mode / pomodoro / white noise. Private keys in Keychain / Keystore.
Desktop Electron · React · Vite · better-sqlite3-multiple-ciphers SQLCipher file-level encryption + field-level AES-GCM. Biometric unlock, screenshot protection, privacy shell.
Relay Node.js 18+ · WebSocket · express-rate-limit · Docker Forwards ciphertext frames only, with an offline queue (7-day TTL). LAN-first, relay as fallback.
Backend Python 3.11+ · FastAPI · PyGit2 · langchain-core Optional: task API, git management, LLM, plugin system. Decoupled from the sync path.

Mobile ⇄ desktop syncs over an encrypted P2P session; the relay is just a NAT-traversal porter that never decrypts. See ARCHITECTURE.md for the full layering.

Screens & views

Fifteen screens on mobile (Home, Calendar, Analytics, Search, Goals, Habits, Notes, Projects, Categories, Tags, Views, Templates, Automation, TaskDetail, Settings) and six non-list views over the same data:

  • Kanban — drag between columns, PanResponder + LayoutAnimation
  • Gantt — dependencies on a timeline
  • Timeline / TimeBlock / Table / MindMap — one view per way of looking
  • Focus mode — full-screen Forest + pomodoro + Web Audio white noise

Draggable reordering is done without react-native-reanimated — PanResponder + LayoutAnimation is enough for the lists a todo app actually has, and the bundle is ~30% smaller for it. Task dependencies (blockedBy / blocks) stop you marking a task done when something it's waiting on is still open.

End-to-end encrypted sync

This is where most of the time went. Two devices handshake before syncing, verify each other's identity, derive a pair of direction-isolated session keys, then every record flows under AES-256-GCM. The relay sees bytes, not content.

identity    Ed25519 long-term keypair per device, private key in Keychain/Keystore
            deviceId = sha256(raw SPKI pubkey), first 16 hex
handshake   X25519 ephemeral ECDH, both sides Ed25519-sign the transcript
derive      HKDF-SHA256 → sendKey / receiveKey, info bound by role direction
records     Sync Master Key (SMK) does AES-256-GCM, wire = iv[12]‖tag[16]‖ct
conflict    updatedAt (LWW) first, version vectors break same-ms ties
transport   9 message types, frame = mode[1]‖length[4 BE]‖payload
            sequence number + sliding window for replay protection

Pairing is an 8-digit code, 5-minute TTL, one-shot. SMK is generated by the host and transferred to the joiner over the encrypted pairing session, then compared in constant time — if it doesn't match, pairing is rejected rather than silently continuing into a state where every later sync would fail to decrypt. Device revocation is a four-step orchestration: kill the runtime session, drop the device record, clear its outbox, and only reset the SMK when no paired devices remain.

The gory details (incl. 7 places where the implementation diverged from the design spec, and why) live in docs/superpowers/specs/.

Running it

Mobile / Web (root of the repo)

npm install
npm run web      # fastest path — opens http://localhost:8081
npm run android  # needs Android Studio or a device
npm run ios      # needs macOS + Xcode

For a deployable web bundle:

npm run build:web   # writes dist/, ready for Netlify / Vercel / S3

Desktop

cd desktop
npm install
npm run dev       # Vite + Electron in dev mode
npm run build     # produces platform installer via electron-builder

System requirements and the full desktop user manual (master password, biometric unlock, vault, sync, backup) are in docs/desktop-user-guide.md.

Relay (self-hosted)

cd relay
docker compose up -d   # uses relay/docker-compose.yml

TLS, Nginx reverse proxy and ACME notes are in docs/relay-deployment.md. The relay only needs to reach the public internet; it never sees plaintext.

Backend (optional)

cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload

OpenAPI spec is auto-generated and CI-validated — see backend/docs/openapi.json (36 endpoints).

Project layout

App.tsx                  # mobile entry, wraps everything in an ErrorBoundary
screens/                 # 15 mobile screens, one file each
src/shared/
  components/common/     # ~20 reusable bits (Button, TaskCard, ...)
  components/views/      # the six non-list views (Kanban, Gantt, ...)
  components/sync/       # mobile sync UI (pairing dialog, device list, ...)
  hooks/                 # useBulkSelection, useKeyboardShortcuts, useUndo, useSyncRuntime
  store/index.ts         # Zustand store entry; state lives in slices/
  sync/                  # mobile E2EE stack (Web Crypto based, byte-compatible with desktop)
  types/index.ts         # every persisted shape lives here
  utils/                 # natural-language date parser, secure storage
desktop/                 # Electron app — main / preload / renderer
  src/main/services/sync/  # desktop E2EE stack (Node crypto based)
  src/renderer/            # React UI (vault, sync settings, task pages)
relay/                   # self-hostable WebSocket relay + Docker
backend/                 # optional FastAPI service
docs/                    # user guide, roadmap, security tracker, relay deployment

The mobile src/shared/sync/ and desktop desktop/src/main/services/sync/ directories are deliberately parallel — same protocol, different crypto backends. The .interop-check.js script at the repo root verifies byte-level compatibility between the two.

Quality bar

# mobile / web
npm run typecheck   # tsc --noEmit, 0 errors
npm run lint        # ESLint v9 flat config, 0 errors
npm run test        # vitest

# desktop
cd desktop && npm run typecheck && npm run lint && npm test

# backend
cd backend && python -m ruff check app && python -m mypy app && python -m pytest tests/ -q

# relay
cd relay && npx tsc --noEmit && npm test

I aim for "0 errors" on all four. The test baseline as of v1.2.0 is 668 tests (mobile 339 · desktop 256 · backend 65 · relay 8), all green on main. The 5000-record end-to-end sync benchmark lands at 649 ms (7699 rec/s) — that figure used to be 52 s before fixing a REQUEST-chunking bug and batching the inserts into one transaction.

Security

Two independent audits (TF-001019 and TF2-001017, 36 findings total) are tracked in a single SECURITY_TRACKER.md. Most are fixed; the rest are tagged in the roadmap. Highlights of what's in place: branch protection with required review, CodeQL, gitleaks with push-protection, dependency review (rejects GPL-3.0/AGPL-3.0), OSSF Scorecard, cosign-signed releases, property-based fuzzing with Hypothesis. Vulnerability reporting is private — see SECURITY.md.

Deployment

The repo ships these GitHub Actions:

  • verify.yml — typecheck on every push (lint runs in ci.yml)
  • build-android.yml — debug APK build artifact
  • eas-build.yml — EAS Cloud Build (manual trigger, needs EXPO_TOKEN)
  • pages-intro.yml — publishes the static project intro in docs/ to GitHub Pages
  • desktop-build.yml — builds the Electron installers (three platforms)
  • release.yml — cosign-keyless signed release + SHA256SUMS

The GitHub Pages site is only an introduction page; the actual apps must be run locally or built via the workflows above.

Documentation

Doc What it covers
ARCHITECTURE.md Layering, state model, sync protocol stack
QUICK_START.md Three paths to a running app
docs/desktop-user-guide.md Desktop manual: install, vault, biometric, sync, backup (442 lines)
docs/ROADMAP.md What's done (Phase 1–8) and what's next
docs/DEVELOPER_GUIDE.md Dev workflow, commands, sync strategy
docs/relay-deployment.md Self-hosting the relay, TLS, Nginx
docs/fuzzing.md Property-based fuzzing with Hypothesis
CHANGELOG.md Per-phase change log
FAQ.md Stuff people actually run into
SECURITY.md Supported versions, private disclosure

Mirrors

Two mirrors, identical content. GitHub is the primary repo (CI, releases, issues); GitCode is a mirror for reach from inside the GFW.

Host Repo
GitHub github.com/MS33834/taskflow
GitCode gitcode.com/badhope/taskflow

File issues and PRs on GitHub. The GitCode mirror receives the same pushes but doesn't run CI.

Known caveats

  • The web build is a single SPA bundle — fine for GitHub Pages, too big for anything that cares about TTFB. Run npm run build:web and check dist/ for the current size rather than trusting a stale number here.
  • Voice input is Web-only. The native side would need a separate speech-recognition library, which would balloon the APK.
  • Screenshot protection on Linux is best-effort. X11/Wayland can't reliably intercept screenshots, so the settings page says so plainly instead of pretending.
  • Mobile ↔ desktop sync requires a self-hosted relay for pairing. Once paired, LAN-direct is preferred when reachable; the relay is fallback.

License

MIT. See LICENSE.


If you're reviewing this for a job, ARCHITECTURE.md is the best place to start — it walks through the state model, the rendering pipeline, and the sync protocol stack.

About

A task and workflow management system with scheduling and automation.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors