← All projects
CommerceMedia

The Actual News

News as a public service — verifiable by design

The Problem: Architectural Truthfulness

The crisis in journalism is not primarily one of intent but of architecture. Newsrooms filled with principled reporters publish through platforms whose economic incentives reward engagement over accuracy, whose correction mechanisms are afterthoughts, and whose verification processes are invisible to readers. Kovach and Rosenstiel identified the core tension decades ago: journalism's first obligation is to the truth, but the institutions delivering journalism are structured around business models that treat truth as a byproduct rather than the product itself.[1] Habermas's analysis of the public sphere reveals the deeper structural problem — when the communicative infrastructure of democratic discourse is colonized by market logic, the rational-critical debate that journalism is supposed to enable degrades into manufactured consent and spectacle.[2] The Actual News starts from a different premise: what if the platform itself made truthfulness the product? Not fact-checking after publication, but a native architecture where every published story ships with a machine-readable verification spine — atomic claims with confidence scores, content-addressed evidence graphs with provenance chains, and append-only correction history. If a story does not meet the evidence threshold, it does not publish.

graph TD S["Published Story"] --> N["Narrative Layer<br/>(Human-Readable Article)"] S --> CL["Claims Ledger<br/>(Typed Atomic Statements)"] S --> EG["Evidence Graph<br/>(Content-Addressed Objects)"] N -->|"each paragraph maps to"| CL CL -->|"each claim links to"| EG CL --> CS["Confidence Scores<br/>0.0 — 1.0"] CL --> SS["Support Status<br/>supported / contested / unsupported"] EG --> CA["Content Addresses<br/>(SHA-256 hashes)"] EG --> TE["Typed Edges<br/>corroborates / contradicts / contextualizes"] EG --> WE["Weighted Edges<br/>strength 0.0 — 1.0"] EG --> PC["Provenance Chains<br/>source → intermediary → claim"] style S fill:#1a1a2e,stroke:#e94560,color:#fff style N fill:#16213e,stroke:#0f3460,color:#fff style CL fill:#16213e,stroke:#0f3460,color:#fff style EG fill:#16213e,stroke:#0f3460,color:#fff
Three-layer story model — every published story decomposes into a human-readable narrative, a structured claims ledger, and a content-addressed evidence graph

Technical Architecture

The platform is implemented as five TypeScript microservices behind a unified API gateway, with a Next.js frontend and PostgreSQL as the primary data store. The Gateway service handles authentication, rate limiting, and request routing. The Story service manages narrative content and its decomposition into claim references. The Claim service maintains the ledger of typed atomic statements — each claim carries a confidence score, a support status (supported, contested, or unsupported), and bidirectional links to both its parent narrative and its supporting evidence. The Evidence service manages the content-addressed object store and the typed, weighted edge graph. The Verify service orchestrates the publish gate — the deterministic quality evaluation that decides whether a story meets the threshold for publication.[3] Newman's argument for service decomposition along business capability boundaries applies precisely: each service owns a distinct domain concept (narrative, claim, evidence, verification), communicates through well-defined contracts, and can be deployed independently. The choice of TypeScript across all services reflects a deliberate tradeoff — shared type definitions between services reduce integration errors at the cost of language-level flexibility.[4] Fowler's patterns for managing distributed state — Unit of Work, Identity Map, Repository — are implemented in the Story and Claim services to ensure that the three-layer decomposition remains consistent across concurrent edits.

