# Novara Security Policy

English · 简体中文

> See also: [Threat Model](docs/threat-model.md) — assets, adversaries, assumptions, non-goals and mitigations, in full. · [Security Architecture](docs/security-architecture.md) — the four trust chains (data at rest / agent access / snapshots / sync) at a glance, and how to verify your download. · [安全架构(中文)](docs/security-architecture.zh-CN.md) · [威胁模型(中文)](docs/threat-model.zh-CN.md) --- > This document has two jobs: (1) tell security researchers **how to report a vulnerability** privately, and (2) explain to users **how Novara protects their data and what it does not protect**. > > **Effective date:** 2026-08-14 · **Last updated:** 2026-10-28 · **Applies to:** Novara novara12 / 12.0 (and earlier versions where noted) --- ## 1. Supported versions Security fixes are provided for the versions below. We strongly recommend always running the latest release. | Version | Status | Notes | |---------|--------|-------| | novara12 (12.0) | ✅ Supported | Current release — interaction-layer rework: card folding with follow memory, path properties, on-card API detection, todo progress; no security-model changes | | novara11.R1 (11.1) | ✅ Supported | Previous release — HTML editor image insertion, image selection and toolbar focus fixes | | novara11 (11.0) | ✅ Supported | Previous release — desktop on .NET 10, v5 database container (Argon2id), partition-level incremental sync, signed update manifests with an optional automatic check, and a codebase-wide hardening pass | | 10.3 | ✅ Supported | Save dialogs append the selected file type's extension again (10.1–10.2 regression) | | 10.2 | ✅ Supported | Markdown split-view rework and editor feedback fixes | | 10.1 | ✅ Supported | Picker fix, semantic toasts, keyless signatures shipping | | 10.0 | ✅ Supported | The trust release: signature verification, reproducible builds, a public threat model, and dependency, licence and coverage gates in CI | | 9.4 | ✅ Supported | Derivation cache and UI-thread offload (performance), plus a full hardening round across desktop and self-hosted server | | 9.3 | ✅ Supported | Hotfix line on top of 9.2 | | 9.2 | ✅ Supported | Hotfix line on top of 9.1 | | 9.1 | ✅ Supported | Hotfix line on top of 9.0 | | 9.0 | ✅ Supported | First release with one-command self-hosting (official Docker image + compose) | | 8.0 | ✅ Supported | First release of cross-device sync through a server you host | | 7.0 | ✅ Supported | First release of the connected era (encrypted snapshots) | | 6.2 | ✅ Supported | Receives critical fixes where feasible | | 6.0 | ✅ Supported | Receives critical fixes where feasible | | 5.x | ✅ Supported | Receives critical fixes where feasible | | 4.0 | ✅ Supported | Receives critical fixes where feasible | | 3.0 | ⚠️ Legacy | Receives critical fixes where feasible | | 2.0 | ⚠️ Legacy | Uses the older AES-CBC encryption; **upgrade recommended** (see Section 7) | | < 2.0 | ❌ Unsupported | | ## 2. Reporting a vulnerability We take security reports seriously and will never penalize good-faith security research. **Please report privately first** — do not open a public issue with exploit details. - **Preferred:** GitHub → **Security** tab → **Report a vulnerability** (Private Vulnerability Reporting). - **Alternative:** Email **owner@novara.xin** with the subject line `[Security] ...`. Please include: 1. the affected version(s); 2. a clear description of the issue and its potential impact; 3. steps to reproduce (or a proof of concept), if available; 4. any suggested fix (optional). **What to expect:** - We aim to acknowledge your report within **5 business days**. - We will keep you updated on our assessment and fix plan. - **High-severity issues will not be disclosed publicly until a fix is released.** Lower-severity issues will be coordinated with you. - Credit: we are happy to credit reporters in release notes unless you ask to remain anonymous. ## 3. Security model & threat model **What we protect.** Novara is a *local-first personal data control layer* — for you and for your AI agents. Its security goal is to protect your data **at rest** — the single database file on disk — and, since 7.0, wherever you choose to take it — snapshots, and since 8.0 sync between your own devices — against: - casual or unauthorized reading when the app is locked or closed (via optional encryption); - silent tampering of the encrypted file (via AES-GCM authentication); - accidental data loss (via atomic writes, a recycle bin, and local backups); - brute-force guessing of the lock password (via lockout and a monotonic clock). **What we do NOT protect (honest limits).** A local application cannot defend against everything. Novara does **not** protect against: - **An unlocked session** — while the app is unlocked and in use, an attacker who can operate your machine can see what you see. - **Malware on your machine** — keyloggers, screen recorders, RATs, or a compromised Windows account can observe or exfiltrate data regardless of Novara's encryption. - **Physical access** — someone with physical access to your unlocked device, or who can reset your Windows password, can potentially reach your files. - **A lost/forgotten password** — by design there is no recovery. Encryption that has no backdoor also means no password reset. - **Memory extraction** — encryption keys exist in memory while the app is unlocked; a sophisticated local attacker with the right tools may extract them. > **Bottom line:** Novara's encryption protects your data *at rest on disk*. It is not a substitute for a healthy, malware-free, physically-secured machine. > **The full model.** This section is the summary. The assets, the adversary classes, the assumptions we depend on but do not control, what we deliberately do not protect, and the mitigation behind each boundary are written out in the **[Threat Model](docs/threat-model.md)**. ## 4. Encryption architecture When the privacy lock is enabled, Novara encrypts the entire database: | Aspect | Detail | |--------|--------| | Cipher | AES-256-GCM (authenticated encryption), 256-bit key | | Key derivation | PBKDF2-SHA256, **3,000,000 iterations** since format v3 (5.3+; ≈340 ms unlock on the reference machine); per-user random 32-byte salt. v2 databases (100,000 iterations) migrate via a one-time opt-in prompt | | Password | 6–64 characters; only a versioned, salted password hash is stored locally — PBKDF2-SHA256 (3,000,000 iterations) since the hash format's second revision, never the plaintext | | Authentication | 12-byte random nonce + 16-byte GCM tag — any tampering is detected | | Data flow | JSON → GZip compression → AES encryption → on-disk file | | File format | Versioned header (`NOVA` magic + version + encryption flag + 16-byte integrity field) + encrypted body | | Migration | Legacy v1 (AES-CBC, Novara 2.0) auto-migrates to v2 (GCM) after one user confirmation; v2 migrates to v3 (hardened KDF) via a one-time opt-in prompt | **Important:** encryption is **off by default**. Without the privacy lock, the database is a plaintext JSON file (protected only by your Windows account permissions). This is a deliberate design choice so that casual users are never locked out of their own data. ### Snapshot exports (7.0+) The connected era adds a new way for data to leave your machine — and the same bar applies: **everything that leaves is encrypted, or it does not leave.** - **Same container, same contract** — a snapshot embeds the database as a `.novaenc` v4 ciphertext block (44-byte self-describing header used as AES-GCM additional data, PBKDF2-SHA256 at 3,000,000 iterations, gzip-compressed payload), the identical versioned contract as encrypted backups. - **Mandatory password gate** — every snapshot requires a password at export: your privacy-lock password or an independent one you enter for that export. A plaintext vault takes exactly the same dialog — the exported file is always the encrypted v4 container, so there is no plaintext export path either way. - **Credentials never ride along** — MCP tokens and per-client authorization data are stripped from the exported settings before the snapshot is sealed. - **Zero network by construction** — the exported viewer file performs no network requests whatsoever: no CDN, no fonts, no telemetry. Decryption happens in your browser via the WebCrypto API; the file never "phones home" because there is no home to phone. - **Password is the only key** — the snapshot password is not stored anywhere by Novara and cannot be recovered. Hosting a snapshot publicly is safe only as far as the password is strong; the app and the deployment guide both say so plainly. ### Sync (8.0+) 8.0 lets your devices stay in step through a server you run yourself — and that server is a **ciphertext store, not a service that can read you**. The same bar applies as everywhere else: nothing leaves unencrypted, and the server holds no key. - **The server never sees plaintext** — it stores one versioned ciphertext blob per change plus a small plaintext envelope (container / crypto / sync version numbers, the base version the payload was built on, a device id and a timestamp). It holds no space key, has no user accounts, performs no decryption, and keeps no plaintext logs. - **Key separation** — the space key is generated on the first device and never leaves the devices. Pairing uses a space id and an enrollment secret the server prints exactly once; each device then carries its own token. Revoking a lost phone therefore costs nothing: the ciphertext it could have downloaded is useless without a key it never had. - **Same container, same contract** — every payload is the `.novaenc` v4 container (44-byte self-describing header used as AES-GCM additional data, PBKDF2-SHA256 at 3,000,000 iterations, gzip-compressed body). Enabling sync requires the privacy lock; a plaintext vault is refused. - **Transport** — the protocol assumes HTTPS. The browser side needs a secure context, so the deployment guide recommends a TLS-terminating reverse proxy with automatic certificates (or a private network link), and the bundled server binds to loopback by default. - **Token handling** — the server stores only a SHA-256 of each device token, compares in constant time, and rate-limits repeated failures. - **Integrity and version discipline** — an upload declares the version it is based on; a mismatch is reported as a conflict instead of silently overwriting, and the overwritten version is retained on the server so a bad push can be rolled back. - **Local audit** — every upload, download, conflict detection and conflict resolution is recorded in a local audit log, and the device center shows each paired device's trust state and last sync. ## 5. Data integrity & reliability Novara includes several layers to prevent data loss and corruption: - **Atomic writes** — every save writes to a temporary file, flushes to disk (`Flush(true)`), then atomically replaces the original. A crash mid-write cannot corrupt the database. - **Single-writer discipline** — a debounced/serialized save queue and a single-instance mutex prevent concurrent writers from clobbering the file. - **Exclusive file lock + Hidden/ReadOnly attributes** — reduce the chance of third-party software accidentally modifying the database. - **Recycle bin (soft delete)** — deleted cards are recoverable for 7 days, then permanently purged on the next startup. - **Rolling local backups** — the built-in backup keeps up to 10 local snapshots you can restore from (4.0+). - **Crash logs (local, redacted)** — crash details are written locally (4.0+) for later diagnosis; sensitive values (e.g. API keys) are redacted and they are never auto-uploaded. - **Integrity checks** — plaintext exports carry a SHA-256 header (dual-header detection keeps older MD5-headered files importable); encrypted backups are authenticated by GCM; imports are validated, normalized, and rolled back on failure. ## 6. Anti-brute-force & lockout - After **5 consecutive wrong passwords**, unlocking is blocked for **30 minutes**. - The failure count and lock deadline persist across restarts (`lockout.dat`), so restarting the app does not reset the lockout. - Since 4.0, the lockout uses a **monotonic clock** (`Environment.TickCount64`), preventing a bypass by rolling the system clock backward. - The lock screen clears its input when the window loses focus, reducing shoulder-surfing. ## 7. Version compatibility & downgrade warnings | Scenario | Behavior | |----------|----------| | 2.0 data opened in 3.0+ | ✅ Read and migrated from AES-CBC (v1) to AES-GCM (v2) after one confirmation | | v2 data opened in 5.3+ | ✅ Readable as-is; migrates to v3 (3,000,000-iteration KDF) via a one-time opt-in prompt | | v3 data opened in 5.2.0 or earlier | ❌ **Not readable** — older versions do not understand the v3 KDF parameters | | v2/v3 data opened in 2.0 | ❌ **Not readable** — 2.0 does not understand the GCM format | > ⚠️ **Before downgrading or rolling back to 2.0**, export a plaintext backup. Once a database has been migrated to GCM (v2) or v3, older versions cannot open it. The v3 migration dialog states this downgrade limit before you opt in. ## 8. Application hardening - **XSS protection in the diary editor** — rich-text HTML is sanitized on load, on save, and after navigation against a strict tag/attribute/URL whitelist, using a real HTML parser (AngleSharp). Script tags, `on*` event attributes (including entity-encoded variants such as `onload`), and dangerous protocols (`javascript:`, `vbscript:`, non-image `data:`) are stripped. Titles are rendered as plain text. - **No code evaluation of untrusted input** — imported HTML and JSON are parsed and normalized, never executed. - **Local-only helper process** — the desktop-sticky-note helper communicates with the main app via local files and named events on the same machine; it makes no network requests and opens no listening ports. - **Minimal surface** — the Novara desktop app opens no listening ports and exposes no remote-procedure-call surface to the network. Outbound traffic happens only on paths you trigger: the API-key detection tiers, cross-device sync to the self-hosted server you paired (8.0+), and the in-app update check against a static release manifest (9.0+) — each appears in the network activity panel (see the Privacy Policy). The optional MCP server is a local named-pipe endpoint only, reachable from the same machine. The separately installed sync server (`NovaraSync`) does listen for connections — it is a component you choose to run yourself, it handles ciphertext only, and it is not part of the desktop app. The exported Snapshot viewer file performs no network requests at all. ## 9. Developer commitments As the project owner, I commit to: 1. **Never** operating a cloud server that collects user data. Any server-side component the project ships in the connected era is self-hosted **by you** and designed to handle ciphertext only — plaintext never reaches it by construction. 2. **Never** adding a backdoor, a password-recovery bypass, or any remote unlock/exfiltration mechanism. 3. **Never** adding telemetry, analytics, or ads. 4. **Never** auto-updating or auto-uploading data without explicit user action. 5. **Disclosing** security issues transparently: fix first, then publish, with credit to reporters. ## 10. MCP interface security model The optional MCP interface lets an AI agent read and write cards. Its security model is defense-in-depth: - **Off by default** — the interface is disabled until you enable it in Settings and copy a token. - **Token authentication** — a per-user token, compared in fixed time (`CryptographicOperations.FixedTimeEquals`), passed via `NOVARA_MCP_TOKEN` or `--token`. - **Database-unlock gate** — a locked or encrypted database refuses every request; an agent can never read encrypted data without unlocking. - **Per-client approval** — the first connection from any client process requires explicit approval, persisted locally. - **Per-client permission matrix** — each approved client holds its own 20-bit matrix (read / create / update / delete across the five data types). New clients start **read-only everywhere except memos**; a mixed (all-types) listing never leaks data from partitions the client cannot read. - **Deletion master switch** — deleting requires both the client's own delete bit and a global toggle; the two are AND-ed so flipping one alone cannot enable deletion. - **Audit log** — every call, including denied attempts, is recorded locally with process, time, tool, target, and result; fields are length-capped and credential-shaped values are masked. - **Sensitive-field redaction** — password/key/token fields are returned as `****`, excluded from search, and protected against relabeling-based extraction. - **Architecture** — `NovaraMCP.exe` is a zero-logic stdio frontend; all data access happens inside the running Novara process (the single data authority), never by the agent touching files directly. Malformed client input answers a spec-compliant error instead of hanging the connection. The core principle — Novara itself never transmits your data — is preserved. See the Privacy Policy for the data boundary when a cloud-hosted AI client is connected. ## 11. Future changes Security hardening continues across releases. The 6.0 round alone tightened MCP input handling, export/import round-trips, and backup-path validation; future updates will keep narrowing the attack surface. Each release's changelog lists security-relevant changes. ## 12. Contact - **Email:** owner@novara.xin - **GitHub:** https://github.com/Novara-owner/Novara-Vault