21 KiB
21 KiB
Architectural Decision Log (ADL)
[2026-08-09] - ADL-001: Distributed Local-First System Baseline
- Status: Accepted
- Context: Design a multi-platform (Windows, Linux, Android, Headless) productivity engine for time, project, and note management without centralized servers.
- Decision: Adopted Rust core with SQLCipher local storage, Automerge CRDT delta sync over libp2p, and Flutter presentation layer bound via C-FFI.
[2026-08-09] - ADL-002: chronogrid_crypto Crate – Argon2id KDF & OS Keychain
- Status: Accepted
- Context: Task 1.2 required implementing the
chronogrid_cryptocrate with Argon2id master-key derivation and OS-native keychain bindings perdocs/ARCHITECTURE/02-security-and-crypto.md. - Decision:
- Argon2id parameters pinned: 64 MiB memory, 3 iterations, 4 threads, 16-byte random salt, 32-byte output.
DerivedKeystruct implementsZeroize + Dropto scrub sensitive memory on scope exit.- OS keychain abstracted via
keyringcrate v3 (Windows Credential Manager / DPAPI, Linux SecretService / libsecret). Android keychain is excluded (cfg(not(target_os = "android"))) and will be handled by Flutter KeyStore bridge in Task 1.5. - Raw key material is Base64-encoded before credential-store insertion for UTF-8 backend compatibility.
- Inline Base64 encode/decode helpers avoid adding an external Base64 dependency.
[2026-08-19] - ADL-003: chronogrid_core Crate – SQLCipher 4.x Database Initializer
- Status: Accepted
- Context: Task 1.3 required implementing the
chronogrid_corecrate with SQLCipher 4.x encrypted database initialization matchingdocs/API_CONTRACTS/schema.sql. - Decision:
rusqlitev0.31 withbundled-sqlcipher-vendored-opensslfeature bundles both SQLCipher and OpenSSL, ensuring a hermetic build with no external C library dependencies.- The 32-byte Argon2id-derived master key is passed as a raw hex key via
PRAGMA key = "x'…'", bypassing SQLCipher's internal PBKDF2 derivation since Argon2id already provides equivalent or stronger protection. PRAGMA cipher_compatibility = 4enforces SQLCipher 4.x defaults (AES-256-CBC, HMAC-SHA512 page auth, 64 KiB page size).PRAGMA foreign_keys = ONis set on every connection per the schema specification.- The canonical schema SQL from
docs/API_CONTRACTS/schema.sqlis embedded viainclude_str!at compile time, ensuring single-source-of-truth alignment without runtime file I/O. - Post-initialization validation confirms all 6 expected tables exist (
namespaces,projects,tasks,notes,task_note_links,ingestion_logs) plus 4 indexes. Databasestruct providesopen(path, key)for file-backed andopen_in_memory(key)for test databases; key length is validated at 32 bytes.- 11 unit tests cover: table/index creation, FK enforcement, cascade deletes, wrong-key rejection, key length validation, schema idempotency, and CRUD round-trips across all tables.
[2026-08-20] - ADL-004: chronogrid_core – Automerge CRDT Integration for Task & Note Registers
- Status: Accepted
- Context: Task 1.4 required integrating the Automerge CRDT engine into
chronogrid_coreso task and note entities support offline-first LWW register mutations with HLC-based causal ordering. - Decision:
- Added
automerge = "0.5"(resolved to v0.5.12) anduuid = "1"withv4feature tochronogrid_core/Cargo.toml. - Extended
schema.sqlwithcrdt_docstable (entity_id TEXT PK,entity_type TEXT,doc_bytes BLOB,updated_at INTEGER) andidx_crdt_docs_typeindex. - Implemented
crdt/sub-module:Hlc(Hybrid Logical Clock withtick()/update()/Display/FromStr),CrdtDoc(generic Automerge wrapper with typed scalar accessors),TaskCrdt(11 mutable fields),NoteCrdt(4 mutable fields). - Each entity mutation stamps the document with an advancing HLC timestamp. Fork/merge delegates to Automerge's deterministic conflict resolution.
Databasegainedsave_crdt_doc(),load_crdt_doc(),delete_crdt_doc()for serialized Automerge blob persistence.- Added
CoreError::CrdtandCoreError::HlcParseerror variants. - 45 unit tests (all passing): HLC ordering/parse, all scalar types, save/load/fork/merge round-trips, CRDT doc DB persistence, schema validation with
crdt_docstable. - Full ADR at
docs/ARCHITECTURE/ADR/0004-core-automerge-crdt-integration.md.
- Added
[2026-08-20] - ADL-005: chronogrid_ffi Crate – Flutter Rust Bridge v2 FFI Layer
- Status: Accepted
- Context: Task 1.5 required generating
flutter_rust_bridgeFFI bindings to bridge all Rust storage, CRDT, and cryptographic methods to the Flutter/Dart presentation layer. - Decision:
- Created
crates/chronogrid_ffi/crate withcdylib+staticlib+libtargets, depending onchronogrid_core,chronogrid_crypto,flutter_rust_bridge = "=2.7.0", andanyhow = "1". - API uses a serialized-bytes pattern: CRDT documents cross FFI as
Vec<u8>(Automerge binary); read functions return DTO structs (FfiTaskData,FfiNoteData,FfiDerivedKey). - Global
Enginesingleton (Mutex<Option<Engine>>) holds theDatabaseconnection +Hlcclock. Thread-safewith_engine()/with_engine_mut()helpers provide access from FRB's isolate thread pool. - 38 public FFI functions across 5 API modules:
engine(init/close/status),database(CRDT doc persistence),crypto(key derivation + platform-gated keychain),task_crdt(18 create/read/set/clear/merge ops),note_crdt(8 create/read/set/clear/merge ops). - All fallible functions return
anyhow::Result<T>, which FRB v2 maps to Dart exceptions. - Keychain functions compile on all platforms but return an error on Android (delegated to Flutter KeyStore bridge).
flutter_rust_bridge.yamlconfigures codegen: Rust input fromapi/, Dart output toapps/chronogrid_flutter/lib/src/rust/.- Flutter
pubspec.yamlupdated withflutter_rust_bridge: ^2.7.0,ffi: ^2.1.0,meta: ^1.11.0. - 25 unit tests (all passing): engine lifecycle, DB CRDT doc persistence, task/note CRDT create/read/mutate/merge round-trips, crypto derivation, stateless read validation, no-engine error paths.
- Full ADR at
docs/ARCHITECTURE/ADR/0005-ffi-bridge-flutter-rust-bridge.md.
- Created
[2026-08-22] - ADL-007: chronogrid_core Schema Extension — Phase 2 Missing Entities (Task 2.1)
- Status: Accepted
- Context: Task 2.1 required extending
docs/API_CONTRACTS/schema.sqlwith 6 missing entity tables identified in the ADL-006 architecture review as a critical gap blocking Phase 3 and Phase 4 development. - Decision:
- Added 6 new tables:
time_blocks,attachments,calendar_feeds,node_peers,automation_rules,ingestion_configs. - All new tables include
hlc_timestamp TEXT NOT NULLfor CRDT compatibility, exceptnode_peerswhich is managed bychronogrid_netand does not participate in CRDT sync. node_peershas no FK tonamespaces— it is a global peer registry scoped to the node, not to a namespace.attachments.storage_pathstores relative paths inside the encrypted vault directory; file bytes are stored outside SQLite to avoid BLOB size limits.calendar_feedsandingestion_configsstore credentials as encrypted blobs (encrypted_password_blob,encrypted_imap_password_blob) — never plaintext.ingestion_configsuses aconfig_typediscriminator (imap|file_watcher|hotkey) with nullable type-specific fields, avoiding a separate table per ingestion type.crdt_docs.entity_typenow supports'time_block'in addition to'task'and'note'.EXPECTED_TABLESincrates/chronogrid_core/src/db.rsupdated from 7 to 13 entries; schema validation enforces all 13 tables on every database open.- 12 new indexes added for all new FK columns and common query patterns.
- 8 new unit tests added covering CRUD round-trips, FK cascade/SET NULL behavior, and
time_blockCRDT doc persistence. - Verification script confirmed: all 13 tables present in schema.sql (PASS), Flutter analysis clean (PASS).
- Full ADR at
docs/ARCHITECTURE/ADR/0007-schema-extension-phase2-entities.md.
- Added 6 new tables:
[2026-08-22] - ADL-008: chronogrid_net Crate — Node Identity & Swarm Key (Task 2.2)
- Status: Accepted
- Context: Task 2.2 required implementing the foundational identity and cluster-authentication primitives for
chronogrid_net: Ed25519 node keypair (NodeIdentity), SHA-256-derivedNodeId, and 256-bit pre-sharedSwarmKey. - Decision:
ed25519-dalek v2chosen for Ed25519 keypair generation (audited,rand_core-compatible, libp2p-compatible for Task 2.3 integration).NodeId= SHA-256(public key bytes) → 32-byte / 64-hex-char stable identifier; maps directly tonode_peers.idprimary key in schema.SwarmKey= 32 bytes fromOsRng;fingerprint()= first 16 hex chars of SHA-256(key) → maps tonode_peers.swarm_key_fingerprint.- Both
NodeIdentityandSwarmKeyimplementZeroizeOnDrop;PersistedIdentityalso zeroized to prevent secret hex strings lingering in heap. SwarmKey::equses constant-time XOR accumulation withblack_boxto mitigate timing side-channels.- Persistence:
NodeIdentity→ JSON (hex-encoded keys);SwarmKey→ 64-char hex file. Both stored inside encrypted vault directory. load_or_create_identity()andSwarmKey::load_or_create()provide idempotent file-backed initialization.- 27 unit tests: generation uniqueness, determinism, hex/bytes round-trips, file I/O, error paths, debug redaction, constant-time equality.
- Full ADR at
docs/ARCHITECTURE/ADR/0008-net-node-identity-swarm-key.md.
[2026-08-22] - ADL-009: chronogrid_net Crate — Transport & Discovery Layer (Task 2.3)
- Status: Accepted
- Context: Task 2.3 required implementing the libp2p transport stack and peer discovery layer in
chronogrid_net, building on the Ed25519NodeIdentityandSwarmKeyfrom Task 2.2. - Decision:
- Added 3 new modules:
peer_event(PeerEventenum),transport(TCP+Noise+Yamux builder,TransportConfig, keypair conversion),discovery(MdnsDiscoveryandKademliaDhtwrappers). - Transport:
build_transport()constructs a TCP + Noise XX + Yamux fallback transport. Primary QUIC transport is deferred toSwarmBuilder::with_quic()in Task 2.5. Noise XX (not XK) is used for TCP because XK requires pre-knowledge of the responder's static key, which is not guaranteed in a dynamic mesh. - SwarmKey authentication: Enforced at the application layer via the
identifyprotocol. The SwarmKey fingerprint (SHA-256(key)[0..8]as 16 hex chars) is embedded in theUserAgentstring (chronogrid/<version>/swarm:<fingerprint>). Peers with mismatched fingerprints are disconnected bySwarmManager(Task 2.5) after identification. - Keypair conversion:
dalek_to_libp2p_keypair()extracts the 32-byte seed fromed25519-dalekand re-imports intolibp2p::identity::ed25519::Keypair. Both use the same curve parameters; the derivedPeerIdis stable. - mDNS: 5-minute TTL, 5-second re-query, IPv4-only, deduplication via
active_peers: HashSet<PeerId>. - Kademlia: In-memory routing store, 60s replication / 5min publication intervals,
add_bootstrap_peer()/bootstrap()API. - Added
tcpandyamuxlibp2p feature flags (omitted from Task 2.2 declaration). - 65 unit tests pass (10 peer_event, 12 transport, 13 discovery, plus 27 from Tasks 2.2 identity/swarm_key, plus 5 doc-tests).
KadEvent::RoutingUpdateduses private libp2p-internal types; routing-table tracking tested via#[cfg(test)] insert_peer_for_test()helper; full coverage deferred to Task 2.5 integration test.- Full ADR at
docs/ARCHITECTURE/ADR/0009-net-transport-discovery-layer.md.
- Added 3 new modules:
[2026-08-22] - ADL-010: chronogrid_net Crate — CRDT Delta Sync Protocol (Task 2.4)
- Status: Accepted
- Context: Task 2.4 required implementing the CRDT delta exchange protocol over libp2p in
chronogrid_net, building on the Ed25519NodeIdentity,SwarmKey, and transport/discovery layers from Tasks 2.2 and 2.3. - Decision:
- Added new
syncmodule with 7 public types:VectorClock,DeltaPacket,SyncCodec,SyncProtocol,GossipEvent,GossipsubBroadcast,NetEvent. VectorClockusesHashMap<entity_id, hlc_timestamp>with lexicographic HLC comparison — no numeric parsing required since the<ms>-<logical>-<node_prefix>format preserves causal ordering under string comparison.DeltaPacketwire format:bincode→lz4_flexcompress →ChaCha20-Poly1305encrypt with 12-byte random nonce prepended. AAD =b"chronogrid-delta"binds ciphertext to protocol context.SyncProtocoluses#[derive(NetworkBehaviour)]wrappinglibp2p::request_response::Behaviour<SyncCodec>.SyncCodecimplements theCodectrait via#[async_trait](required by libp2p-request-response 0.27.0) usingfutures::AsyncRead/AsyncWrite(nottokio::io).GossipsubBroadcastsubscribes to topicchronogrid/state-changes/1with SHA-256 content-addressed message deduplication andValidationMode::Strict.NetEventenum:DeltasReceived { from_peer, deltas },SyncComplete { peer_id },SyncError { peer_id, reason },GossipReceived { entity_type, entity_id, hlc_timestamp, origin_peer }.- Added dependencies:
bincode = "1",lz4_flex = "0.11",chacha20poly1305 = "0.10",generic-array = "0.14",async-trait = "0.1", libp2p"request-response"feature. - 100 unit tests pass + 5 doc-tests pass (35 new sync tests + 65 from Tasks 2.2/2.3).
- Full ADR at
docs/ARCHITECTURE/ADR/0010-net-crdt-delta-sync-protocol.md.
- Added new
[2026-08-22] - ADL-012: chronogrid_core Crate — SyncEngine P2P Integration (Task 2.6)
- Status: Accepted
- Context: Task 2.6 required wiring the P2P sync layer from
chronogrid_netintochronogrid_coreso that CRDT delta packets received from remote peers are automatically merged into the local SQLCipher database, and local CRDT mutations are broadcast to connected peers. - Decision:
chronogrid_netadded as an optional dependency under thep2pfeature flag inchronogrid_core/Cargo.toml;tokio(sync+rt) andtracingalso gated behindp2p.- New
core::syncmodule withSyncEnginestruct: ownsArc<Mutex<Database>>,SwarmHandle, and abroadcast::Receiver<NetEvent>. - Background Tokio task loops on
event_rx.recv():NetEvent::DeltasReceived→merge_remote_deltas()→ Automerge merge → SQL row update. merge_remote_deltas(deltas: Vec<DeltaPacket>) -> Result<Vec<String>>returns affected entity IDs for UI cache invalidation.notify_local_change(entity_id)callsswarm_handle.trigger_sync()to push local mutations to peers.CoreError::Sync(String)variant added for sync-specific errors.SwarmHandle::new_for_test()added tochronogrid_netfor unit test isolation (no live swarm required).- 4 unit tests gated behind
#[cfg(all(test, feature = "p2p"))]: task delta merge, note delta merge, empty delta list, engine construction. - Full ADR at
docs/ARCHITECTURE/ADR/0012-core-sync-engine-p2p-integration.md.
[2026-08-22] - ADL-011: chronogrid_net Crate — P2P Swarm Manager (Task 2.5)
- Status: Accepted
- Context: Task 2.5 required implementing the top-level
SwarmManagerinchronogrid_net, composing all protocol behaviours from Tasks 2.2–2.4 into a single running libp2pSwarmwith a clean public API. - Decision:
- Added new
swarm_managermodule with 4 types:ChronoGridBehaviour(composite#[derive(NetworkBehaviour)]),SwarmCommand(internal control enum),SwarmHandle(cloneable public API),SwarmManager(event loop owner). ChronoGridBehaviourcomposes:MdnsBehaviour+KadBehaviour<MemoryStore>+SyncProtocol+GossipBehaviour+IdentifyBehaviour+relay::client::Behaviour.- Relay client: In libp2p 0.54,
relay::client::new()returnsOutput = Connection(not(PeerId, Muxer)), sowith_other_transport()cannot be used. The correct API iswith_relay_client()in the SwarmBuilder chain, which provides therelay::client::Behaviourto thewith_behaviour()closure. SyncProtocolEvent: The#[derive(NetworkBehaviour)]macro onSyncProtocol(single fieldinner) generatesSyncProtocolEvent::Inner(request_response::Event<...>)— unwrapped via pattern match in the event handler.- Event loop:
tokio::select!multiplexesswarm.next()(requiresfutures::StreamExt) andcmd_rx.recv(). - SwarmKey auth: After
IdentifyEvent::Received, validatesagent_versionagainst expected swarm key fingerprint; disconnects mismatched peers. - CRDT sync flow:
ConnectionEstablished→SyncRequest(local_clock)→SyncResponse(encrypted_deltas, remote_clock)→NetEvent::DeltasReceived+NetEvent::SyncComplete. - Channels:
mpsc(64)forSwarmHandle → SwarmManagercommands;broadcast(256)forSwarmManager → subscribersevents. - 111 unit tests pass + 5 doc-tests pass (11 new swarm_manager tests + 100 from prior tasks).
- Full ADR at
docs/ARCHITECTURE/ADR/0011-net-p2p-swarm-manager.md.
- Added new
[2026-08-22] - ADL-006: Architecture Review Board Assessment & Master Development Plan v2
- Status: Accepted
- Context: A comprehensive senior architecture review was conducted covering all crates, documentation, task structure, and the development plan. Phase 1 is complete and production-quality. Phases 2–5 require a structured execution plan to enable automated agent development.
- Decision:
- Created
docs/ARCHITECTURE/05-architecture-review-assessment.md— full ARB assessment with gap analysis, risk register, and recommendations. - Created
docs/ARCHITECTURE/06-master-development-plan.md— authoritative execution plan superseding OSAD Section 8, with 37 tasks across Phases 2–5, quality gates, and acceptance criteria traceability. - Created
docs/TASKS/phase_2_networking_and_schema.md— 9 tasks for libp2p networking, schema extension, daemon scaffold, and verification pipeline hardening. - Created
docs/TASKS/phase_3_flutter_ui_shell.md— 10 tasks for Flutter BLoC architecture, all 4 primary views, FRB codegen, and build scripts. - Created
docs/TASKS/phase_4_ai_and_automation.md— 9 tasks for NLP engine, IMAP ingestion, file watchers, CalDAV server, and automation rules engine. - Created
docs/TASKS/phase_5_headless_and_release.md— 9 tasks for daemon completion, integration testing, performance testing, and release packaging. - Fixed documentation typos in
docs/UI_UX/02-calendar-timeblocking.mdanddocs/UI_UX/03-kanban-gantt-views.md. - Expanded
docs/WORKFLOWS/01-master-user-story.mdfrom a stub to a substantive workflow reference document. - Identified critical gaps: 6 missing schema tables, FFI type mismatch (
due_date: i64vsu64), incomplete verification script, missing build scripts, unscaffolded daemon crate. - Confirmed all Phase 1 architectural decisions as correct — no reversals required.
- Next task for automated execution: Task 2.1 — Extend Schema with Missing Entities.
- Created
[2026-08-23] - ADL-007: chronogrid_daemon — Headless Peer Node Binary Scaffold
- Status: Accepted
- Context: Task 2.7 required scaffolding the
apps/chronogrid_daemon/headless binary crate, composing all Phase 2 components (chronogrid_crypto,chronogrid_core,chronogrid_net) into a runnable daemon process. - Decision:
- Added
apps/chronogrid_daemonto the Cargo workspacememberslist. - Created
apps/chronogrid_daemon/Cargo.tomlas a[[bin]]crate (chronogrid-daemon) with dependencies:chronogrid_core(featurep2p),chronogrid_crypto,chronogrid_net,clap = "4"(derive),serde(derive),toml = "0.8",tokio(full),tracing,tracing-subscriber(env-filter + fmt),anyhow. apps/chronogrid_daemon/src/main.rsimplements the full startup sequence in 8 annotated steps:- CLI argument parsing via
clap(--config,--db,--log-level). - TOML config loading from platform-default path (
/etc/chronogrid/config.tomlor%APPDATA%\ChronoGrid\config.toml) with graceful fallback to built-in defaults. - Node identity (
NodeIdentity) and swarm key (SwarmKey) load-or-create from configured paths. - Master key resolution: Argon2id KDF keyed from node+swarm fingerprint concatenation + random salt stored in OS keychain. On first boot the key is generated and persisted; on subsequent boots it is retrieved and re-derived.
- Encrypted SQLCipher database opened via
chronogrid_core::Database::open()with automatic parent directory creation. SwarmManager::start()launched with parsedVec<Multiaddr>listen addresses.SyncEngine::new()bound to the swarm handle and started via.run().- Graceful shutdown on SIGINT (all platforms) and SIGTERM (Unix via
tokio::signal::unix).
- CLI argument parsing via
chronogrid_net::lib.rsextended to re-exportlibp2p::MultiaddralongsidePeerId, avoiding a directlibp2pdependency in the daemon crate.apps/chronogrid_daemon/config.example.toml— fully documented reference configuration.docs/DEPLOYMENT/01-headless-daemon-setup.md— comprehensive setup guide covering build, Linux systemd service, systemd unit hardening (NoNewPrivileges, ProtectSystem, etc.), cluster swarm-key distribution, CLI reference, key management, firewall rules, upgrade procedure, and troubleshooting table.- Full ADR at
docs/ARCHITECTURE/ADR/0012-daemon-scaffold.md.
- Added