verify-service/src/queries/publish-gate.sql
-- Publish Gate: deterministic quality evaluation
-- All thresholds loaded from versioned policy pack
WITH story_metrics AS (
  SELECT
    s.id AS story_id,
    COUNT(c.id) AS total_claims,
    COUNT(c.id) FILTER (
      WHERE c.support_status = 'unsupported'
    )::float / NULLIF(COUNT(c.id), 0) AS unsupported_ratio,
    COUNT(c.id) FILTER (
      WHERE c.support_status = 'contradicted'
    ) AS contradicted_count,
    COUNT(DISTINCT e.id) FILTER (
      WHERE e.source_type = 'primary'
    )::float / NULLIF(COUNT(DISTINCT e.id), 0) AS primary_evidence_ratio,
    BOOL_AND(
      c.confidence >= p.min_confidence
    ) AS meets_confidence_floor,
    COUNT(DISTINCT e.id) FILTER (
      WHERE e.source_type = 'primary'
        AND hi.claim_id IS NOT NULL
    ) >= p.high_impact_corroboration AS high_impact_corroborated
  FROM stories s
  JOIN claims c ON c.story_id = s.id
  LEFT JOIN evidence_edges ee ON ee.claim_id = c.id
  LEFT JOIN evidence e ON e.id = ee.evidence_id
  LEFT JOIN high_impact_claims hi ON hi.claim_id = c.id
  CROSS JOIN active_policy p
  WHERE s.id = $1
  GROUP BY s.id, p.min_confidence, p.high_impact_corroboration
)
SELECT
  story_id,
  CASE
    WHEN unsupported_ratio > (SELECT max_unsupported FROM active_policy)
      THEN 'FAIL: unsupported claim ratio exceeded'
    WHEN contradicted_count > 0
      THEN 'FAIL: contradicted claims present'
    WHEN primary_evidence_ratio < (SELECT min_primary_ratio FROM active_policy)
      THEN 'FAIL: insufficient primary evidence'
    WHEN NOT meets_confidence_floor
      THEN 'FAIL: claims below confidence floor'
    WHEN NOT high_impact_corroborated
      THEN 'FAIL: high-impact claims lack corroboration'
    ELSE 'PASS'
  END AS gate_result,
  unsupported_ratio,
  contradicted_count,
  primary_evidence_ratio
FROM story_metrics;
Publish gate evaluation — a single transactional query that enforces all quality thresholds before a story can reach publication

The Evidence Graph

The evidence graph is the system's epistemological core. Every piece of evidence is stored as a content-addressed object — its identifier is the SHA-256 hash of its contents, making it immutable and independently verifiable. Evidence objects are connected to claims through typed, weighted edges: a document might corroborate a claim with weight 0.8, or contextualize it with weight 0.5, or contradict it with weight 0.9. This structure draws directly from Berners-Lee's linked data principles — each evidence node has a URI, is dereferenceable, and links to other nodes using standards-based relationships — but applies them to epistemological rather than informational linking.[5] The provenance chain extends this further: every evidence object records its source, any intermediaries through which it was obtained, and the timestamp of acquisition, creating an auditable trail from published claim back to primary source. This is not fact-checking in the traditional sense — a human reviewer deciding whether something is true — but what Mercier and Sperber call "epistemic vigilance" encoded as data structure.[6] Their argumentative theory of reasoning — that human reason evolved not for individual truth-seeking but for social justification and evaluation — maps onto the evidence graph's design: the graph does not determine truth but makes the structure of justification transparent, enabling readers (and automated systems) to evaluate the reasoning for themselves.

Test IDPurposeInvariant
CT-01Reject stories with zero claimsEvery published story has at least one supported claim
CT-02Reject unsupported ratio above thresholdUnsupported claims never exceed policy maximum
CT-03Reject any contradicted claimsZero contradicted claims in published stories
CT-04Enforce primary evidence minimumPrimary evidence ratio meets policy floor
CT-05Validate confidence floorAll claims meet minimum confidence score
CT-06Require high-impact corroborationHigh-impact claims have independent corroboration
CT-07Verify correction immutabilityCorrections are append-only; original claims preserved
Conformance test matrix — seven tests (CT-01 through CT-07) validating gate behavior against an RFC-style protocol specification with 10 hard invariants

Policy-as-Code

The publish gate's quality thresholds are not hardcoded but externalized into versioned policy packs — JSON documents that define the specific numerical boundaries for each gate check. A local civic newsroom covering city council meetings and a national investigative platform breaking complex financial stories can run the same codebase with different policy configurations: the civic newsroom might accept a lower primary evidence ratio for routine meeting coverage while demanding strict corroboration for any claims about public officials, while the investigative platform might require multiple independent sources for every claim regardless of impact classification.[7] Ostrom's framework for polycentric governance — multiple overlapping authorities with nested rules — is the direct inspiration: the platform provides the rule engine, but the rules themselves are locally determined, versioned, and auditable. Lessig's observation that code is law takes on literal force here — the publish gate's SQL query is the editorial policy, executable and testable rather than aspirational and ambiguous.[8] Policy packs are immutable once deployed: changing a threshold creates a new version, and historical stories retain a reference to the policy version under which they were published, enabling retrospective analysis of how editorial standards evolved over time.

graph TD SUB["Story Submitted<br/>for Publication"] --> LOAD["Load Active<br/>Policy Pack"] LOAD --> CHECK["Run Gate Query<br/>(Single Transaction)"] CHECK --> C1{"Unsupported<br/>ratio OK?"} C1 -->|No| FAIL["FAIL<br/>Return metrics + reason"] C1 -->|Yes| C2{"Zero<br/>contradictions?"} C2 -->|No| FAIL C2 -->|Yes| C3{"Primary evidence<br/>ratio met?"} C3 -->|No| FAIL C3 -->|Yes| C4{"Confidence<br/>floor met?"} C4 -->|No| FAIL C4 -->|Yes| C5{"High-impact<br/>corroborated?"} C5 -->|No| FAIL C5 -->|Yes| PASS["PASS<br/>Story published"] FAIL --> Q["Queue for<br/>additional reporting"] PASS --> PUB["Published with<br/>verification spine"] style SUB fill:#1a1a2e,stroke:#e94560,color:#fff style PASS fill:#0d1117,stroke:#238636,color:#fff style FAIL fill:#0d1117,stroke:#da3633,color:#fff
Publish gate evaluation flow — a story submission triggers deterministic threshold checks against the active policy pack, producing a pass or fail with detailed metrics

Correction Model and Append-Only History

Traditional news platforms handle corrections through quiet edits — the original text is silently replaced, and readers who saw the earlier version have no way to know what changed or why. The Actual News treats corrections as first-class immutable events in an append-only log. When a claim is corrected, the original claim is preserved with its original evidence graph, a new claim version is appended with updated evidence, and a correction event records the reason, the author, the timestamp, and the diff between old and new.[3] Newman's event-sourcing pattern — storing state changes as an immutable sequence of events rather than overwriting current state — provides the technical foundation: the claims ledger is not a mutable table but an append-only log where every state transition is recorded. This makes it possible to reconstruct the exact state of any story at any point in its history, including which evidence supported which claims at the time of original publication versus after correction. Conformance test CT-07 validates this invariant: it verifies that the correction mechanism never deletes or modifies original claim records, only appends new versions with correction metadata.

claim-service/src/handlers/correct-claim.ts
interface CorrectionEvent {
  originalClaimId: string;
  correctedText: string;
  reason: string;
  newEvidence: EvidenceRef[];
  author: string;
  timestamp: Date;
}

async function correctClaim(
  event: CorrectionEvent,
  tx: Transaction
): Promise<ClaimVersion> {
  // Original claim is NEVER modified — only its status changes
  await tx.query(
    `UPDATE claims SET support_status = 'corrected',
     corrected_at = $1 WHERE id = $2`,
    [event.timestamp, event.originalClaimId]
  );

  // New version appended with full provenance
  const newVersion = await tx.query(
    `INSERT INTO claim_versions
     (claim_id, version, text, confidence, evidence_refs,
      correction_reason, corrected_by, created_at)
     VALUES ($1, nextval('claim_version_seq'), $2, $3, $4, $5, $6, $7)
     RETURNING *`,
    [
      event.originalClaimId,
      event.correctedText,
      0.0, // confidence resets — must be re-evaluated
      JSON.stringify(event.newEvidence),
      event.reason,
      event.author,
      event.timestamp,
    ]
  );

  // Correction event recorded in append-only audit log
  await tx.query(
    `INSERT INTO correction_log
     (claim_id, old_version, new_version, reason, author, timestamp)
     VALUES ($1, $2, $3, $4, $5, $6)`,
    [
      event.originalClaimId,
      newVersion.rows[0].version - 1,
      newVersion.rows[0].version,
      event.reason,
      event.author,
      event.timestamp,
    ]
  );

  return newVersion.rows[0];
}
Correction event handler — appends a new claim version while preserving the original, creating an auditable correction trail

