Everything You Ever Wanted to Know About Chidi.md

The Definitive Architectural Codex, System Internals, and Operating Manual

Release v0.2.0 Vanilla ES2020+ JavaScript Zero Build / No npm Air-Gapped Local LLM Strict DOMPurify Sanitization
The Three Standard Architectural Inquiries

Every subsystem, interface boundary, and user control in this comprehensive manual answers three foundational architectural questions: What it does (observable inputs, state transitions, and behaviors), How it works (execution trace across the DOM, asynchronous event loop, and network protocols), and Why it works (software engineering invariants, threat mitigation, and architectural decisions).

Book I: Systems Philosophy & Origins

1. The Soul of a Local-First Terminal Reader

In a computational landscape increasingly dominated by invasive telemetry, opaque cloud services, and bloated electron runtimes, Chidi.md represents a deliberate return to clarity, restraint, and user sovereignty. Named after the indecisive moral philosopher who endlessly weighed every ethical ramification, Chidi.md approaches personal notes and document collections with unyielding respect for user privacy and data locality.

Chidi.md is a single-page, no-build web application designed to load, display, and analyze personal Markdown collections in a distraction-free, retro green/amber console environment. It enables users to browse extensive folder hierarchies of .md documents, synthesize summaries, brainstorm questions, converse through threaded follow-up dialogues, and conduct deep multi-document research queries across their entire knowledge base.

The Five Core Invariants of Chidi.md
  1. Zero Build Step, Zero npm Tooling (D-001): Clone the repository and serve it immediately. The runtime requires only three core files—index.html, main.js, and style.css—accompanied by pinned CDN libraries.
  2. Absolute Local-Only AI (D-007): No hosted artificial intelligence APIs, no remote proxies, and no API keys. All model inference occurs on a user-controlled local server (such as Ollama or an OpenAI-compatible daemon) listening strictly on loopback interfaces.
  3. Untrusted Input Boundary (D-006, P1-01): Both user Markdown files and model responses are treated as untrusted inputs. They reach the DOM strictly through DOMPurify sanitization.
  4. View Invariance & Race Condition Immunity (P2-05): Model generation takes time. An answer is displayed only under the document view that requested it; late responses to discarded views are quarantined and dropped.
  5. Deterministic Session Durability (D-003): State persistence resides entirely under a single, well-defined localStorage key (chidiMdSession), protecting sessions across page reloads without external databases.

2. Air-Gapped AI & The Zero-Key Mandate

Originally conceived with support for remote hosted APIs, Chidi.md made a pivotal architectural shift documented in Decision D-007 (superseding D-002). Storing cloud API keys in browser storage poses inherent security risks: any Cross-Site Scripting (XSS) vulnerability or rogue extension could exfiltrate credentials, while transmitting sensitive personal notes across third-party endpoints compromises user privacy.

Under the Zero-Key Mandate, all API key fields, cloud endpoints, and credential prompts have been eradicated from the system. AI capabilities connect exclusively to local model servers running on the user's personal machine (such as http://localhost:11434 for Ollama or http://localhost:8080/v1 for llama.cpp, LM Studio, or vLLM). The browser enforces a strict localhost host check before dispatching requests, ensuring that your thoughts and notes never leave your hardware.

3. Runtimes & Browser Environment Capabilities

Chidi.md is engineered for modern desktop browsers with particular optimizations across platforms:

3.1 Desktop Chromium (Chrome, Brave, Edge)

What It Does

Delivers the full-featured Chidi.md operating experience, including individual file selection, multi-file selection, and recursive folder scanning.

How It Works

Leverages the modern W3C File System Access API (window.showDirectoryPicker()). When the user clicks "Scan Folder", the browser prompts for folder authorization and recursively traverses directory handles to stream every .md file into application memory.

Why It Works

Allows instant ingestion of large personal wikis, Obsidian vaults, and project documentation trees without manual file-by-file picking or compression archives.

3.2 Firefox & Safari (Standard Web Standards)

What It Does

Provides complete document viewing, markdown parsing, local AI interaction, session saving, and multi-file loading.

How It Works

File loading operates via the standardized HTML5 <input type="file" multiple accept=".md"> element. When showDirectoryPicker is absent, attempting to "Scan Folder" gracefully alerts the user with platform-specific instructions while preserving full multi-file selection via "ADD FILES".

Why It Works

Maintains cross-browser accessibility and deterministic behavior without forcing non-standard polyfills or breaking the no-build philosophy.


Book II: Core Architecture & Engine Room

4. The No-Build Single-Page Architecture

Adhering strictly to Decision D-001, Chidi.md avoids bundlers, transpilers, npm dependencies, and minification steps. The application source code is delivered directly to the browser as written:

5. Data Flow & Architecture Pipeline

The data flow through Chidi.md follows a unidirectional, strictly filtered pipeline:

+-----------------------------------------------------------------------------------------+
|                                    USER INTERACTION                                     |
|     [ADD FILES] (File Input)  |  [Scan Folder] (DirectoryPicker)  |  [Ask Form] / [AI]  |
+-----------------------------------------------------------------------------------------+
                                             |
                                             v
+-----------------------------------------------------------------------------------------+
|                                 STATE MANAGER (main.js)                                 |
|                                                                                         |
|   +-----------------------+     +------------------------+     +--------------------+   |
|   | state.loadedFiles     |     | state.history          |     | state.chatHistory  |   |
|   | [{name, content},...] |     | [0, 4, 1, 3...]        |     | [{role, content}]  |   |
|   +-----------------------+     +------------------------+     +--------------------+   |
|               |                             |                             |             |
|               v                             v                             v             |
|   +-----------------------+     +------------------------+     +--------------------+   |
|   | state.modelSettings   |     | state.viewId (Counter) |     | state.isAsking     |   |
|   | {apiStyle, baseUrl}   |     | (Race condition guard) |     | (Request lock)     |   |
|   +-----------------------+     +------------------------+     +--------------------+   |
+-----------------------------------------------------------------------------------------+
        |                                                              |
        | (Document Markdown / Model Text)                             | (JSON Serialization)
        v                                                              v
