144 lines
5.5 KiB
Markdown
144 lines
5.5 KiB
Markdown
# ADR-0012: chronogrid_daemon — Headless Peer Node Binary Scaffold
|
||
|
||
**Date:** 2026-08-23
|
||
**Status:** Accepted
|
||
**Task:** 2.7
|
||
**Author:** Automated Agent (Cline)
|
||
|
||
---
|
||
|
||
## Context
|
||
|
||
Phase 2 completed the P2P networking stack (`chronogrid_net`) and the
|
||
core-to-net sync bridge (`chronogrid_core` with `p2p` feature). The missing
|
||
piece was a runnable binary that composes these components into a headless
|
||
background process suitable for deployment on Linux servers, NAS devices,
|
||
or always-on Windows machines.
|
||
|
||
Requirements (from `docs/TASKS/phase_2_networking_and_schema.md`, Task 2.7):
|
||
|
||
- Binary crate `chronogrid-daemon` in `apps/chronogrid_daemon/`.
|
||
- CLI args via `clap`: `--config`, `--db`, `--log-level`.
|
||
- TOML config loading with platform-default fallback.
|
||
- `chronogrid_crypto` key derivation + OS keychain integration.
|
||
- `chronogrid_core` database initialization.
|
||
- `chronogrid_net` SwarmManager startup.
|
||
- Structured logging via `tracing`.
|
||
- Graceful shutdown on SIGTERM/SIGINT.
|
||
- `config.example.toml` documentation.
|
||
- `docs/DEPLOYMENT/01-headless-daemon-setup.md` setup guide.
|
||
|
||
---
|
||
|
||
## Decision
|
||
|
||
### 1. Crate Structure
|
||
|
||
Added `apps/chronogrid_daemon` to the workspace `Cargo.toml` members list.
|
||
The crate is a pure `[[bin]]` crate (no `[lib]` target) named `chronogrid-daemon`.
|
||
|
||
Dependencies are intentionally minimal — all heavy lifting is delegated to the
|
||
three internal crates (`chronogrid_core`, `chronogrid_crypto`, `chronogrid_net`).
|
||
|
||
### 2. CLI Argument Design
|
||
|
||
`clap` with `derive` is used for zero-boilerplate CLI argument parsing.
|
||
Three arguments:
|
||
|
||
| Flag | Type | Purpose |
|
||
|---|---|---|
|
||
| `-c`/`--config` | `Option<PathBuf>` | Config file path (optional) |
|
||
| `--db` | `Option<PathBuf>` | DB path override (optional) |
|
||
| `--log-level` | `String` | Tracing filter (default: `info`) |
|
||
|
||
### 3. TOML Configuration
|
||
|
||
Four config sections with `#[serde(default)]` on every field, allowing partial
|
||
or empty config files. The `Config::default()` implementation provides sensible
|
||
platform-aware defaults:
|
||
|
||
- **Linux defaults**: `/var/lib/chronogrid/` for all data files, `/etc/chronogrid/config.toml`.
|
||
- **Windows defaults**: `%APPDATA%\ChronoGrid\` for all data files.
|
||
|
||
### 4. Headless Key Management
|
||
|
||
The daemon does not require a human-entered passphrase. Instead:
|
||
|
||
1. The "passphrase" for Argon2id is synthesized from the node's `NodeId` hex +
|
||
the cluster `SwarmKey` fingerprint: `"chronogrid-daemon:<node_id>:<fingerprint>"`.
|
||
2. A fresh 16-byte random salt is generated on first boot and stored together
|
||
with the 32-byte derived key as a 48-byte blob in the OS keychain.
|
||
3. On subsequent boots, the 48-byte blob is retrieved, the salt extracted, and
|
||
the key re-derived — providing cryptographic verification that the retrieved
|
||
credential is correct for this node.
|
||
|
||
This approach provides per-node database encryption uniqueness without requiring
|
||
interactive password input, while still leveraging the OS keychain for
|
||
credential protection (DPAPI on Windows, SecretService on Linux).
|
||
|
||
### 5. Startup Sequence (8 Steps)
|
||
|
||
```
|
||
Step 1: Init tracing (log_level from CLI or RUST_LOG env)
|
||
Step 2: Load TOML config (graceful fallback to defaults)
|
||
Step 3: load_or_create_identity() + SwarmKey::load_or_create()
|
||
Step 4: resolve_master_key() — Argon2id KDF + OS keychain
|
||
Step 5: Database::open() — SQLCipher + schema validation
|
||
Step 6: SwarmManager::start() — libp2p swarm with mDNS/Kad/Gossipsub
|
||
Step 7: SyncEngine::new().run() — bridges swarm events to CRDT DB
|
||
Step 8: shutdown_signal().await — blocks until SIGINT/SIGTERM
|
||
```
|
||
|
||
### 6. Graceful Shutdown
|
||
|
||
`tokio::signal` is used for cross-platform signal handling:
|
||
- `ctrl_c()` for SIGINT on all platforms.
|
||
- `signal(SignalKind::terminate())` for SIGTERM on Unix (`#[cfg(unix)]`).
|
||
- Windows only handles SIGINT (SIGTERM is not a native Win32 concept).
|
||
|
||
The shutdown sequence: `SwarmHandle::shutdown()` → join `SyncEngine` task.
|
||
|
||
### 7. `libp2p::Multiaddr` Re-export
|
||
|
||
Rather than adding a direct `libp2p` dependency to `chronogrid_daemon`,
|
||
`chronogrid_net::lib.rs` was extended to re-export `libp2p::Multiaddr`
|
||
alongside the existing `libp2p::PeerId` re-export. This keeps `chronogrid_net`
|
||
as the single dependency for all libp2p types used by downstream crates.
|
||
|
||
---
|
||
|
||
## Consequences
|
||
|
||
### Positive
|
||
|
||
- Single binary with zero runtime dependencies beyond the OS keychain service.
|
||
- First-boot experience is fully automated (key generation, schema init).
|
||
- `config.example.toml` provides comprehensive documentation in the file itself.
|
||
- Systemd unit uses security hardening directives (`NoNewPrivileges`,
|
||
`ProtectSystem=strict`, `ReadWritePaths=/var/lib/chronogrid`).
|
||
- `Multiaddr` re-export simplifies downstream crate dependency graphs.
|
||
|
||
### Neutral
|
||
|
||
- Argon2id KDF runs on every boot (even when retrieving from keychain) to verify
|
||
the credential. This takes ~1–2 s at 64 MiB / 3 iterations — acceptable for
|
||
a daemon that starts infrequently.
|
||
|
||
### Negative / Risks
|
||
|
||
- **Android**: The keychain module is excluded on Android (`cfg(not(target_os = "android"))`).
|
||
The daemon binary is not intended for Android — the Flutter KeyStore bridge
|
||
(Task 1.5) handles that platform.
|
||
- **Passphrase-based key mode not yet exposed**: The current daemon uses only
|
||
the automated node+swarm-fingerprint passphrase. A future enhancement could
|
||
add `--passphrase` for user-supplied secrets on shared-hardware deployments.
|
||
|
||
---
|
||
|
||
## Related
|
||
|
||
- ADR-0002: `chronogrid_crypto` Argon2id KDF & OS Keychain
|
||
- ADR-0003: `chronogrid_core` SQLCipher Database Initializer
|
||
- ADR-0011: P2P Swarm Manager (`SwarmManager`, `SwarmHandle`)
|
||
- Task 2.8: Expand Verification Pipeline (next task)
|