Economic Model and the AGPL Mandate

The economic model follows from the architecture: if value comes from verification rather than attention, then funding can decouple from advertising and click-through metrics. A platform whose product is the evidence graph — the structured, machine-readable justification for every claim — creates value that compounds over time as the graph grows, rather than depreciating as news cycles move on. Zuboff's analysis of surveillance capitalism demonstrates the structural problem with attention-based models: when user engagement is the product, the platform's economic incentive is to maximize time-on-site through emotional provocation rather than informational accuracy.[9] The Actual News inverts this by making the evidence graph the monetizable asset — institutions, researchers, and other newsrooms can subscribe to the structured data feed, paying for verified claims and their provenance chains rather than for reader eyeballs. Benkler's framework for commons-based peer production provides the theoretical grounding for the licensing choice: the AGPL ensures that any deployment of the platform must share its source code, including modifications, creating a transparency mandate that mirrors the platform's editorial principles.[10] A platform built on the premise that evidence should be transparent cannot itself be opaque. The AGPL makes this principle legally enforceable — any organization running a modified version must publish their modifications, preventing proprietary forks that might weaken the verification requirements while trading on the platform's reputation for rigor.

DimensionAttention-Based PlatformThe Actual News
Revenue sourceAdvertising / user dataEvidence graph subscriptions
Optimization targetTime-on-site / engagementClaim verification coverage
Correction modelQuiet edits / footnotesAppend-only immutable events
Quality signalShares / viralityPrimary evidence ratio
License modelProprietary / closedAGPL — transparency mandate
Value over timeDepreciates (news cycle)Compounds (evidence graph grows)
Economic model comparison — attention-based platforms versus verification-based architecture

The choice of five distinct microservices — rather than a monolith or a more aggressive decomposition — reflects a pragmatic assessment of domain boundaries. The Story, Claim, and Evidence services map to the three conceptual layers of the story model, while the Gateway and Verify services provide cross-cutting infrastructure. Each service can scale independently: during breaking news events, the Story and Claim services see high write throughput, while the Evidence service experiences sustained read-heavy loads as the graph is traversed for verification.[4] Fowler's domain model pattern — where business logic lives in rich domain objects rather than in service-layer scripts — is applied within each service: a Claim is not a passive data transfer object but an entity with behavior (it can evaluate its own support status, compute its confidence score, and validate its evidence links). The inter-service communication uses synchronous HTTP for queries and asynchronous events for state changes, a hybrid that Fowler identifies as appropriate when strong consistency is needed within a bounded context (the publish gate transaction) but eventual consistency is acceptable across contexts (evidence graph updates propagating to related claims).

By the Numbers

5
Microservices
7
Conformance Tests
10
Hard Invariants
3
Story Layers
AGPL
License
SQL
Publish Gate
Figure 1. System metrics — five microservices enforcing deterministic quality policies across three story layers

References

  1. Kovach, Bill and Tom Rosenstiel. The Elements of Journalism: What Newspeople Should Know and the Public Should Expect. Three Rivers Press, 2014.
  2. Habermas, Jurgen. The Structural Transformation of the Public Sphere. MIT Press, 1962.
  3. Newman, Sam. Building Microservices: Designing Fine-Grained Systems. O'Reilly Media, 2015.
  4. Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2002.
  5. Berners-Lee, Tim. Linked Data. W3C Design Issues, 2006.
  6. Mercier, Hugo and Dan Sperber. The Enigma of Reason. Harvard University Press, 2017.
  7. Ostrom, Elinor. Governing the Commons: The Evolution of Institutions for Collective Action. Cambridge University Press, 1990.
  8. Lessig, Lawrence. Code and Other Laws of Cyberspace. Basic Books, 1999.
  9. Zuboff, Shoshana. The Age of Surveillance Capitalism. PublicAffairs, 2019.
  10. Benkler, Yochai. The Wealth of Networks: How Social Production Transforms Markets and Freedom. Yale University Press, 2006.