CivicNewspaper Architecture

CivicNewspaper is the repository name. The installed desktop app is The Civic Desk.

The product is a local-first newsroom desktop app for small local publishers. It watches civic sources, imports source lists, finds leads, stores evidence, helps an editor draft and verify stories, and exports or publishes a static local paper.

The system is advisory about editorial judgment. It can rank, warn, summarize, and suggest verification work. It must not make the editor's news judgment for them, but deterministic package-integrity checks can block static approval/export until broken evidence, empty copy, reporter-note scaffolding, or unsupported citations are fixed.

System Overview

flowchart LR
    Editor["Editor"] --> UI["React desktop UI"]
    UI --> Tauri["Tauri command bridge"]
    Tauri --> Core["Rust core modules"]
    Core --> DB["SQLite app database"]
    Core --> Files["Local output folders"]
    Core --> Ollama["Ollama local AI"]
    Browser["Browser extension"] --> Loopback["127.0.0.1 pairing server"]
    Loopback --> Core
    Core --> Web["Public web sources"]
    Core --> Publishers["Publishing providers"]
    Publishers --> Here["here.now"]
    Publishers --> Pages["GitHub Pages"]
    Publishers --> CF["Cloudflare Pages"]
    Publishers --> Netlify["Netlify"]
    Publishers --> WP["WordPress"]
    Publishers --> Substack["Substack assisted workflow"]

Major Components

Desktop Shell

Local AI

The app uses Ollama for local model calls when available. The AI path supports:

The app must degrade when Ollama is missing, slow, offline, or when a model is not installed. Deterministic fetch, import, review, publish, and backup flows should remain usable without AI.

Source Intake

Source intake has three main paths:

PDF source-list import is intentionally disabled in the public beta. Editors should convert PDF source lists to TXT, CSV, DOCX, or XLSX, or paste URLs directly.

Browser Extension

The browser extension is a local helper for sending pages into The Civic Desk while the editor reads.

Newsroom Flow

flowchart TD
    Sources["Sources"] --> Fetch["Fetch and import evidence"]
    Fetch --> Observe["Create observations"]
    Observe --> Entities["Extract entities"]
    Observe --> Diff["Detect changes"]
    Observe --> Leads["Create leads"]
    Leads --> Queue["Story Queue"]
    Leads --> Dark["Dark Signals"]
    Queue --> Verify["Verification Queue"]
    Dark --> Verify
    Verify --> Draft["Workbench draft"]
    Draft --> Advisor["Optional advisor checks"]
    Advisor --> Human["Human edit and approval"]
    Human --> Publish["Compile, export, publish, share"]

Civic Intelligence Layer

The civic intelligence layer converts raw evidence into structured newsroom observations.

Observation types include:

Entity types include:

The app stores observations and entities so the editor can trace why a lead exists, what changed, and which source produced it.

Dark Signal Desk

The Dark Signal Desk is for weak, early, unusual, or socially surfaced signals that might become stories.

It should:

It must not hide information from the editor. Ranking is not censorship. The editor decides what to pursue.

Verification Queue

The verification queue turns leads and signals into action items.

Task states:

Tasks can link back to stories, signals, sources, entities, and evidence.

Workbench And Editorial Guardrails

The Workbench is where writers and editors draft, revise, hold, approve, or return stories for more work.

Guardrails and the press-freedom/legal-risk advisor are advisory only. They can warn about sourcing, attribution, defamation risk, privacy risk, public/private figure questions, and verification gaps. They do not make the editor's decision. Separately, deterministic static-package checks can visibly block approval/export until the public package is valid.

Publisher identity, organization type, tone, copyright/footer text, and public site language are configurable so the app does not invent the publisher's business model or editorial policy.

Publishing

Publishing is built around static output first.

Default publishing order:

  1. here.now for simple temporary civic website publishing.
  2. GitHub Pages for a durable public archive.
  3. Netlify for credentialed technical users.
  4. Assisted/manual WordPress and Cloudflare Pages hosting by exporting the folder or ZIP and recording the public URL.
  5. Substack/newsletter as distribution, not the canonical archive.

The app can produce:

Publisher Connector Layer

The Rust publisher layer uses provider-neutral concepts:

Supported provider families in the current code:

WordPress and Substack are currently assisted workflows: generate or export the publishable package, publish in the outside service, and allow the user to store the final public URL after posting.

Data Storage

SQLite is the application database. Schema changes are applied by migration files in src-tauri/migrations/; there is no 0002 migration. Applied schema version is tracked with PRAGMA user_version.

Current migration files:

Evidence identity uses content_hash as the canonical text hash, but duplicate suppression is scoped to (source_id, url, content_hash) so identical notices from different sources remain separate corroborating observations.

Current primary tables:

The Stage 3 story-quality layer depends on these newer supporting tables:

Security Model

Security boundaries are local-first, not offline-only.

Deployment Model

The release build creates desktop installers from the Tauri app. v0.3.3 adds an ARM64-only macOS DMG alongside the signed Windows NSIS installer. A single publisher job collects both verified artifacts before creating the draft release and combined checksum manifest.

The Windows NSIS package uses Tauri's WebView2 download bootstrapper. On a machine without Microsoft Edge WebView2 Runtime, initial installation therefore requires network access to obtain that prerequisite; the current public-beta package is not an offline installer. This is separate from first-run local AI setup, which downloads the app-managed Ollama runtime and selected model.

Windows package signing is part of the current release artifact gate. Tauri signs the application executable, generated NSIS uninstaller, and outer installer during bundling; CI then installs the candidate and fails closed unless all three Authenticode signatures and timestamps are valid. The v0.3.3 Apple Silicon beta is intentionally not Developer ID signed or notarized; its gate instead requires ARM64 inspection, exact-DMG checksums, explicit unsigned-release disclosure, and Apple Silicon clean-machine proof. Linux is deferred to v0.3.4.

Current Stable-Release Gaps

The app has public-beta functionality, but stable release still needs: