A citation-first RAG system for support teams that need an answer they can verify before continuing the call.
Truenote turns SOPs, policy documents, screenshots, and tables into a controlled knowledge base. Customer service representatives ask a question and receive a grounded answer with clickable source excerpts. If the evidence is weak or the answer fails citation checks, Truenote refuses instead of guessing.
A plausible answer is only the beginning. Truenote is built around the next question a representative has to answer: Can I trust this enough to say it to a customer?
| Need | What Truenote does |
|---|---|
| Find exact policy terms and natural-language matches | Runs vector and PostgreSQL full-text search in parallel, then uses trigram search for keyword zero-hits and Cohere to rerank the merged candidates. |
| Keep unsupported answers off the screen | Refuses before generation when retrieval confidence is too low. Generated answers must cite known retrieved chunks or they are rejected. |
| Prevent cross-program retrieval | Filters every retrieval query by server-resolved program_id and classification, then applies a final fail-closed scope check. |
| Preserve context across chunk boundaries | Adds adjacent chunks for the strongest reranked anchors without letting those unscored neighbors affect the confidence gate. |
| Catch bad source material before it goes live | Holds uploads through file checks, malware scanning, content DLP, parsing, preview, and role-authorized activation or review. |
| Measure quality instead of judging a demo | Attributes failures to retrieval, reranking, thresholding, or generation, with optional claim-level faithfulness judging. |
flowchart LR
Q["Question"] --> E["Embed"]
E --> V["Vector search"]
Q --> K["Full-text search"]
K -->|"zero hits"| T["Trigram fallback"]
V --> M["Merge and deduplicate"]
K --> M
T --> M
M --> R["Cohere rerank"]
R --> G{"Confidence gate"}
G -->|"below threshold"| X["Refuse without an LLM call"]
G -->|"passes"| N["Add adjacent context"]
N --> L["ZDR-pinned generation route"]
L --> C{"Citation and content validation"}
C -->|"valid"| A["Answer with clickable sources"]
C -->|"invalid or all routes fail"| X
Vector, full-text, trigram, neighbor, citation, and history reads are scoped in SQL to the authenticated user's program and clearance. Conversation history may help rewrite a follow-up into a standalone question, but it is not factual evidence for the answer.
Security is part of Truenote's data path, not a separate checklist. The repository includes controls for authorization, controlled ingestion, model input and output, audit delivery, browser requests, dependency review, and recovery-oriented retention.
| Area | What is implemented | Evidence and operating path |
|---|---|---|
| Program and classification isolation | SQL filters use server-resolved scope and a final fail-closed program check. | Negative tests cover cross-program access, malformed principals, and classification boundaries. |
| Grounded generation | Retrieval gates, untrusted-excerpt instructions, citation validation, sensitive-output blocking, and defensive refusal protect every answer. | The eval harness measures retrieval, refusal, citation, and claim-level faithfulness. |
| Controlled ingestion | File signatures, EICAR checks, default-on scanner enforcement, content DLP, inactive versions, role-authorized activation or review, revocation, and retention gates protect source activation. | Lifecycle records preserve provenance, scan results, audited temporary scanner overrides, reviewer decisions, and document history. |
| Authentication and browser defense | Argon2id local passwords, hashed session tokens, OIDC Authorization Code with PKCE, MFA or ACR validation, CSP, Origin checks, and Fetch Metadata checks protect access. | Automated tests exercise identity validation, foreign origins, security headers, and privileged routes. |
| Audit and SIEM | Hash-chained security events, a transactional outbox, signed delivery, bounded retries, lease fencing, dead-letter state, and health reporting preserve security receipts. | Control DDL includes acceptance queries; tests cover queueing, delivery failure, retry, and recovery behavior. |
| Supply chain | Pull requests, main, weekly, and manual runs execute PCI/vulnerability evidence checks, type checks, the production build, unit tests, high-severity dependency audit, CycloneDX SBOM generation, Gitleaks, and CodeQL security-extended analysis. |
A safe baseline accounts for all 51 retained CodeQL results; the strict managed-release gate honestly remains blocked by missing owners, dates, and dispositions. Alert upload, current hosted execution, and release-linked results still require live verification. |
Read the security documentation index, the PCI DSS readiness evidence pack, and the technical security capabilities brief. Report vulnerabilities through SECURITY.md.
- Answers render as Markdown with inline citation chips. A chip opens the exact source excerpt and document version.
- Follow-up questions are rewritten into standalone retrieval queries. Prior answers are not reused as evidence.
- A clear "Not in knowledge base" state can be flagged for content owners.
- Keyboard shortcuts keep the ask, read, cite, and return loop fast during a call.
- Feedback and refusals feed a program-scoped content-gap queue.
- Upload PDF, DOCX, PNG, JPG, WebP, Markdown, or text sources.
- Review provenance, scan findings, classification, parsed content, and chunk boundaries before activation.
- Keep immutable document versions. Re-uploading creates a new inactive version instead of overwriting history.
- Senior managers and super users may activate their own uploads after automated controls pass. Manager and CSR uploads remain inactive until an authorized senior manager or super user reviews them.
- Inspect retrieval confidence, rerank scores, provider attempts, latency, evaluation history, and redacted application errors.
Each document version moves through a controlled, asynchronous pipeline:
- Store the original bytes and SHA-256 digest.
- Validate the file signature and check for EICAR.
- Send raw bytes to the configured malware scanner. Enforcement defaults on; missing or failed scanning quarantines the upload. A super user can temporarily disable the external scanner through an audited Security control while file validation, content checks, and authorized approval remain active.
- Parse PDFs and images with LandingAI ADE, DOCX with Mammoth, and text formats directly.
- Scan parsed content for sensitive information and prompt-injection markers. Blocking findings quarantine the upload before embedding.
- Split content near 500 tokens without breaking tables or lists. Add a document and heading path to each chunk.
- Embed and index clean chunks.
- Keep the version inactive until an authorized senior manager or super user approves it.
The eval harness runs the retrieval and generation pipeline against curated in-knowledge-base and out-of-knowledge-base questions. It reports answer accuracy, citation accuracy, false refusals, correct refusals, stage-level failure attribution, and latency. The optional judge breaks answers into claims and checks each claim against the excerpts the model received.
pnpm --filter @workspace/scripts run eval
pnpm --filter @workspace/scripts run eval -- --limit 5
pnpm --filter @workspace/scripts run eval -- --judge
pnpm --filter @workspace/scripts run eval -- --threshold 0.25The eval commands require a configured database and provider keys. A model or reranker change is not complete until its score distribution and refusal threshold have been evaluated together.
| Layer | Choice |
|---|---|
| Frontend | React 18, Vite, Tailwind CSS, Wouter |
| API and workers | Express, TypeScript, pg-boss |
| Database | PostgreSQL with pgvector, pg_trgm, and Drizzle bindings |
| Parsing | LandingAI ADE Parse v2, Mammoth for DOCX |
| Embeddings | OpenAI text-embedding-3-small |
| Retrieval | Vector search, PostgreSQL full-text search, trigram fallback, Cohere reranking |
| Generation | Administrator-ordered, server-allowlisted OpenRouter routes with one pinned ZDR provider per request |
| Hosting | Replit with Neon-backed PostgreSQL and object storage |
Answer generation does not fall back to a direct provider outside the enforced OpenRouter route policy. Direct OpenAI embeddings and the optional eval judge are separate data flows and need their own organization-level retention controls. LandingAI ZDR is also an account setting, not a request flag.
artifacts/
rag-app/ React application for representative and admin workflows
api-server/ Express API, auth, retrieval, ingestion, and security controls
lib/
db/ Shared Drizzle schema bindings and database client
scripts/ Evaluation, seeding, re-ingestion, and maintenance workers
docs/security/ Control DDL and the technical security capabilities brief
PRODUCT.md Product users, principles, and boundaries
DESIGN.md Interface tokens, components, motion, and accessibility rules
- Node.js 22 or newer
- pnpm 10 through Corepack
- PostgreSQL with the
vector,pg_trgm, andpgcryptoextensions - Provider credentials listed in
.env.example
The current database model and invariants are documented in .claude/reference/data-model.md. Reviewed security DDL lives under docs/security/. The project manages schema changes with explicit SQL rather than drizzle-kit.
Install locked dependencies and create a local environment file:
corepack enable
pnpm install --frozen-lockfile
cp .env.example .envOn PowerShell, use Copy-Item .env.example .env. For local development, set PORT=5173 and API_PORT=5000; the checked-in example uses the Replit port arrangement.
Run the API and frontend in separate terminals:
pnpm dev:apipnpm dev:webRun pnpm worker in a third terminal for ingestion and background evaluation jobs.
pnpm check
pnpm testChanges to ingestion, retrieval, reranking, generation, or citation behavior also require the eval suite against a representative fixture or deployed test corpus. Runtime integrations such as OIDC, malware scanning, SIEM delivery, storage, and provider retention settings need tests in the environment where they are configured.
Read CONTRIBUTING.md before opening a pull request. The short version: keep every representative-facing answer cited or refused, preserve server-side scope enforcement, add negative tests for boundary changes, and run the checks that match the area you changed.