Veritas is the autonomous AI-powered news agency for Ghana and West Africa. This
document is the comprehensive technical specification of what is implemented
in apps/veritas/* and libs/veritas/*. Every entity, enum, endpoint, and
event below is traceable to source code; the grounding sources are listed at the
end of each major section.
This document is organized as follows: §1–§2 enumerate the applications and
libraries; §3–§4 cover the technology stack and infrastructure; §5 is the
complete domain object model (the @veritas/core type system); §6–§9 document
the running services (REST API, NLP, ingestion, AI workers); §10 covers the
Prisma persistence schema; §11 covers domain events; §12–§13 document the agents
system and CMS; §14 covers content authentication; §15–§16 list external
integrations and configuration; §17 states acceptance criteria.
A new engineer reading this document for the first time should start with §1 (which apps exist), then §5 (what the core data model looks like), then §6 (what the API exposes), and then read §10 (how it is persisted) alongside whichever service section is most relevant to their work.
1. Application Inventory#
Veritas ships 13 Nx applications under apps/veritas/.
| Application | Package | Type | Description |
|---|---|---|---|
| API server | @veritas/api |
Hono HTTP service | REST API, API-key + JWT auth, OpenAPI/Swagger UI |
| NLP service | veritas-nlp |
Hono HTTP service | NLP analysis endpoints (sentiment, topics, claims, …) |
| Ingestion | veritas-ingestion |
CLI worker | RSS/sitemap/social collection pipeline (four modes) |
| AI workers | veritas-ai-workers |
BullMQ workers | AI content generation, processing, analysis, editorial |
| Agents | veritas-agents |
Hono HTTP service | Multi-agent orchestration service |
| CMS | veritas-cms |
Hono HTTP service | Editorial content-management workflow engine |
| Video | veritas-video |
Worker + HTTP | HeyGen video production pipeline |
| Audio | veritas-audio |
Worker | TTS and podcast production |
| Social | veritas-social |
Worker + HTTP | Multi-platform social automation |
| Analytics | veritas-analytics |
Service | Content, user, and revenue analytics |
| Notifications | veritas-notifications |
Service | Email and FCM push delivery |
| Web | veritas-web |
Next.js PWA | Progressive web application |
| Mobile | veritas-mobile |
Expo / React Native | iOS and Android application |
The API server's package name is @veritas/api; its default HTTP port comes
from the PORT environment variable and falls back to 3002 in
apps/veritas/api/src/main.ts. The NLP service reads NLP_PORT (default
3002).
Grounding: apps/veritas/ directory listing; apps/veritas/api/package.json;
apps/veritas/api/src/main.ts; apps/veritas/nlp/src/config.ts.
2. Library Inventory#
Veritas ships 64 libraries under libs/veritas/. Libraries are organized into
six tiers by responsibility: Foundation (core types and infrastructure), Agents
(the AI newsroom), NLP/Fact-checking/Bias, Content and Knowledge, Media
Production, and Business/Monetization. The tables below list every library in
each tier with its npm package name and description.
Foundation Libraries#
| Package | Path | Description |
|---|---|---|
@veritas/core |
libs/veritas/core |
Core domain types, branded primitives, error types |
@veritas/models |
libs/veritas/models |
Zod validation schemas mirroring the core types |
@veritas/database |
libs/veritas/database |
Prisma client, repositories, health, metrics, seed |
@veritas/auth |
libs/veritas/auth |
Authentication, API keys, OAuth, middleware |
@veritas/cache |
libs/veritas/cache |
Redis caching abstraction |
@veritas/search |
libs/veritas/search |
Elasticsearch client, query builder, index manager |
@veritas/llm |
libs/veritas/llm |
LLM client router, cost optimizer, multi-provider |
@veritas/nlp-core |
libs/veritas/nlp-core |
NLP primitives and shared utilities |
@veritas/events |
libs/veritas/events |
Domain event bus (publisher/subscriber) |
Agent Libraries#
| Package | Description |
|---|---|
@veritas/agents-core |
Base agent framework, LLM client, message bus, state |
@veritas/agents-orchestrator |
Multi-agent workflow orchestration, registry, router |
@veritas/agents-editorial |
Editor-in-Chief, Managing Editor, Content Strategist |
@veritas/agents-journalism |
Investigative, political, breaking, business agents |
@veritas/agents-fact-checking |
Fact-checker agent with web evidence search |
@veritas/agents-social-media |
Social media manager agent |
@veritas/agents-devops |
DevOps and infrastructure management agent |
@veritas/agents-product |
Product management agent |
@veritas/agents-qa |
QA and testing automation agent |
NLP, Fact-Checking, and Bias Libraries#
| Package | Description |
|---|---|
@veritas/ghana-nlp |
Ghana NLP (Khaya) API client |
@veritas/bias-detection |
Political bias analysis and GPL scoring |
@veritas/fact-checking |
Claim verification and evidence scoring |
@veritas/claims |
Claim extraction and linking |
@veritas/content-classification |
Topic and category classification |
Content and Knowledge Libraries#
| Package | Description |
|---|---|
@veritas/rag |
RAG pipeline (indexing, retrieval, archive) |
@veritas/knowledge-graph |
Entity graph, profiles, relationships |
@veritas/story-clustering |
Semantic story grouping and timeline |
@veritas/recommendations |
Collaborative/content-based filtering |
@veritas/headline-service |
Headline generation and A/B testing |
@veritas/article-generation |
AI article drafting pipeline |
@veritas/content-auth |
Content authenticity, hashing, C2PA, provenance |
@veritas/cms |
CMS workflow engine |
@veritas/ingestion-core |
Core ingestion pipeline primitives |
@veritas/ai-next-gen |
Investigations, OSINT, real-time anchors |
@veritas/automated-content |
Weather, market, fuel-price, traffic generators |
@veritas/archive |
Content archival and retrieval |
@veritas/research-assistant |
Research tools for journalists |
Media Production Libraries#
| Package | Description |
|---|---|
@veritas/video-production |
HeyGen video composition and rendering pipeline |
@veritas/audio-production |
ElevenLabs/Ghana NLP TTS and mastering pipeline |
@veritas/live-stream |
YouTube Live, RTMP, graphics, interactive Q&A |
@veritas/social-automation |
Cross-platform social media posting |
Business and Monetization Libraries#
| Package | Description |
|---|---|
@veritas/analytics |
Content, user, revenue, social analytics |
@veritas/payments |
Stripe, Paystack, mobile money integration |
@veritas/billing |
Subscriptions, invoicing, pricing, usage |
@veritas/notifications |
Push (FCM), email, SMS notification delivery |
@veritas/b2b-sdk |
TypeScript SDK for B2B API consumers |
@veritas/b2b-sdk-python |
Python SDK for B2B consumers |
@veritas/api-client |
Internal/shared API client |
@veritas/newsletter |
Email newsletter campaigns |
@veritas/seo |
SEO optimization and sitemap generation |
@veritas/business |
Profitability and business optimization |
@veritas/expansion |
Pan-African geographic expansion |
@veritas/developer-support |
AI-powered developer support chatbot for B2B API |
@veritas/signup |
User registration flows |
Regional, Access, and Infrastructure Libraries#
| Package | Description |
|---|---|
@veritas/regional |
Region-specific content and configuration |
@veritas/ussd |
USSD channel for telco news delivery |
@veritas/platforms |
Platform SDKs for smartwatch, TV, enterprise |
@veritas/ab-testing |
A/B testing for headlines and content |
@veritas/community |
Community engagement features |
@veritas/comments |
Article commenting system |
@veritas/emergency |
Emergency broadcast and alert distribution |
@veritas/storage |
File storage abstraction (S3/MinIO/GCS) |
@veritas/realtime-data |
Real-time data feeds and streaming |
@veritas/risk |
Risk assessment and content safety |
@veritas/operations |
Security, compliance, QA, monitoring ops |
Grounding: libs/veritas/ directory listing (64 entries).
3. Technology Stack#
Languages and Runtimes#
| Technology | Purpose |
|---|---|
| TypeScript | All TypeScript packages (strict mode, ESM) |
| Node.js | >= 22 — server runtime for all backend services |
| Python | B2B Python SDK (@veritas/b2b-sdk-python) |
Frameworks and Key Dependencies#
| Dependency | Used by | Purpose |
|---|---|---|
hono + @hono/node-server |
api, nlp, agents, cms | HTTP server |
@hono/swagger-ui |
api | Swagger UI documentation page |
@hono/zod-openapi |
api | Zod-validated OpenAPI integration |
zod |
models, api, nlp | Runtime schema validation |
@prisma/client + prisma |
database | ORM and migrations |
pg |
api, ingestion | Raw PostgreSQL access |
ioredis |
api, agents, ingestion | Redis client |
@elastic/elasticsearch |
api, search | Elasticsearch client |
stripe |
api, payments | Card payments |
bcryptjs |
api | Password hashing |
Grounding: apps/veritas/api/package.json; libs/veritas/core/package.json;
libs/veritas/database/package.json.
4. Infrastructure Requirements#
Core Infrastructure#
| Service | Purpose |
|---|---|
| PostgreSQL | Primary database, with pgvector and citext |
| Redis | Caching, BullMQ job queues, sessions, agent pub/sub |
| Elasticsearch | Full-text article search |
| MinIO / AWS S3 | Media asset storage |
PostgreSQL Extensions#
The Prisma datasource enables two extensions via
previewFeatures = ["postgresqlExtensions"]:
| Extension | Mapped name | Purpose |
|---|---|---|
pgvector |
vector |
Vector embeddings for semantic deduplication / RAG |
citext |
citext |
Case-insensitive text |
Database Configuration#
| Property | Value |
|---|---|
| Connection variable | VERITAS_DATABASE_URL |
| ORM | Prisma (prisma-client-js generator) |
| Schema location | libs/veritas/database/prisma/schema.prisma |
| Generated SQL | libs/veritas/database/prisma/generated/schema.sql |
The Prisma schema defines roughly 75 models and 50 enums (see §10 Persistence).
Grounding: libs/veritas/database/prisma/schema.prisma lines 1–17; model/enum
declarations.
5. Domain Object Model (@veritas/core)#
@veritas/core is the canonical type layer — the single source of truth for
every domain entity, enum, and primitive in the Veritas system. All other
libraries import types from here rather than defining their own.
@veritas/models provides matching Zod schemas
(libs/veritas/models/src/schemas/) so that API boundaries can validate
incoming and outgoing data against these types at runtime.
The sections below document each type file in libs/veritas/core/src/. The
grounding note at the end of §5 lists every source file referenced.
5.1 Branded Primitives (common.ts)#
Branded primitives prevent accidental mixing of semantically distinct string values (e.g., passing a raw string where a validated URL is expected). Each primitive has a factory function and a type guard. The table below lists the four primitives and their validation rules.
| Type | Underlying | Validation regex / rule |
|---|---|---|
Uuid |
string |
isValidUuid — 8-4-4-4-12 hex UUID |
UrlString |
string |
isValidUrlString — ^https?://.+ |
CountryCode |
string |
isValidCountryCode — ^[A-Z]{2}$ (ISO 3166-1 alpha-2) |
Slug |
string |
isValidSlug — ^[a-z0-9]+(?:-[a-z0-9]+)*$ |
Factory functions: createUuid, createUrlString, createCountryCode,
createSlug. Shared interfaces: Timestamped (createdAt, updatedAt),
Cursor<T>, PaginatedResponse<T>, PaginationParams, WithMetadata.
Result<T, E> / AsyncResult<T, E> are discriminated success/failure unions.
Metadata is Record<string, unknown>.
5.2 Language (language.ts)#
The language system encodes Ghana's multilingual reality directly into the type
layer. The eight BCP 47 language tags drive content routing, TTS provider
selection, and search index configuration. CodeSwitchingResult models the
common Ghanaian pattern of mixing English with a local language mid-sentence.
LANGUAGE_TAGS (BCP 47) — 8 values:
| Tag | Name | Primary regions |
|---|---|---|
en |
English | Greater Accra, Ashanti, Western, Eastern, Central |
tw |
Twi (Akan) | Ashanti, Eastern, Greater Accra, Bono, Ahafo |
ee |
Ewe | Volta, Oti, Greater Accra |
gaa |
Ga | Greater Accra |
ha |
Hausa | Northern, Upper East, Upper West, North East, Savannah |
dag |
Dagbani | Northern, North East, Savannah |
fr |
French | Upper West, Upper East |
und |
Undetermined | — |
type Language is an alias of LanguageTag. TRANSLATION_QUALITIES =
machine | human | verified. Translation carries id, languageTag,
headline, summary, body, bodyHtml, quality, metadata.
CodeSwitchingResult reports primaryLanguage, secondaryLanguages,
confidence, hasCodeSwitching, and languageProportions. LocaleConfig and
DEFAULT_LOCALE_CONFIGS define per-language date/time/number formatting; all
eight default to country GH, ltr direction.
5.3 Article (article.ts)#
Article is the central entity in the domain model. It carries the full
lifecycle of a piece of journalism: from raw ingestion metadata (sourceUrl,
sourcePublishedAt) through editorial enrichment (claims, biasScore,
perspectives) to publication and archival. The table below lists every field;
fields marked null are optional or unset at ingestion time and populated
progressively by the pipeline.
ARTICLE_STATUSES —
draft | pending_review | published | scheduled | archived | retracted.
ARTICLE_CONTENT_TYPES —
news | analysis | opinion | feature | interview | investigation | breaking | live_update | explainer | fact_check.
Article (extends Timestamped) — fields:
| Field | Type | Notes |
|---|---|---|
id |
Uuid |
required |
slug |
Slug |
required |
status |
ArticleStatus |
required |
contentType |
ArticleContentType |
required |
headline |
string |
required |
summary |
string | null |
lede |
body |
string |
plain text |
bodyHtml |
string | null |
rendered HTML |
featuredImage |
MediaAsset | null |
hero image |
media |
MediaAsset[] |
all assets |
audioVersion |
MediaAsset | null |
audio rendering |
videoVersion |
MediaAsset | null |
video rendering |
categories |
Category[] |
|
tags |
Tag[] |
|
entities |
Entity[] |
named entities |
topics |
Topic[] |
|
source |
ArticleSource | null |
originating source ref |
sourceUrl |
UrlString | null |
|
sourcePublishedAt |
Date | null |
|
authors |
AuthorRef[] |
|
attributions |
ArticleAttribution[] |
aggregated-content credits |
isOriginal |
boolean |
|
isAIGenerated |
boolean |
|
humanReviewed |
boolean |
|
reviewedBy |
UserRef | null |
|
factCheckStatus |
FactCheckStatus | null |
|
claims |
Claim[] |
|
biasScore |
BiasScore | null |
|
perspectives |
Perspective[] |
alternative perspectives |
storyClusterId |
Uuid | null |
|
relatedArticleIds |
Uuid[] |
|
languageTag |
LanguageTag |
|
translations |
Translation[] |
|
publishedAt |
Date | null |
|
scheduledAt |
Date | null |
|
views |
number |
|
shares |
number |
|
readTimeSeconds |
number |
estimated read time |
seo |
ArticleSEO | null |
|
metadata |
Metadata |
Supporting types: ArticleSource (id, name, domain, url), ArticleSEO
(metaTitle, metaDescription, canonicalUrl, ogImage, twitterCard ∈
summary | summary_large_image, focusKeywords, schemaType ∈
NewsArticle | Article | ReportageNewsArticle). ArticleListItem and
ArticleDetail (extends list item) are read projections; ArticleFeedItem adds
recommendationReason
(trending | following | similar | breaking | personalized | editorial),
score, hasInteracted, isFollowed. ArticleEngagement carries
view/share/comment/save counts and a scrollDepth distribution
(25% / 50% / 75% / 100%). ArticleSearchParams defines query/filter fields
with sortBy ∈ relevance | recency | popularity | engagement.
Article versioning — ARTICLE_CHANGE_TYPES
(created | updated | published | unpublished | corrected | retracted | restored | metadata_updated | media_updated | translation_added | auto_saved).
ArticleVersion snapshots content per revision; ArticleVersionDiff records
changed fields and TextDiff operations (equal | insert | delete);
ArticleVersionComparison, ArticleVersionHistory, ArticleVersionSummary
round out the revision API.
Article media — ARTICLE_MEDIA_PLACEMENTS
(hero | inline | gallery | sidebar | pullquote | background | thumbnail).
ArticleMedia, ArticleMediaGallery (layout
grid | carousel | masonry | slideshow), and ArticleMediaSummary model
in-article media placement.
DTOs: ArticleCreateInput, ArticleUpdateInput.
5.4 Source (source.ts)#
A Source represents a monitored news outlet. SourceFeed represents one
polling endpoint within that outlet (an outlet may have multiple RSS feeds).
SourceCredibility is the multi-signal credibility assessment that the system
continuously updates as the source publishes articles that are later verified or
refuted. Note that the Prisma SourceKind enum (§10) is broader than the
SOURCE_KINDS in @veritas/core; both are documented below.
SOURCE_KINDS (core type) —
publisher | government | ngo | blog | social | academic | wire_service | other.
SOURCE_STATUSES — active | paused | retired | pending.
OWNERSHIP_TYPES —
private | state | public | political | religious | ngo | community | academic | unknown | other.
FEED_TYPES — rss | atom | sitemap | api | scrape.
Source (extends Timestamped) — id, name, kind, status,
canonicalDomain, homeUrl, countryCode, region, languages, notes,
ownership (SourceOwnership), bias (SourceBiasMetadata), metadata.
SourceFeed (extends Timestamped) — id, sourceId, feedType, url,
isActive, fetchIntervalSeconds, priority, lastFetchedAt,
lastHttpStatus, etag, lastModified, consecutiveFailures, backoffUntil,
metadata.
SourceBiasMetadata — politicalScore (−1…1), confidence (0…1),
methodology, lastAssessedAt.
SourceReliability — score (0–100), factualAccuracy, correctionRate,
transparency, factCheckedCount, lastAssessedAt.
SOURCE_CREDIBILITY_TIERS —
highly_credible | generally_credible | mixed_credibility | low_credibility | unreliable | unrated.
SourceCredibility is a multi-signal assessment: tier, overallScore,
factualAccuracy, editorialStandards, transparency, correctionPractices,
sourceAttribution, clickbaitFrequency, sensationalism, totalFactChecked,
verifiedTrueCount, verifiedFalseCount, misleadingCount,
correctionsIssued, retractionsIssued, externalRatings
(SourceExternalRating[]), notes, redFlags (SourceCredibilityRedFlag[]),
lastAssessedAt, methodologyVersion.
SourceBias (extended) adds articlesAnalyzed, topicBreakdown,
historicalTrend, and indicators whose type ∈
loaded_language | source_selection | story_selection | omission | placement | headline_framing | image_selection.
SourceCredibilityRedFlag.type ∈
known_misinformation | undisclosed_ownership | deceptive_practices | content_farm | propaganda | satire_misrepresented | ai_generated_mass | plagiarism_pattern | state_controlled | advertising_disguised.
Other types: SourceCoverage, SourceRegistryEntry,
SourcePerformanceMetrics. DTOs: SourceCreateInput, SourceUpdateInput,
SourceFeedCreateInput.
Note: the Prisma
SourceKindenum is broader than the core type — see §10.
5.5 Fact-Check (fact-check.ts)#
The fact-check type system models the complete verification lifecycle: claim
extraction from article text (Claim), evidence retrieval and scoring
(ClaimEvidence), verdict assignment (ClaimVerification), and the compiled
report (FactCheckReport). Verdicts carry both a status label and a confidence
score so downstream consumers can threshold on confidence as well as verdict.
FACT_CHECK_OVERALL_STATUSES —
verified | likely_true | disputed | misleading | mostly_false | false | unverifiable | unverified.
Each status has a display label in FACT_CHECK_STATUS_LABELS and a hex color in
FACT_CHECK_STATUS_COLORS.
CLAIM_TYPES —
factual | opinion | prediction | quote | statistical | historical | scientific.
Claim (extends Timestamped) — id, text, normalizedText, type,
articleId, speaker, context, checkworthiness (0–1),
extractionConfidence (0–1), metadata.
ClaimEvidence (extends Timestamped) — id, claimId, url,
sourceId, quote, title, summary, stance
(supports | refutes | neutral | inconclusive), relevance (0–1),
credibility (0–1), retrievedAt, metadata.
ClaimVerification (extends Timestamped) — id, claimId, status
(FactCheckOverallStatus), confidence, evidenceIds, summary, notes,
verifiedBy, metadata.
FactCheckStatus (article-level) — overall, a claims array of
{ claimId, text, status, confidence }, lastCheckedAt, checkedBy.
FactCheckReport (extends Timestamped) — id, articleId, claims,
verifications, evidence, overallVerdict, summary, methodology,
authorId, status (draft | review | published), publishedAt.
ClaimMatch reports similarity and matchType
(exact | paraphrase | related | contradicts). CheckworthinessFactors and
CheckworthinessAssessment score whether a claim is worth checking (priority
∈ low | medium | high | urgent).
CORRECTION_KINDS — correction | clarification | update | retraction.
CORRECTION_SEVERITIES — low | medium | high. ArticleCorrection
(extends Timestamped) — id, articleId, sourceId, kind, severity,
summary, publicNote, publishedAt, metadata.
5.6 Bias (bias.ts)#
The bias type system models both the continuous political score (a float on
−1…+1) and the discrete Ghana Political Lean (GPL) band (an integer on −3…+3
mapping to NDC vs. NPP alignment). The discrete band is used for display and
editorial reporting; the continuous score is used for computation and trending.
The CoverageBlindspot type models the complementary problem of what the
newsroom is not covering.
BiasScore — political (−1…1), regional (string[]), confidence,
methodology, lastAssessedAt.
Ghana Political Spectrum —
GHANA_POLITICAL_AXIS_VERSION = 'gh_political_axis_v1'.
GHANA_POLITICAL_BIAS_BANDS is the integer set [-3, -2, -1, 0, 1, 2, 3],
where −3 = Strong NDC lean … +3 = Strong NPP lean.
toGhanaPoliticalBiasBand(score) clamps and rounds a continuous score;
ghanaPoliticalAlignment(band) maps a band to npp | ndc | independent;
ghanaPoliticalBandLabel(band) returns the human label.
BIAS_ASSESSMENT_METHODS —
automated_nlp | manual_review | crowd_sourced | expert_panel | algorithmic | hybrid.
BiasAssessment — id, targetType (article | source), targetId,
overallScore (BiasScore), ghanaPoliticalBand, method,
linguisticIndicators (LinguisticBiasIndicators), coverageIndicators
(CoverageBiasIndicators), notes, assessedAt, assessedBy.
PERSPECTIVE_STANCES — supporting | critical | neutral | analysis.
Perspective carries source, url, stance, summary, metadata.
BiasCorrection suggests balancing actions (biasType ∈
political | regional | source | linguistic | coverage). CoverageBlindspot
(type ∈ topic | region | perspective | source_type) and NewsDietAnalysis
model under-coverage detection.
5.7 Entity (entity.ts)#
Entities are the named real-world things mentioned in articles: people, organizations, places, and events. The system links entity mentions across articles (entity linking) and maintains structured sub-types for Ghana-specific entities such as political figures with their position, party, and constituency.
ENTITY_TYPES —
person | organization | place | event | product | work | concept | legislation | other.
Entity — id, type, name, normalizedName, summary, externalIds
(EntityExternalIds: wikidata, wikipedia, dbpedia, freebase, custom),
metadata.
EntityMention records an entity occurrence in text (startOffset,
endOffset, confidence, salience). Subtypes: PersonEntity,
OrganizationEntity (orgType ∈
government | political_party | ngo | company | media | academic | other),
PlaceEntity (placeType ∈
country | region | city | district | landmark | other), EventEntity
(eventType ∈
election | conference | protest | disaster | ceremony | sports | other).
EntityCandidate / EntityResolutionResult model entity linking.
Ghana-specific — GHANA_POLITICAL_POSITIONS
(president | vice_president | minister | deputy_minister | mp | mce | dce | regional_minister | chief_of_staff | speaker | chief_justice).
GhanaPoliticalFigure extends PersonEntity with politicalData (position,
party ∈ npp | ndc | cpp | other, constituency, region, tenureStart,
tenureEnd).
5.8 Media (media.ts)#
MediaAsset is the base type for all media attached to articles: images,
videos, audio renders, embeds, and documents. Sub-types add provider-specific
fields (e.g., VideoAsset carries HLS/DASH URLs; AudioAsset carries a
waveform for the audio player). Storage is abstracted over multiple backends
(s3, gcs, r2, minio, local, cdn).
MEDIA_KINDS — image | video | audio | embed | document | infographic.
MEDIA_ROLES —
featured | inline | thumbnail | gallery | social | hero | background | other.
MEDIA_STORAGE_PROVIDERS — external | s3 | gcs | r2 | minio | local | cdn.
MEDIA_SOURCES — extracted | uploaded | generated | licensed.
MediaAsset — id, kind, role, source, originalUrl, cdnUrl,
mimeType, width, height, durationSeconds, sizeBytes, checksum,
altText, caption, credit, storage (MediaStorage), metadata.
Subtypes: VideoAsset (extends MediaAsset; transcodeStatus ∈
pending | processing | completed | failed, variants, hlsUrl, dashUrl,
thumbnail), AudioAsset (variants, waveform, transcript), EmbedAsset
(provider ∈
youtube | vimeo | twitter | facebook | instagram | tiktok | spotify | soundcloud | other).
ResponsiveImage / ImageVariant support responsive image delivery.
5.9 Taxonomy (taxonomy.ts)#
Taxonomy organizes articles into a three-level hierarchy: broad Category
(e.g., politics, sports), more specific Topic (with trending detection), and
free-form Tag. The 12 seed categories (DEFAULT_CATEGORIES) and 16 Ghana
administrative regions (GHANA_REGIONS) are hard-coded constants so that
classification models and the UI can reference them by stable values.
Category (extends Timestamped) — hierarchical via parentId; CategoryTree
adds children, depth, path. Topic (extends Timestamped) — more
specific than category, carries isTrending and trendScore. Tag
(extends Timestamped) — free-form, with usageCount.
DEFAULT_CATEGORIES — 12 seed categories: politics, business, sports,
entertainment, technology, health, education, environment, crime, opinion,
regional, international.
GHANA_REGIONS — all 16 administrative regions, each with slug, name,
code (e.g. greater-accra/GA, ashanti/AS, … western-north/WN).
Helpers: buildCategoryPath, getCategoryDescendants. Statistics types:
CategoryStats, TopicStats.
5.10 Story Cluster (story-cluster.ts)#
A StoryCluster groups articles from different outlets that cover the same
underlying event. Clustering can be triggered by semantic similarity, entity
overlap, temporal proximity, or a hybrid of all three. StoryTimeline and
StoryTimelinePoint give readers a chronological view of how a story developed.
StoryImportanceScore feeds the recommendation engine with a newsworthiness
signal beyond recency.
CLUSTERING_METHODS —
semantic_similarity | entity_overlap | temporal_proximity | topic_modeling | hybrid | manual.
STORY_CLUSTER_STATUSES — active | developing | archived | merged.
StoryCluster (extends Timestamped) — id, title, summary, status,
method, score, canonicalArticleId, articleIds, lastActivityAt,
archivedAt, mergedIntoId, metadata.
StoryTimeline / StoryTimelinePoint (eventType ∈
breaking | update | analysis | reaction | follow_up) build chronological story
views. StoryCoverageAnalysis reports source/regional/language distribution and
a perspective balance. StoryImportanceFactors / StoryImportanceScore score
newsworthiness (timeliness, impact, prominence, proximity, conflict, novelty,
human interest). ClusterMergeRequest, RelatedStoryRecommendation,
StorySearchParams, StorySearchResult complete the API.
5.11 Author (author.ts)#
Authors can be either external correspondents (human writers from ingested
sources) or internal Veritas contributors. AuthorProfile is the public- facing
profile used on the website; ArticleAttribution handles syndication credit for
aggregated content.
Author (extends Timestamped) — id, name, slug, bio, avatarUrl,
sourceId, socialLinks, expertise, metadata. AuthorRef and UserRef
are lightweight references. ArticleAttribution records syndication credit.
CONTRIBUTOR_ROLES —
author | editor | reviewer | translator | photographer | illustrator | contributor.
AUTHOR_VERIFICATION_STATUSES — unverified | pending | verified | notable.
AuthorProfile (extends Author) is the public-profile entity with
credentials, affiliations, beats, awards, profileStats,
profileCompleteness. DTOs: AuthorCreateInput, AuthorUpdateInput,
AuthorProfileUpdateInput.
5.12 Errors (errors.ts)#
All Veritas services use a shared error hierarchy so that API responses are
consistent and clients can write a single error-handling path. DomainError
carries a DOMAIN_ERROR_CODES code for programmatic discrimination;
normalizeError and createErrorResponse are helpers used by the Hono error
handler in veritas-api.
@veritas/core exports HTTP error classes (HttpError, BadRequestError,
UnauthorizedError, ForbiddenError, NotFoundError, ConflictError,
ValidationError, RateLimitError, InternalError,
ServiceUnavailableError), a DomainError class with DOMAIN_ERROR_CODES, and
helpers normalizeError and createErrorResponse.
Grounding:
libs/veritas/core/src/{common,language,article,source,fact-check, bias,entity,media,taxonomy,story-cluster,author,errors,index}.ts.
6. REST API (@veritas/api)#
veritas-api is the single HTTP gateway for all external traffic. Web, mobile,
and B2B clients speak only to this service; it proxies no other internal
services directly but shares database access via the common library layer. All
routes are versioned under /api/v1. The service exposes 29 route groups
covering the full product surface: editorial content, analytics, payments,
compliance, and B2B access.
6.1 Server Configuration#
- Framework: Hono with
@hono/node-server. - Route mounting: all v1 routes are mounted under
/api/v1(seeregisterV1Routes, which creates a sub-Honoand callsapp.route('/api/v1', v1)). - Middleware (
server.ts, applied in order): requestlogger(optional),timing,secureHeaders,cors(optional), Redis-backed rate limiting on/api/*(optional), sandbox detection on/api/*, API-key authentication on/api/*(optional), and a globalerrorHandler. - Auth: API-key authentication middleware (
X-API-Key); a separate JWT access-token mechanism backs the/auth/*routes (infrastructure/auth/accessToken.ts). - Sandbox mode: requests with test API keys (
sk_test_*) are detected bysandboxMiddleware;X-Sandbox-*headers tune scenario, seed, latency, and error injection. A dedicated/api/v1/sandboxroute group is mounted when a Postgres pool is available. - OpenAPI:
generateOpenApiSpecemits an OpenAPI document served atGET /openapi.json, with Swagger UI atGET /docs(and/docs/*). The spec declares a single server with base URL/api/v1.
6.2 Route Groups#
All paths below are relative to /api/v1. Methods are taken directly from the
route handler source files. The tables are organized by functional area; within
each area, read the route list as a contract — what the API promises to provide.
Note on the health route: the /health and /ready liveness/readiness
endpoints are mounted at the app root (not under /api/v1), so they are
reachable without an API key for use by load balancer health checks.
Health (routes/health.ts, mounted at app root, not under /api/v1):
liveness/readiness endpoints.
Auth (auth.ts):
| Method | Path | Purpose |
|---|---|---|
| POST | /auth/register |
Register a reader account |
| POST | /auth/login |
Log in, issue access token |
| GET | /auth/me |
Current authenticated user |
| POST | /auth/logout |
Log out |
Articles (articles.ts):
| Method | Path | Purpose |
|---|---|---|
| GET | /articles |
List articles |
| GET | /articles/trending |
Trending articles |
| GET | /articles/search |
Search articles |
| GET | /articles/search/suggest |
Search autocomplete suggestions |
| GET | /articles/:id |
Article detail |
| GET | /articles/:id/related |
Related articles |
Sources (sources.ts):
| Method | Path | Purpose |
|---|---|---|
| GET | /sources |
List sources |
| GET | /sources/bias-spectrum |
Sources arranged by bias |
| GET | /sources/media-groups |
Media-group groupings |
| GET | /sources/cross-ownership |
Cross-ownership relationships |
| GET | /sources/:id |
Source detail |
| GET | /sources/:id/transparency |
Transparency record |
| GET | /sources/:id/corrections |
Source corrections |
| GET | /sources/:id/bias |
Source bias assessment |
| GET | /sources/:id/ownership |
Ownership detail |
| GET | /sources/:id/regional-emphasis |
Regional coverage emphasis |
| GET | /sources/:id/regional-comparison |
Regional comparison |
Claims and fact-checking (claims.ts):
| Method | Path | Purpose |
|---|---|---|
| GET | /claims |
List claims |
| GET | /claims/:id |
Claim detail |
| GET | /claims/:id/articles |
Articles citing a claim |
| POST | /fact-check |
Run a fact check |
Bias (bias.ts):
| Method | Path | Purpose |
|---|---|---|
| POST | /bias/score |
Score text for political bias |
| GET | /articles/:id/bias |
Get an article's bias score |
| POST | /articles/:id/bias |
Compute/store an article's bias |
The bias routes use a pluggable PoliticalBiasScorer from AppContext; when
not wired, they fall back to a Ghana keyword baseline in
domain/political-bias-scorer.ts.
Stories (stories.ts): GET /stories, GET /stories/:id,
GET /stories/:id/articles.
Taxonomy (taxonomy.ts): GET /categories, GET /topics.
Media renders (audio.ts, video.ts): GET /audio, GET /video.
Voice / Avatar profiles (voiceProfiles.ts, avatarProfiles.ts): voice and
avatar profile management.
Writing assistant (writingAssistant.ts): POST /writing-assistant.
Tips (tips.ts): POST /tips — encrypted reader tip submission.
Home-feed preferences (preferences.ts):
GET / PUT / PATCH /preferences/home-feed.
Reading mode (reading-mode.ts): reading-mode preferences.
Compliance — NMC (compliance.ts): 14 endpoints under /compliance/nmc/*
covering guidelines, violations, remedies, complaint stages, content checks
(POST /compliance/nmc/check, .../check/batch), complaints CRUD and
transitions, per-source compliance scores, statistics, and ethics references.
Data protection (data-protection.ts): DPA (Data Protection Act) consent
and subject-request endpoints.
Election coverage (election-coverage.ts): 11 endpoints under /election/*
— constitutional references, guidelines, offences, parties, phase detection,
coverage checks (POST /election/check), active elections, configuration, and
per-election violations and balance reports.
Payments — Mobile Money (payments.ts): 13 endpoints under
/payments/momo/* (request, status, pre-approval, refund, validate, balance,
collection/disbursement/pre-approval callbacks) plus
/payments/transactions/:userId and /payments/subscriptions/:userId.
Payments — Vodafone / AirtelTigo / Stripe (vodafone-payments.ts,
airteltigo-payments.ts, stripe-payments.ts): provider-specific payment
routes.
Subscriptions (subscriptions.ts): ~26 endpoints — plan catalog
(/subscriptions/plans, .../plans/:planId), current subscription, lifecycle
(subscribe, upgrade, downgrade, cancel, resume, pause, unpause),
history, usage metering (usage, usage/monthly, usage/increment), feature
access checks, family-plan invitations and membership, and student verification.
Paywall (paywall.ts): paywall configuration and metering.
Subscription analytics (subscription-analytics.ts): subscription metrics.
Ads (ads.ts): 15 endpoints under /ads/* — config, eligibility,
placements, impression/click tracking, ad units CRUD, campaigns CRUD, campaign
status, creatives, and analytics.
Ad networks (ad-networks.ts): ad-network configuration.
B2B API (b2b-api.ts): ~27 endpoints under /b2b/* — content feed
(/b2b/feed, .../feed/trending, .../feed/:id), search and suggest,
fact-check claims and search, entity search/CRUD/articles/relationships and
mention/relationship ingestion, media listing/search, alerts CRUD plus alert
notifications, and /b2b/monitoring/stats.
API keys (api-keys.ts): 11 endpoints under /api-keys/* — create, list,
detail, update, delete, rotate, per-key usage and billing, usage summary,
validation, and rate-limit checks.
Grounding: apps/veritas/api/src/interfaces/http/server.ts;
routes/v1/index.ts; the individual route files under routes/v1/.
7. NLP Service (veritas-nlp)#
veritas-nlp is a dedicated Hono microservice for all NLP analysis. It runs as
a separate process from the API so that CPU-intensive analysis tasks do not
block API response times. The NLP service exposes 23 HTTP endpoints (plus
health) organized around nine capability areas: sentiment, topics, keywords,
summarization, claims, language, embeddings, evidence, and Ghana-specific NLP.
Most analysis endpoints have a /batch variant for processing multiple items in
a single request.
The service is provider-agnostic: it routes each request to the best available provider (e.g., Cohere for embeddings, HuggingFace for sentiment, Ghana NLP Khaya for local-language tasks) and falls back gracefully if a provider is unavailable. All provider API keys are read from environment at startup:
HUGGINGFACE_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, COHERE_API_KEY,
SERPER_API_KEY, BRAVE_API_KEY, GHANA_NLP_API_KEY, KHAYA_API_KEY.
NLP Endpoints#
The table below lists all 23 analysis endpoints plus the three health probes.
The default port is NLP_PORT (3002).
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Health check |
| GET | /healthz |
Simple liveness |
| GET | /ready |
Readiness probe |
| POST | /sentiment |
Sentiment analysis |
| POST | /sentiment/batch |
Batch sentiment analysis |
| POST | /topics/classify |
Topic / category classification |
| POST | /topics/classify/batch |
Batch topic classification |
| POST | /keywords/extract |
Keyword extraction |
| POST | /keywords/extract/batch |
Batch keyword extraction |
| POST | /summarize/extractive |
Extractive summarization |
| POST | /summarize/abstractive |
Abstractive (LLM) summarization |
| POST | /claims/extract |
Claim extraction from text |
| POST | /claims/extract/batch |
Batch claim extraction |
| POST | /language/detect |
Language detection |
| POST | /language/code-switch |
Code-switching detection |
| POST | /language/normalize |
Ghanaian English normalization |
| POST | /embeddings/embed |
Text embedding generation |
| POST | /embeddings/embed/batch |
Batch embedding generation |
| POST | /similarity/compare |
Semantic similarity comparison |
| POST | /evidence/search |
Evidence retrieval for claim verification |
| POST | /ghana/translate |
Ghana NLP cross-language translation |
| POST | /ghana/tts |
Ghana NLP local-language TTS |
| POST | /ghana/ner |
Named-entity recognition via Ghana NLP |
Service Providers#
Each NLP capability is implemented under src/services/ as a separate module
with a primary provider and optional fallbacks. The table below lists the
capability, its default provider, and any secondary providers.
The NLP service organizes implementations under src/services/, each with a
default provider plus fallbacks:
- sentiment — HuggingFace + a Ghana-calibration layer.
- topics — HuggingFace zero-shot classification.
- keywords — RAKE algorithm.
- summarization — extractive (statistical) and abstractive (LLM).
- claims — heuristic extractor plus an LLM extractor.
- embeddings — Cohere and OpenAI providers.
- similarity — HuggingFace.
- evidence — Serper and Brave search providers.
- language — detection and normalization.
- ghana — Ghana NLP / Khaya client (translate, TTS, NER).
Grounding: apps/veritas/nlp/src/server.ts; config.ts; src/services/*
directory listing.
8. Ingestion Pipeline (veritas-ingestion)#
veritas-ingestion is the content discovery engine. It runs as a CLI process
with four modes that together implement the full ingestion cycle. In production
these modes typically run as separate containers pointing at the same database
and Redis instance:
- Scheduler polls the source registry on configured intervals and writes raw feed items to PostgreSQL.
- Producer reads unprocessed feed items from PostgreSQL and pushes them onto the BullMQ queue for the Worker.
- Worker picks up queue jobs, scrapes the full article, normalizes it, runs SimHash deduplication, and stores it in PostgreSQL and Elasticsearch.
- Seed bootstraps a fresh installation with the 33 Tier-1 and government Ghana news sources.
A single CLI deployable with four modes (apps/veritas/ingestion/src/main.ts):
| Mode | Command | Description |
|---|---|---|
| Scheduler | node main.js scheduler |
Fetches feeds on schedule, queues raw items |
| Producer | node main.js producer |
Enqueues unprocessed items into the BullMQ queue |
| Worker | node main.js worker |
Processes the queue: scrape, normalize, deduplicate |
| Seed | node main.js seed |
Seeds the database with Ghana news sources |
The seed command supports --dry-run and --summary.
Collectors and Pipeline#
The ingestion pipeline is composed of four sub-systems wired together in the Worker mode. Collectors fetch raw feed data; the Scraper extracts the full article body; the Pipeline normalizes and deduplicates; the Queue manages job delivery and retry.
- Collectors (
src/collectors/): RSS collector + parser, sitemap collector, social collector, X (Twitter) collector. - Scraper (
src/scraper/): article extraction,robots.txthandling, throttling. - Pipeline (
src/pipeline/): language detection, media extraction, normalization, timestamp normalization, SimHash near-duplicate detection, and the processing worker. - Queue (
src/queue/): the main processing queue is namedveritas:content-processing; the per-item job type isprocess_feed_item.
Seeded Sources#
Running node main.js seed bootstraps the source registry with two sets of
Ghana news sources. New engineers should run the seed command after the first
database migration to get a working set of sources for local development.
The seed command upserts two source sets:
TIER1_GHANA_SOURCES(seeds/tier1-ghana-sources.ts) — 21 Tier-1 Ghana media sources (e.g. MyJoyOnline, Adom Online, Citi Newsroom, Graphic Online), grouped by media group, contributing roughly 80 feeds in total.GOVERNMENT_SOURCES(seeds/government-sources.ts) — 12 Ghana government / institutional sources, contributing roughly 22 feeds.
Combined: 33 seeded sources / ~102 feeds. Helper functions (getTotalFeedCount,
getMediaGroupSummary, getGovFeedSummary, getActiveGovSources) summarize
the seed set; runSeed also prints a media- group breakdown and a bias-band
distribution.
Grounding: apps/veritas/ingestion/src/main.ts;
src/seeds/{seed, tier1-ghana-sources,government-sources,index}.ts;
src/queue/constants.ts; src/collectors/, src/pipeline/, src/scraper/
directory listings.
9. AI Workers (veritas-ai-workers)#
veritas-ai-workers is the background processing engine for AI enrichment. Each
worker is a BullMQ consumer that processes one specific job type — a clean
separation that allows individual workers to be scaled independently based on
queue depth. Workers are organized into four registries by function;
ALL_WORKERS merges all four for registration at startup.
To discover workers programmatically, use listWorkers() to get all 24 worker
definitions, or getWorker(name) to retrieve a specific one by its queue name.
BullMQ workers organized into four registries under
apps/veritas/ai-workers/src/workers/. ALL_WORKERS merges all four;
getWorker and listWorkers provide lookup. There are 24 workers in total.
Content generation (CONTENT_WORKERS, 8): weather-report,
traffic-update, market-summary, fuel-price, gpl-score,
ecg-load-shedding, event-calendar, trend-analysis.
Content processing (PROCESSING_WORKERS, 9): article-summarization,
headline-variants, article-tagging, article-priority, claim-linking,
press-release, breaking-news, entity-extraction, seo-metadata.
Analysis (ANALYSIS_WORKERS, 5): story-clustering, source-accuracy,
blindspot-detection, political-bias, sentiment-analysis.
Editorial (EDITORIAL_WORKERS, 2): fact-check, original-content.
Each worker exposes a tick function and a getDefault*Options factory.
Grounding:
apps/veritas/ai-workers/src/workers/{index,content/index, processing/index,analysis/index,editorial/index}.ts.
10. Persistence — Prisma Schema#
The Prisma schema at libs/veritas/database/prisma/schema.prisma (≈1,900 lines)
defines roughly 75 models and 50 enums. It is the authoritative source of truth
for the database structure — the domain object model in @veritas/core (§5)
mirrors it in TypeScript, but the Prisma schema is what actually migrates.
The schema is large; the sections below summarize the most important enums and models organized by functional area. Engineers implementing new features should read the full schema file directly, using this section as a map.
10.1 Selected Enums#
The following enums are defined in the Prisma schema. Several have counterparts
in @veritas/core but with slightly different value sets — where there is a
difference, both are documented (see §5.4 for the SourceKind divergence).
SourceStatus (active | paused | retired | pending); SourceKind
(newspaper | broadcaster | news_agency | digital_native | wire_service | publisher | government | ngo | blog | social | academic | other
— broader than the @veritas/core SOURCE_KINDS); OwnershipType; FeedType;
ArticleStatus; ArticleContentType; TranslationQuality;
PerspectiveStance; EntityType; MediaKind / MediaRole / MediaSource /
MediaStorageProvider; FactCheckOverallStatus; ClaimType; EvidenceStance;
ClusteringMethod; StoryClusterStatus; EmbeddingProvider; VoiceProvider;
SocialPlatform; ContentPriority; ScheduleRunStatus;
WhatsAppSubscriberStatus / WhatsAppPostStatus; SourcePreferenceType;
ContributorRole; CorrectionKind / CorrectionSeverity; AIReviewKind; the
NMC enums (NmcViolationCategory, NmcComplaintStage, NmcComplaintSeverity,
NmcComplainantType, NmcRemedy); the DPA enums (DpaLawfulBasis,
DpaProcessingPurpose, DpaRequestType, DpaRequestStatus); the election
enums (ElectionType, ElectionCoverageViolationType,
CoverageComplianceLevel); and the payment enums (PaymentTransactionType,
PaymentTransactionStatus, SubscriptionStatus).
10.2 Selected Models by Area#
The 75 models group naturally into the functional areas listed below. Each
bullet names the Prisma models that belong to that area; the relationships
between models in the same area are captured in the schema's @relation
directives.
- Sources & feeds —
Source,SourceFeed,FeedItem,NormalizedFeedItem,SocialMonitor,SocialPost. - Articles —
Article(withpriority,canonicalNormalizedFeedItemId, flagsisOriginal/isAIGenerated/humanReviewed, engagement counters, and ~30 relations),Author,ArticleAuthor,ArticleAttribution,ArticleTranslation,ArticlePerspective,ArticleRelated. - Taxonomy —
Category,Topic,Tag,ArticleCategory,ArticleTopic,ArticleTag. - Entities —
Entity,ArticleEntity,EntityRelationship. - Media —
MediaAsset,ArticleMedia. - Story clusters —
StoryCluster,StoryClusterMember. - Fact-checking —
Claim,ArticleClaim,ArticleFactCheck,ClaimVerification,ClaimEvidence. - Embeddings —
ArticleEmbedding(pgvector). - Auth & users —
AuthUser,AuthIdentity,PasswordReset,UserArticleRead,UserSavedArticle,UserTopicFollow,UserSourcePreference,UserAuthorFollow. - Media production —
VoiceProfile,ArticleAudioRender,AvatarProfile,ArticleVideoRender. - Notifications —
UserPushToken,PushNotificationOutbox,PushNotificationDelivery. - Social scheduling —
YouTubeShortsScheduleRun,TikTokScheduleRun,WhatsAppChannelSubscriber,WhatsAppChannelPost. - Editorial —
ArticleCorrection,ArticleAIReview,ArticleRevision,ArticleSummaryVariant,ArticleHeadlineVariant,CalendarEvent. - Compliance —
NmcComplaint,NmcComplaintTransition,NmcContentCheck,NmcViolationFlag;DpaConsent,DpaSubjectRequest;Election,ElectionCoverageViolation. - Payments —
PaymentTransaction,PaymentSubscription.
Grounding: libs/veritas/database/prisma/schema.prisma — model/enum
declarations.
11. Domain Events (@veritas/events)#
The event bus enables loose coupling between Veritas services and other Oshun
domains. When an article is published, the veritas.article.published event
fires and any subscriber — inside or outside Veritas — can react without being
called directly. This is how, for example, a notification service learns that a
breaking news article has gone live without being tightly coupled to the CMS.
@veritas/events is built on @oshun/event-bus. It exposes
createVeritasPublisher / createVeritasSubscriber and the
VERITAS_EVENT_TYPES constant. The schema-to-payload mapping is
VeritasEventPayloads.
VERITAS_EVENT_TYPES defines 41 event type strings, all veritas.*-namespaced,
grouped into ten areas. The table below maps each constant name to its string
value and shows which group it belongs to.
| Group | Event types (constant → string) |
|---|---|
| Article lifecycle | ARTICLE_CREATED veritas.article.created; ARTICLE_UPDATED; ARTICLE_PUBLISHED; ARTICLE_UNPUBLISHED; ARTICLE_ARCHIVED; ARTICLE_DELETED |
| Content processing | ARTICLE_INGESTED; ARTICLE_PROCESSED; ARTICLE_ENRICHED; ARTICLE_TRANSLATED |
| Fact-checking | CLAIM_EXTRACTED; CLAIM_VERIFIED; FACT_CHECK_STARTED; FACT_CHECK_COMPLETED; FACT_CHECK_FAILED |
| Story clustering | STORY_CLUSTER_CREATED veritas.story.created; STORY_CLUSTER_UPDATED; STORY_CLUSTER_MERGED; ARTICLE_CLUSTERED |
| Source & feed | SOURCE_CREATED; SOURCE_UPDATED; SOURCE_SUSPENDED; SOURCE_ACTIVATED; FEED_FETCHED; FEED_FAILED |
| Media generation | VIDEO_GENERATION_STARTED; VIDEO_GENERATION_COMPLETED; VIDEO_GENERATION_FAILED; AUDIO_GENERATION_STARTED; AUDIO_GENERATION_COMPLETED; AUDIO_GENERATION_FAILED; IMAGE_GENERATION_COMPLETED |
| Alerts | BREAKING_NEWS_PUBLISHED veritas.alert.breaking; MODERATION_ALERT; BIAS_ALERT; MISINFORMATION_ALERT |
| Analytics | TRENDING_UPDATED; ENGAGEMENT_RECORDED; READER_MILESTONE |
| User | USER_SUBSCRIBED; USER_PREFERENCES_UPDATED; USER_SAVED_ARTICLE |
| NLP processing | NLP_SENTIMENT_COMPLETED; NLP_TOPICS_CLASSIFIED; NLP_KEYWORDS_EXTRACTED; NLP_SUMMARY_GENERATED; NLP_ENTITIES_EXTRACTED |
Representative Event Payloads#
Not all 41 event types have a dedicated payload interface — 37 do; the remaining 4 are declared as type constants only. The payloads below are the most commonly subscribed-to events and illustrate the data available to consumers.
ArticleCreatedPayload(extendsArticleEventBase:articleId,slug,headline, optionalsourceId/sourceName/authorIds/categoryIds/topicIds,languageTag) — addscontentType,isOriginal,isAIGenerated, optionalsourceUrl,summary.ClaimVerifiedPayload—claimId, optionalarticleId,verdict(true | mostly_true | mixed | mostly_false | false | unverifiable),confidence, anevidencearray, optionalverifiedBy,verificationMethod(automated | manual | hybrid).FeedFetchedPayload—feedId,sourceId,sourceName,feedUrl,itemsFetched,newItems,fetchDurationMs, optionalnextFetchAt.BreakingNewsPublishedPayload—alertId,articleId,headline, optionalsummary,priority(critical | high | normal), optionalcategoryIds/topicIds,expiresAt.NLPEntitiesExtractedPayload—articleId, anentitiesarray (text,type,confidence,positions, optional linked id/url),processingDurationMs.
Note: VeritasEventPayloads maps 37 of the 41 event types to explicit payload
types; ARTICLE_ARCHIVED, ARTICLE_DELETED, SOURCE_SUSPENDED,
SOURCE_ACTIVATED, FACT_CHECK_FAILED, the *_GENERATION_STARTED/*_FAILED
variants, and the three USER_* events are declared as type constants without a
dedicated payload interface.
Grounding: libs/veritas/events/src/types.ts; src/index.ts.
12. Agent System (veritas-agents, @veritas/agents-*)#
The agent system implements a virtual newsroom. Seven specialized agents run concurrently, each with a defined role, priority, and set of capabilities. The Orchestrator distributes incoming tasks to the appropriate agent; the Message Bus handles inter-agent communication; the State Manager persists agent state across restarts; and the Health Monitor self-heals failed agents.
For new engineers, the easiest mental model is: the 24 AI workers (§9) do fast, queue-based enrichment without judgment; the 7 agents do slower, reasoned editorial work with full editorial context and decision authority.
12.1 Agents Service#
veritas-agents (apps/veritas/agents/) is a Hono service composed of:
- Orchestrator (
orchestration/orchestrator.ts) — task distribution withdistributionStrategy: 'load_balanced', auto-scaling thresholds, agent registry, and periodic state sync. - Message Bus (
communication/message-bus.ts) — Redis pub/sub with an in-memory fallback, bounded queue, and retry policy. - State Manager (
state/state-manager.ts) — Redis KV state with a 24-hour TTL, compression, and theveritas:state:key prefix. - Health Monitor (
monitoring/health-monitor.ts) — self-healing with a failure threshold of 3, latency/error-rate thresholds, and a cap of 10 healing actions per hour. - Metrics Collector (
metrics/metrics-collector.ts) — per-agent metrics.
12.2 Default Agent Roster#
getDefaultAgents() in apps/veritas/agents/src/main.ts registers the 7
default agents with the priorities and concurrency limits shown below. Higher
priority agents pre-empt lower-priority ones when resources are constrained. The
two journalist agents (IDs journalist-1 and journalist-2) share the same
role constant but have different specializations (investigative and political
respectively).
getDefaultAgents() in apps/veritas/agents/src/main.ts registers 7
agents:
| Agent ID | Role | Priority | Max concurrent tasks |
|---|---|---|---|
editor-in-chief-1 |
editor_in_chief |
10 | 5 |
managing-editor-1 |
managing_editor |
9 | 5 |
fact-checker-1 |
fact_checker |
8 | 3 |
journalist-1 |
journalist (investigative) |
7 | 3 |
journalist-2 |
journalist (political) |
7 | 3 |
content-strategist-1 |
content_strategist |
6 | 3 |
social-media-manager-1 |
social_media_manager |
5 | 5 |
AGENT_ROLES (in core/types.ts) also defines system roles orchestrator and
monitor. JOURNALIST_SPECIALIZATIONS are
political | business | sports | entertainment | regional | investigative | breaking_news | general.
AGENT_STATUSES are
initializing | idle | busy | paused | error | shutting_down | stopped.
TASK_TYPES enumerates content-creation, verification, media, research,
editorial, and system task kinds.
12.3 Agents HTTP API#
veritas-agents exposes an HTTP API for submitting tasks and monitoring agent
health. It is an internal API — only called by veritas-api and the CMS, not
exposed to external clients directly.
All routes prefixed /v1:
| Method | Path | Purpose |
|---|---|---|
| GET | /health, /ready |
Service liveness/readiness |
| GET | /v1/agents |
List all agents |
| GET | /v1/agents/:id |
Agent detail |
| GET | /v1/agents/role/:role |
Agents filtered by role |
| GET | /v1/agents/:id/health |
Agent health |
| GET | /v1/agents/:id/issues |
Agent health issues |
| POST | /v1/tasks |
Submit a task |
| GET | /v1/tasks/:id |
Task detail |
| GET | /v1/orchestrator/stats |
Orchestrator statistics |
| POST | /v1/orchestrator/distribute |
Trigger task distribution |
| GET | /v1/health/issues |
Active health issues |
| GET | /v1/health/history |
Self-healing history |
| GET | /v1/metrics |
Metrics (JSON) |
| GET | /v1/metrics/prometheus |
Metrics (Prometheus format) |
12.4 Specialized Agent Libraries#
@veritas/agents-editorial ships editor-in-chief, managing-editor, and
content-strategist (with a Google Trends provider).
@veritas/agents- journalism ships investigative-correspondent, political-,
business-, and breaking-news-correspondent agents.
@veritas/agents-fact- checking ships the fact-checker agent with
web-evidence search. @veritas/agents-social-media, @veritas/agents-devops,
@veritas/agents-product, and @veritas/agents-qa ship their respective
agents. @veritas/agents-core provides the base agent, LLM client,
context-window manager, message bus, state, health, metrics, and logging
primitives; @veritas/agents-orchestrator provides an orchestrator with a
registry, router, and conflict resolver.
Grounding: apps/veritas/agents/src/{main,server,core/types}.ts;
apps/veritas/agents/src/{orchestration,communication,state,monitoring, metrics}/
directory listings; libs/veritas/agents-*/src/ listings.
13. CMS (veritas-cms)#
The CMS is the final gate before publication. Articles pass through it in a
Draft → Review → Schedule → Publish lifecycle. The AI review stage uses Claude
to produce a structured approve/reject/request-changes decision that either
progresses the article to scheduling or returns it to the journalist with
written rationale. The rate limiter and quiet-hours configuration prevent feed
flooding and respect Ghanaian audience patterns.
The CMS service (apps/veritas/cms/) runs the editorial workflow. Its
configuration (src/config.ts) sets:
maxArticlesPerWindow: 6— at most 6 articles per 30-minute window.defaultQuietHours: { start: '22:00', end: '06:00' }— no scheduled publishing during quiet hours.defaultTimezone: 'Africa/Accra'.- Optimal-time windows including a 06:00 morning slot and a 20:00–22:00 prime- time window.
Service modules: articles/, revisions/ (revision service with diffing),
reviews/ (AI-assisted review service), scheduling/ (scheduler service with
quiet-hours enforcement), queue/ (publication queue), and storage/ (memory
storage adapter).
Grounding: apps/veritas/cms/src/{config,types,main}.ts; src/ directory
listing.
14. Content Authentication (@veritas/content-auth)#
Content authentication gives Veritas's AI-generated articles and media assets a cryptographic provenance record that can be independently verified. This matters for two reasons: (1) it allows Veritas to prove an article's origin if it is later disputed or misattributed; (2) the C2PA manifests make the content interoperable with other publishers and platforms implementing the C2PA standard for AI-generated media disclosure.
@veritas/content-auth implements cryptographic content hashing, blockchain
timestamp proofs, authenticity certificates, media-origin tracking, edit
history, and device signing, plus deepfake and image-manipulation detection. It
exports managers including BlockchainTimestampManager and
DeviceSigningManager (with create* factory functions).
The type surface includes ContentHash / HashAlgorithm, BlockchainTimestamp
/ BlockchainNetwork / MerkleTreeNode / BatchTimestamp,
AuthenticityCertificate / DigitalSignature / VerificationBadge /
CustodyRecord, MediaOrigin / ExifData, and a full C2PA type set —
C2PAManifest, C2PAAssertion, C2PACredential, C2PASignature,
C2PAValidationStatus — for content-provenance manifests.
Grounding: libs/veritas/content-auth/src/index.ts; src/ listing.
15. V2 Esports Fact-Checking Bridge#
The V2 esports tooling package @v2/esports-tools (apps/v2/esports-tools/)
consumes @veritas/fact-checking to verify esports outcomes and reporting after
matches conclude. Veritas owns the verification primitives; V2 owns only the
esports framing and the rollback isolation. Veritas itself does not depend on
V2.
veritas-esports-fact-checking.ts exposes buildV2VeritasEsportsFactChecking
(binding id v2.esports.veritas-fact-checking), which composes the real Veritas
scoring primitives — calculateVerificationScore, calculateConsensus, and
getDomainCredibility — over evidence collected for three surfaces: bracket
result verification, post-match reporting, and the esports news
pipeline. The output is Veritas's own FactCheckReport type, so a bracket or
news claim is scored against weighted, domain-credibility-ranked evidence
exactly as any other Veritas claim would be.
The contract is deliberately advisory and off rollback. Its rollback policy
is off-rollback-post-match-journalism and it carries
mayInfluenceRollback: false: a fact-check verdict can never rewrite a match
that already happened, reorder a bracket, or feed back into the deterministic
simulation. A verified bracket emits
v2.esports.veritas.bracket-result.verified. When the reported winner, the
replay winner, and the bracket winner disagree, the bridge does not silently
pick one — it holds publication of the affected post-match report or news story
until the conflict is resolved by an editor, so unverified esports claims never
ship as fact.
This bridge is documented under
V2/docs/integration/veritas-esports-fact-checking.md and is verified by
V2/ue/Tools/check-v2-veritas-esports-fact-checking.py.
Grounding: apps/v2/esports-tools/src/veritas-esports-fact-checking.ts;
libs/veritas/fact-checking/src/.
16. External Integrations#
The table below maps each external service to the library or application that uses it and summarizes its purpose. All provider credentials are injected as environment variables — no keys are hard-coded in source.
| Service | Library/App | Purpose |
|---|---|---|
| Anthropic Claude | llm, agents-core, nlp |
LLM orchestration, abstractive summarization |
| OpenAI | llm, nlp |
Alternative LLM provider, embeddings |
| Cohere | nlp (embeddings) |
Embeddings |
| HuggingFace | nlp (sentiment, topics, similarity) |
Inference API |
| Ghana NLP (Khaya) | ghana-nlp, nlp (ghana) |
Local-language TTS, NER, translation |
| Serper | nlp (evidence) |
Search-based evidence retrieval |
| Brave Search | nlp (evidence) |
Alternative evidence search |
| HeyGen | video-production, veritas-video |
AI video avatar rendering |
| ElevenLabs | audio-production, veritas-audio |
English text-to-speech |
| Stripe | payments, veritas-api |
Card payments and subscriptions |
| Mobile money | payments, veritas-api |
MTN MoMo, Vodafone Cash, AirtelTigo Money |
| Firebase (FCM) | notifications |
Push notifications |
| Social platforms | social-automation, veritas-social |
YouTube, TikTok, Meta, Telegram, etc. |
Grounding: apps/veritas/nlp/src/config.ts; route files under
apps/veritas/api/src/interfaces/http/routes/v1/; library directory names.
17. Configuration Reference#
The table below lists the environment variables required by the main Veritas services. Variables not listed here (e.g., social platform API keys, HeyGen credentials) follow the same pattern: read from environment, no defaults in source code.
| Variable | Consumed by | Purpose |
|---|---|---|
VERITAS_DATABASE_URL |
database, api, ingestion | PostgreSQL connection string |
PORT |
api, video, social | HTTP port (api default 3002) |
NLP_PORT |
nlp | NLP service port (default 3002) |
HUGGINGFACE_API_KEY |
nlp | HuggingFace inference |
ANTHROPIC_API_KEY |
nlp, agents, ai-workers | Claude API access |
OPENAI_API_KEY |
nlp, agents | OpenAI access |
COHERE_API_KEY |
nlp | Cohere embeddings |
SERPER_API_KEY |
nlp | Serper evidence search |
BRAVE_API_KEY |
nlp | Brave evidence search |
GHANA_NLP_API_KEY |
nlp | Ghana NLP / Khaya API |
KHAYA_API_KEY |
nlp | Khaya API (alternate key) |
Grounding: apps/veritas/api/src/main.ts; apps/veritas/nlp/src/config.ts.
18. Acceptance Criteria#
The acceptance criteria below define the minimum observable behaviors that confirm a Veritas deployment is correctly wired. They can be used as a post-deployment smoke test checklist and as the reference for what a CI integration test suite should verify.
A deployment of the Veritas domain is correct when:
- Type integrity —
@veritas/coreexports the documented branded primitives, entities, enums, and error classes;@veritas/modelsZod schemas validate them. - API surface —
veritas-apimounts all v1 route groups under/api/v1, serves OpenAPI at/openapi.jsonand Swagger UI at/docs, and applies the middleware chain (timing, security headers, rate limiting, sandbox detection, API-key auth) in order. - NLP service —
veritas-nlpexposes the documented analysis endpoints and reports readiness via/ready. - Ingestion —
veritas-ingestionruns in scheduler, producer, worker, and seed modes;seedupserts the 33 Tier-1 and government Ghana sources. - AI workers — the 24 workers across the four registries are discoverable
via
listWorkersandgetWorker. - Persistence — the Prisma schema migrates against PostgreSQL with the
pgvectorandcitextextensions. - Events —
@veritas/eventspublishes and subscribes the 41veritas.*event types via@oshun/event-bus. - Agents —
veritas-agentsregisters the 7-agent roster with the documented priorities and exposes the/v1/*orchestration API. - CMS —
veritas-cmsenforces the 6-articles-per-30-minutes rate limit and the 22:00–06:00 Africa/Accra quiet hours.
Grounding Statement#
Every entity, field, enum value, endpoint, event, and count in this document was
read directly from source in libs/veritas/* and apps/veritas/* — principally
libs/veritas/core/src/, libs/veritas/events/src/types.ts,
libs/veritas/database/prisma/schema.prisma, and the route, worker, seed,
agent, and config files cited under each section's grounding note. No counts,
schemas, or integrations were inferred. Where the codebase and an earlier draft
of these docs disagreed (source counts, the API route prefix, the language-tag
set, the worker roster), the code was treated as authoritative.