From b46d0cfab4bfae6e755d3c48b00e15cee2a52373 Mon Sep 17 00:00:00 2001 From: Jacob Durham Date: Wed, 28 Oct 2026 18:08:19 -0700 Subject: [PATCH] docs: align security/contributing docs with OAuth allowlist; fix versions and foundation name --- CHANGELOG.md | 6 +++--- CONTRIBUTING.md | 9 +++++---- README.md | 5 +++-- SECURITY.md | 12 +++++++----- docs/DESIGN.md | 2 +- docs/SECURITY-BOUNDARIES.md | 4 ++-- docs/TODO.md | 4 ++-- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97749ed..078cc03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,10 +105,10 @@ stored score; bump it whenever a feature, weight, or threshold changes. produces clear errors on encryption mode mismatches. The archive is as sensitive as the live database — IMAP secrets are never included and must be re-entered on the target machine via `nodary set-secret`. -- README donate Payment Link for Jacobrakai Foundation - (Donate / Support Jacobrakai Foundation — JACOBRAKAI FOUNDATION 501(c)(3)), +- README donate Payment Link for JACOBRAKAI FOUNDATION + (Donate / Support JACOBRAKAI FOUNDATION — JACOBRAKAI FOUNDATION 501(c)(3)), plus `.github/FUNDING.yml` custom Stripe URL for the GitHub Sponsor button. -- README notes Jacobrakai Foundation 501(c)(3) status (EIN 33-3382083) and +- README notes JACOBRAKAI FOUNDATION 501(c)(3) status (EIN 33-3382083) and IRS Letter 947 dated September 3, 2026. - Privacy-invariant regression tests (`tests/test_privacy_invariants.py`) and a `SECURITY.md` threat-model stub. After a synthetic sync with rich diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ddbfaa..58d19ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,10 +2,11 @@ ## Ground rules (these are the product) -1. **Nothing leaves the machine.** No telemetry, no cloud calls, no external - scoring APIs, no "anonymous" stats. The only network connection this - codebase may open is the read-only IMAP session (and Flask bound to - 127.0.0.1). PRs adding any other outbound traffic will be rejected. +1. **Nothing leaves the machine.** No telemetry, no cloud scoring APIs, no + "anonymous" stats. Allowed network use is the read-only IMAP session, + optional OAuth2 token refresh to the allowlisted Google/Microsoft token + endpoints in `src/nodary/auth/oauth2.py`, and Flask bound to 127.0.0.1. + PRs adding any other outbound traffic will be rejected. 2. **No new persisted content.** Body text, subjects, filenames, and full URLs are never written to the database. If a feature needs new data, derive the minimal structural value (count, extension, registrable diff --git a/README.md b/README.md index 83cff66..88dafeb 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,7 @@ Recompute all derived profiles, tiers, and scores from locally stored facts: ```sh uv run nodary rebuild +uv run nodary status ``` Run `nodary rebuild` after a `tldextract` upgrade to clear PSL drift. The @@ -397,11 +398,11 @@ uv run nodary set-secret installations. - Nodary never sends mail and never modifies mailbox contents. -## Jacobrakai Foundation +## JACOBRAKAI FOUNDATION JACOBRAKAI FOUNDATION (EIN 33-3382083) is an IRC 501(c)(3) public charity under 170(b)(1)(A)(vi), effective February 11, 2025. Contributions are deductible. IRS Letter 947 dated September 3, 2026. -[Donate / Support Jacobrakai Foundation](https://donate.stripe.com/eVq4gy97DanS9h60phfrW00) +[Donate / Support JACOBRAKAI FOUNDATION](https://donate.stripe.com/eVq4gy97DanS9h60phfrW00) — JACOBRAKAI FOUNDATION 501(c)(3) diff --git a/SECURITY.md b/SECURITY.md index 20c4399..627410f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -30,10 +30,11 @@ verdict that a message is malicious. - Bind the dashboard off `127.0.0.1`, or load external dashboard assets. - Publish red-team or exploit writeups against third-party mail providers. -The only network connection the codebase may open is the read-only IMAP -session (plus Flask bound to localhost). Public Suffix List data, the -freemail list, and confusables are vendored; they are not fetched at -runtime. +Allowed network use is the read-only IMAP session, optional OAuth2 token +refresh to the allowlisted Google/Microsoft token endpoints in +`src/nodary/auth/oauth2.py`, and Flask bound to localhost. Public Suffix +List data, the freemail list, and confusables are vendored; they are not +fetched at runtime. ## Privacy invariants @@ -82,4 +83,5 @@ and `tests/test_ui.py`. This is an offline local app. If Nodary persists forbidden content, writes credentials into the database, or opens outbound traffic beyond read-only -IMAP, open a GitHub issue against this repository. +IMAP and the allowlisted OAuth2 token endpoints, open a GitHub issue against +this repository. diff --git a/docs/DESIGN.md b/docs/DESIGN.md index c490411..4649f36 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -1,6 +1,6 @@ # Nodary Design -Status: **current implementation, v0.3.0**. The database schema in +Status: **current implementation, v0.3.1**. The database schema in `src/nodary/storage/schema.sql` is authoritative for table/column details. Nodary is a local-first, deterministic behavioral-analysis tool for BEC and diff --git a/docs/SECURITY-BOUNDARIES.md b/docs/SECURITY-BOUNDARIES.md index 2d41a38..d750617 100644 --- a/docs/SECURITY-BOUNDARIES.md +++ b/docs/SECURITY-BOUNDARIES.md @@ -5,12 +5,12 @@ Local-first, read-only analysis. This document states the isolation invariants t ## Trust boundary - **Process:** `nodary sync` and `nodary ui` run as the local user. Dashboard binds `127.0.0.1` only (never `0.0.0.0`). No cloud scoring, no telemetry. -- **Network:** No outbound requests after install. `tldextract` uses vendored PSL snapshot (`suffix_list_urls=()`), confusables from `data/uts39/confusables.txt`; `auth/oauth2.py` allowlists only Google + Microsoft token endpoints. +- **Network:** After install, outbound traffic is limited to read-only IMAP and the allowlisted Google/Microsoft OAuth2 token endpoints in `auth/oauth2.py`. `tldextract` uses a vendored PSL snapshot (`suffix_list_urls=()`); confusables come from `data/uts39/confusables.txt`. - **TLS:** `ui/tls.py` generates mkcert local CA cert; fallback plain HTTP warns. No secrets in DB. ## Data projection allowlist -`ui/server.py:_SCORED_MESSAGE_COLS` is the ONLY projection for scored rows. It explicitly excludes subject, body, filenames, full URLs. The dashboard is self-contained HTML (`src/nodary/ui/templates/index.html` — 605 lines, inline style+script, `tests/test_ui.py` asserts no `http://` `src=`). +`ui/server.py:_SCORED_MESSAGE_COLS` is the ONLY projection for scored rows. It explicitly excludes subject, body, filenames, full URLs. The dashboard is self-contained HTML (`src/nodary/ui/templates/index.html` — inline style+script; `tests/test_ui.py` asserts no `http://` `src=`). ```sql _SCORED_MESSAGE_COLS = "m.id, m.message_id, m.sender_id, m.from_email_norm, diff --git a/docs/TODO.md b/docs/TODO.md index d0176b5..789bac6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -11,8 +11,8 @@ work; `CHANGELOG.md` remains the release-history source of truth. structure parsing without persisting body text, subjects, filenames, or full URLs. Why: keeps the behavioral profile privacy-preserving. - [x] Sender/domain profiles, trust tiers 0-3, and deterministic scoring engine - v1.0.0 with stored feature explanations. Why: scores are recomputable and - inspectable. + v1.1.0 (`ENGINE_VERSION`) with stored feature explanations. Why: scores + are recomputable and inspectable. - [x] `nodary rebuild` replay path. Why: profiles, tiers, and scores can be regenerated from fact tables after backfills or schema/profile changes. - [x] SQLCipher-at-rest support keyed from the OS keychain, plus visible