remnix logo

Threat model

What remnix detects, prevents, and accepts on untrusted remotes.

Remote storage and the network are untrusted. Local SQLite, the OS keyring, and the recovery key / authenticators are trusted on a given device.

Classifications:

#ThreatClassNotes
1Rollback to older valid metadata/manifestdetectedpullMetadata rejects rm.Counter < trusted
2Replay of old event bundlesaccepted / recoverableIdempotent apply by origin seq; old ciphertext still decrypts
3Deleted/withheld acknowledgementacceptedBlocks GC until retire; history remains
4Checkpoint rollbackdetected / recoverableTrusted checkpoint id; key recover reloads snapshot
5Concurrent key rotationsdetectedGeneration seq uniqueness; publish is lock-serialized
6Active device retired while offlineacceptedOffline device cannot ack; retire is explicit
7Partial uploadpreventedPutAtomic; rclone does not mark success on a truncated stream
8Truncated objectdetectedDecrypt/verify fail; FaultTransport tests
9Duplicate event objectprevented / recoverableOrigin (device, seq) identity; insert ignore
10Stale listing from cloudaccepted / detectedNext sync sees new listing; FaultTransport StaleList
11Delete then stale reappearanceacceptedTombstones + GC plan; cloud eventual consistency
12Different remote views across devicesacceptedSync converges; not a global snapshot isolation
13Clock skewacceptedRanking uses timestamps; protocol identity is seq/counter not wall clock
14Tampering with encrypted object bytesdetectedAEAD / MAC failure
15Forged/modified manifestdetectedHMAC over generation and remote manifests
16Copied metadata from another repositorydetectedProbe + MAC; foreign SMK cannot unwrap

Rows are not all “prevented” on purpose. The protocol was not rewritten solely to close accepted limitations. Follow-ups belong in issues, not silent format breaks.

Additional controls: portable YAML has no credentials; rclone.conf lives in the data directory (0600) beside local.yaml, not in the commit-safe config dir; logs, doctor, daemon status, and callback tails pass through internal/redact; history insert skips REMNIX_RECOVERY_KEY= and similar.