+-------------------------------------------------------+    +----------------------------+
|             SAFE RENDERING PIPELINE                   |    |       PERSISTENCE          |
|                                                       |    |                            |
|   [Raw String]                                        |    | localStorage               |
|        |                                              |    | ["chidiMdSession"]         |
|        v                                              |    +----------------------------+
|   [marked.parse(content)]  (Markdown AST -> HTML)     |
|        |                                              |
|        v                                              |
|   [DOMPurify.sanitize(html)] (Strips scripts/vectors) |
|        |                                              |
|        v                                              |
|   [#markdownDisplay.innerHTML] (Verified Safe DOM)    |
+-------------------------------------------------------+
        ^
        | (Sanitized LLM Response)
+-------------------------------------------------------+
|             LOCAL MODEL TRANSPORT ENGINE              |
|                                                       |
|   isLocalUrl() Check -> http://localhost:11434        |
|   Ollama: POST /api/chat                              |
|   OpenAI-Compatible: POST /v1/chat/completions        |
+-------------------------------------------------------+

6. State Machine & Element Registry

Source: main.js:2-50

What It Does

Caches all 37 distinct DOM element references into a single immutable elements dictionary and manages application lifecycle through an explicit, reactive state store.

How It Works
  1. On DOMContentLoaded, elements queries every ID defined in index.html (verified at build time by tests/check_structure.py).
  2. state holds loadedFiles, navigation pointers (history, historyIndex), active markdown content, conversation turns (chatHistory), active model configuration (modelSettings), query status (isAsking), and view tracking (viewId).
  3. updateUI() inspects state conditions to dynamically enable or disable control buttons (disabling navigation when history bounds are reached, disabling AI features when no files are loaded, locking forms during generation).
Why It Works

Eliminates fragmented DOM lookups across functions. Centralizing state guarantees that UI rendering is a deterministic projection of current memory.

7. Markdown & Sanitization Engine

Source: main.js:254-261, index.html:7-8 (Decisions D-001, D-006, P1-01)

What It Does

Transforms arbitrary Markdown syntax into clean HTML and aggressively neutralizes Cross-Site Scripting (XSS) vectors, malicious iframe injections, and JavaScript URLs before injection into the DOM.

How It Works
  1. Input text is passed to convertMarkdownToHtml(markdownText).
  2. marked.parse(markdownText) executes client-side AST tokenization and HTML generation via pinned marked@18.0.14.
  3. The generated HTML string passes immediately through DOMPurify.sanitize() via pinned DOMPurify@3.4.16, stripping dangerous tags (<script>, <iframe>, <object>) and hostile attributes (onload, onerror, javascript:).
  4. The sanitized markup is assigned to markdownDisplay.innerHTML. tests/check_structure.py enforces that no other dynamic innerHTML assignment exists in main.js.
Why It Works

Guarantees zero script execution from untrusted Markdown documents or manipulated model replies. Pinned Subresource Integrity (SRI) hashes ensure CDN scripts cannot be compromised in transit.

8. Local LLM Protocol & Provider Adapters

Source: main.js:96-180, main.js:462-550 (Decision D-007, P1-06)

What It Does

Bridges Chidi.md to any local Large Language Model server, supporting both Ollama's native REST endpoints and OpenAI-compatible completions APIs with automatic payload normalization.

How It Works
  1. URL Verification: isLocalUrl(url) validates that the configured URL targets a loopback interface (localhost, 127.0.0.1, [::1], or *.localhost). Non-local addresses are rejected immediately.
  2. Ollama Adapter: Formats requests to {base}/api/chat with {model, messages, stream: false} and extracts response.message.content. Models are queried via GET {base}/api/tags and capability-checked via POST {base}/api/show.
  3. OpenAI-Compatible Adapter: Formats requests to {base}/chat/completions with {model, messages} and extracts choices[0].message.content. Compatible with LM Studio, llama.cpp server, LocalAI, and vLLM.
Why It Works

Decouples the user's interface from specific model vendors while ensuring that all inference traffic is air-gapped on the local machine.

9. View Invariance & Stale Response Guarding

Source: main.js:470-495 (Roadmap P2-05)

What It Does

Prevents race conditions and late model responses from bleeding into newly opened documents when users navigate or restart while an AI query is in flight.

How It Works
  1. Each time a file is displayed or a session restarts, state.viewId increments:
    const displayFile = (index) => {
        state.viewId++;
        ...
    };
  2. When an AI request starts, startRequest() captures the current capturedViewId = state.viewId along with a unique request ID.
  3. Upon response resolution, isStale(reqId, capturedViewId) checks if state.viewId !== capturedViewId.
  4. If the view has drifted, the response text is dropped, the activity spinner hides, and the status bar alerts: "Late reply from <model> dropped (file changed)."
Why It Works

Local LLMs can take seconds or minutes to generate responses. View invariance ensures that answers never contaminate the wrong document, preserving conversation integrity.

10. Session Serialization & Storage HAL

Source: main.js:264-325 (Decisions D-003, D-007, P2-02)

What It Does

Persists the entire active workspace (loaded files, view history stack, chat conversation turns, and model settings) to browser localStorage under chidiMdSession, complete with quota protection.

How It Works
  1. Serialization: saveSession() compiles {loadedFiles, history, historyIndex, chatHistory, modelSettings} into a JSON string and commits it to localStorage.setItem(SESSION_STORAGE_KEY, ...).
  2. Quota Circuit Breaker: If the write throws a storage quota error (typically ~5 MB in modern browsers), the catch block measures payload size and reports: "Error: Session is ~X MB; browser storage is full. Your previous save is still kept."
  3. Hydration & Quarantine: restoreSession() parses saved data, restores state pointers, and automatically purges any legacy apiKey or Gemini data from earlier revisions.
Why It Works

Users can refresh their browser, close tabs, or return days later without losing their place or loaded files, with graceful degradation when quotas are exceeded.


Book III: Document Intelligence Workflows

11. Document-Grounded Prompt Engineering

Chidi.md enforces strict document grounding. The model is never queried as an ungrounded general chatbot; it is primed with explicit instructions to act as an objective, faithful document analyst. The text of the active file is injected as context, followed by the user's specific directive.

12. The Summarize Pipeline

Source: main.js:555-570

What It Does

Generates a concise, structured executive summary of the currently viewed Markdown document, highlighting core arguments, conclusions, and key takeaways.

How It Works

Constructs a single-turn prompt containing the document content and the directive: "Provide a concise summary of the following document. Highlight key points and main ideas." The reply is parsed through DOMPurify and appended to the display with an AI heading badge.

Why It Works

Enables instant scanning of lengthy technical documents or research notes without reading several thousand words manually.

13. The Suggestion Generator

Source: main.js:573-610

What It Does

Analyzes the current document and synthesizes 3 to 5 insightful, probing questions that a reader might ask, rendering them as interactive, clickable buttons in the console.

How It Works
  1. Directs the model: "Based on the following document, suggest 3-5 insightful questions a reader might ask to better understand or explore the content."
  2. Parses numbered list items from the response text into distinct question strings.
  3. Renders each question as an interactive button (.suggested-question-btn). Clicking any button populates the query and automatically triggers the follow-up pipeline.
Why It Works

Sparks intellectual curiosity and helps readers discover blind spots or implicit assumptions in complex documentation.

14. Threaded Follow-Up Dialogue (Ask Form)

Source: main.js:510-550, index.html:27-31 (Roadmap P2-01)

What It Does

Provides a persistent "Ask about this file..." input bar below the document, maintaining a multi-turn conversation thread scoped exclusively to the active file.

How It Works
  1. Submitting a question pushes {role: "user", content: question} to state.chatHistory.
  2. The conversation history is dispatched to the local model server. During inference, the input box, Ask button, and all AI controls are disabled.
  3. The model's reply is added to state.chatHistory as {role: "assistant", content: reply} and appended to the rendered output. Focus returns to the input box.
Why It Works

Provides a true conversational copilot experience where users can ask progressive questions, clarify specific clauses, and drill into document specifics.

15. Ask All: Cross-Document Corpus Synthesis

Source: main.js:612-640, index.html:46

What It Does

Executes a single, comprehensive inquiry across every Markdown file loaded in the workspace, synthesizing facts from dozens or hundreds of files into one coherent answer.

How It Works
  1. Clicking "Ask All" opens the asynchronous Input Modal prompting for the question.
  2. Concatenates all loaded files into a single context block:
    File: notes/arch.md
    ---
    [Content]
    ===
    File: notes/decisions.md
    ---
    [Content]
  3. Directs the model to answer the query specifically citing evidence across the provided files. The synthesized result is appended to the active screen.
Why It Works

Eliminates the friction of manually searching across fragmented notes. Users can ask "Where did we decide our caching strategy?" and receive a cited synthesis instantly.

16. Dynamic Capability Probing

Source: main.js:115-180 (Roadmap P2-04)

What It Does

Queries the local model server to list available models, dynamically detects their capabilities, and disables embedding-only models that cannot participate in conversational chat.

How It Works
  1. On Ollama, queries GET /api/tags for model names, then calls POST /api/show for each model to inspect capabilities. If capabilities lacks completion, chat is flagged false.
  2. On OpenAI-compatible servers, models with embed in their ID are flagged false.
  3. fillModelSelect() sorts capable models first and appends disabled options for embedding models, preventing accidental selection.
Why It Works

Prevents confusing runtime errors caused by querying embedding models (such as nomic-embed-text) for conversational text generation.


Book IV: Console Ergonomics & User Interface

17. Retro Console Aesthetic & CSS Design

Source: style.css, index.html:16-63

What It Does

Styles Chidi.md as a vintage CRT terminal workstation with custom monospaced typography, amber/cyan accent highlights, and subtle terminal borders.

How It Works

Loads Google Fonts (Space Mono for legible body reading, VT323 for CRT console titles). Uses a dark palette (#0d1117 background, #38bdf8 cyan borders) and fluid CSS grid/flexbox layouts that adapt seamlessly to various desktop viewport sizes.

Why It Works

Fosters a focused, distraction-free reading mindset reminiscent of early Unix workstations without sacrificing modern layout reliability.

18. Buffer Traversal & History Stack

Source: main.js:41-45, main.js:430-460

What It Does

Maintains an explicit navigation history stack of loaded files, driving the PREV and NEXT buttons with boundary detection and duplicate skipping.

How It Works
  1. state.history stores an ordered array of file indexes representing viewed documents. state.historyIndex tracks the current cursor position.
  2. Clicking PREV decrements historyIndex; clicking NEXT increments it.
  3. updateUI() disables PREV when at index 0 and disables NEXT when at the end of the history stack.
Why It Works

Provides predictable, browser-like forward/backward navigation across documents without losing state or reloading content.

20. Status Readouts & Operational Feedback

Source: main.js:18-20, style.css:120-145

What It Does

Provides real-time feedback via the footer status readout (loaded file count, system logs, error alerts) and a header activity loader spinner.

How It Works
  1. fileCountDisplay.textContent reflects state.loadedFiles.length.
  2. messageBox.textContent renders standardized log prefixes (SYSTEM LOG: Standby., SYSTEM LOG: Error: ...).
  3. loader.classList.remove('hidden') activates an animated CSS spinner in the header during asynchronous I/O or model inference.
Why It Works

Operators always have immediate visibility into application state, knowing precisely when operations are queued, active, or errored.


Book V: Security & Trust Boundaries

21. Threat Model & Untrusted Inputs

What It Does

Defines the trust boundary between user files, external model outputs, and DOM execution contexts.

How It Works

Both user-provided Markdown files and model responses are treated as hostile, untrusted data. Even if a Markdown file contains embedded <script>alert(1)</script>, <img src=x onerror=...>, or [click](javascript:...), it is filtered through DOMPurify before reaching the DOM.

Why It Works

Users frequently download Markdown files or repositories from the public internet. Defense-in-depth sanitization guarantees that opening a hostile Markdown file can never compromise the user's browser.

22. Strict Localhost Perimeter

Source: main.js:96-105 (Decision D-007)

What It Does

Enforces that outbound network requests initiated by Chidi.md can only target local loopback network adapters on the user's machine.

How It Works

isLocalUrl(value) parses the input via the browser's URL API and confirms:

  • The protocol is http: or https:.
  • The hostname is strictly localhost, 127.0.0.1, [::1], or ends with .localhost.
Any remote domain (e.g. api.openai.com or evil-server.com) is rejected immediately with an error.

Why It Works

Eliminates SSRF vectors and ensures that sensitive document contents are never accidentally transmitted across the public internet.

23. Double-Pass Sanitization Boundary

Source: main.js:254-261, main.js:500-505 (Roadmap P1-01, P1-02)

What It Does

Ensures that structural headings and content elements are assembled with DOM-safe APIs rather than raw template strings.

How It Works

When appending model output, appendAiOutput() builds container elements, sets headings via heading.textContent = titleText, and processes body markdown through convertMarkdownToHtml(). Template strings are never directly assigned to innerHTML.

Why It Works

Prevents title or prompt injection attacks from escaping HTML boundaries, maintaining structural integrity across all dynamic content.

24. Storage Quarantine & Secret Scrubbing

Source: main.js:315-325 (Decision D-007)

What It Does

Scans incoming session payloads for legacy credentials (e.g. older versions that stored Gemini API keys) and permanently purges them upon hydration.

How It Works

When restoring from localStorage, restoreSession() checks for parsedData.apiKey or parsedData.geminiChatHistory. If detected, it immediately deletes those keys and saves the sanitized session back to disk.

Why It Works

Eliminates orphaned API secrets from user storage, ensuring adherence to the Zero-Key Mandate.


Book VI: Quality Assurance & Testing Engineering

The Testing Philosophy: "Verified Means Observed"

In accordance with AGENTS.md and docs/TESTING.md, claims in documentation or code reviews are invalid until observed in execution. Chidi.md maintains an automated test harness covering DOM element wiring, documentation integrity, and dual-browser end-to-end smoke tests.

26. Structural Wiring Linting (tests/check_structure.py)

What It Does

A fast, zero-dependency Python script that validates the structural agreement between index.html and main.js in under 100 milliseconds.

How It Works
  1. Verifies that every ID queried via document.getElementById() in main.js exists exactly once in index.html.
  2. Verifies that no duplicate IDs exist in index.html.
  3. Verifies that all local script and stylesheet paths exist on disk.
  4. Enforces that every innerHTML assignment in main.js is either a string literal or passes through convertMarkdownToHtml().
  5. Verifies that all CDN scripts include exact version numbers and valid SRI sha384 hashes.
Why It Works

Catches wiring bugs, typos, and security regressions before code ever reaches a browser or version control.

27. Documentation Truth Auditor (tools/check_docs.py)

What It Does

An automated auditor that checks documentation files for link drift, stale roadmap items, invalid decision references, and unfulfilled template placeholders.

How It Works
  1. Scans all project documentation in docs/ and the repository root.
  2. Validates that every Markdown link targets an existing file on disk.
  3. Verifies that referenced Roadmap IDs (e.g. P1-01) and Decision IDs (e.g. D-007) exist in their canonical files.
  4. Audits docs/HANDOFF.md session logs for proper formatting and limits.
Why It Works

Guarantees that documentation remains an accurate, living reflection of codebase reality rather than decaying over time.

28. Dual-Browser Playwright Smoke Suite (tests/e2e)

What It Does

An offline end-to-end test suite running 34 automated browser tests across both Chromium and Firefox, simulating live user workflows and verifying network boundaries.

How It Works
  1. Uses Playwright to spin up headless browser instances against an offline mock server. Pinned CDN libraries are served directly from node_modules.
  2. Tests file loading, duplicate skipping, and history navigation.
  3. Tests hostile Markdown sanitization (verifying that onerror payloads are stripped).
  4. Tests local URL validation (verifying that non-local URLs are refused).
  5. Tests live Ollama and OpenAI-compatible API mocking, model capability filtering, and late answer quarantine (P2-05).
  6. Tests storage quota exhaustion handling (P2-02).
Why It Works

Proves that the entire application operates seamlessly across major desktop rendering engines without relying on external network connectivity during CI runs.

29. Continuous Integration Automation (.github/workflows/checks.yml)

What It Does

Runs the fast structural checks and dual-browser Playwright smoke test on every push and pull request.

How It Works

Executes on GitHub Actions Ubuntu runners with pinned Node and Python environments. Actions are pinned to immutable commit SHAs for supply-chain security.

Why It Works

Enforces that no breaking change or security regression can be merged into main without passing automated verification.


Book VII: Interface Codex & Master Feature Matrix

📖 Companion Manual: The Interface Codex

Every button, input field, modal workflow, and utility script in Chidi.md is documented in exhaustive detail in our companion manual, featuring full What It Does, How It Works, and Why It Works breakdowns:

Open The Complete Interface Codex (All 33 Controls) →

30. Master Feature & Control Directory

Quick reference catalog of all user interface controls and system functions across 9 functional categories:

Category Controls & Interfaces
📂 File Ingestion & Loading (4) ADD FILES, Scan Folder, FILES Count, File Drop
🧭 Navigation & History Stack (4) PREV Button, NEXT Button, Random Picker, Markdown Viewport
🧠 Document Intelligence (6) Summarize, Suggest, Ask Form, Ask Input, Ask Button, Question Buttons
📚 Corpus Synthesis (2) Ask All, Input Modal
⚙️ Model Configuration (7) Model Button, Server Type, Server URL, Model Selector, Refresh List, Save Model, Cancel Model
💾 Session Persistence (5) Save Session, Restart Session, Restore Confirm, Restore Deny, Quota Circuit Breaker
🛡️ Security Boundaries (4) DOMPurify Sanitizer, Local URL Enforcer, Stale Response Guard, Legacy Key Scrubber
🖥️ Operational Feedback (3) SYSTEM LOG, Activity Loader, Console Header
🛠️ Tooling & Test Automation (4) serve.py, check_docs.py, check_structure.py, Playwright Smoke Suite

Technical Appendices & Reference

Appendix A: Dependencies & SRI Hashes

All third-party CDN scripts loaded in index.html are pinned to exact versions with Subresource Integrity (SRI) sha384 hashes:

Library Version CDN Provider SRI sha384 Integrity Hash
marked 18.0.14 jsDelivr sha384-2vpGtuKqJvFlwJqYnf/wUMuzUfhUnYBt9oay0e2yaFcq0Dh6/aEbQ8YAOeKGzlYo
DOMPurify 3.4.16 jsDelivr sha384-a7SzOxErzJ3ZpQz0zJ32d67dSitNzPcbfybc/ykU9KJhMgZkwqfSxlhhdJRS+XGL
Google Fonts Space Mono & VT323 Google Fonts CDN Preconnected font stylesheet (styling only)

Appendix B: Session Schema & Invariants

The chidiMdSession item in localStorage conforms to the following strict JSON specification:

{
  "loadedFiles": [
    {
      "name": "filename.md",
      "content": "# Document Title\n\nFull text..."
    }
  ],
  "history": [0, 2, 1],
  "historyIndex": 2,
  "chatHistory": [
    {
      "role": "user",
      "content": "Full text of document..."
    },
    {
      "role": "assistant",
      "content": "Summary or answers..."
    }
  ],
  "modelSettings": {
    "apiStyle": "ollama",
    "baseUrl": "http://localhost:11434",
    "model": "llama3.1:8b"
  }
}

Appendix C: Local Model Server Setup Guide

Chidi.md requires a local model server. Follow these configuration guidelines:

Ollama Setup & CORS Configuration

Install Ollama from ollama.com and pull a model (e.g. ollama pull llama3.1:8b). Because Chidi.md runs in the browser, Ollama must allow browser Cross-Origin Resource Sharing (CORS):

OpenAI-Compatible Servers (LM Studio, llama.cpp)

For LM Studio, start the Local Server on port 1234 and ensure CORS is enabled. In Chidi.md, choose OpenAI-compatible and set the URL to http://localhost:1234/v1.

Appendix D: Troubleshooting & Failure Modes

Observed Symptom Underlying Cause Resolution Procedure
Error: Failed to fetch The local model server is offline, listening on a different port, or refusing CORS origins. Verify the server is running. For Ollama, confirm OLLAMA_ORIGINS="*" is set.
Error: Session is ~X MB; browser storage is full. The total size of loaded files and chat history exceeds the browser's ~5 MB localStorage quota (P2-02). Click Restart to begin a fresh session with fewer files, or load documents selectively.
Late reply from <model> dropped The user switched documents or restarted while a generation was in flight (P2-05). Expected safety behavior. The system quarantined the late reply to prevent cross-document contamination.
Scan Folder button displays alert The browser does not support the File System Access API (e.g. Firefox or Safari). Use the "ADD FILES" picker instead, or run Chidi.md in a desktop Chromium browser.