ai newsSa., 24. Okt.
EilmeldungOpenAI's Codex Found a Node.js Brotli RCE. Imperva Ran Code on an Official Build

Building Climate Court: Evidence-First Climate Investigations with Next.js, Optional AI, and Verifiable Reports

How we built a local-first investigation platform—and why the most valuable improvements came from testing what happens when things go wrong. Corporate sustainability claims often sound precise: “We reduced our carbon emissions by 40%.” But precision in wording does not necessarily mean precision i

dev.toSamstag 11:46 · 24.10.8 Min. Lesezeit
Bild: dev.to

How we built a local-first investigation platform—and why the most valuable improvements came from testing what happens when things go wrong.

Corporate sustainability claims often sound precise:

“We reduced our carbon emissions by 40%.”

But precision in wording does not necessarily mean precision in evidence.

Reduced compared with which year? Does the figure describe absolute emissions or emissions intensity? Which operations are included? What happened to supply-chain emissions?

Climate Court was built to make those questions visible.

The application helps users organize a climate claim into assertions, inspect supporting material, identify evidence gaps, and produce a transparent report. A finalized report can then be fingerprinted and registered on a local blockchain for independent integrity verification.

Every finding should point to evidence—or explain what evidence is missing.

The application includes a dashboard, investigation workspace, printable reports, JSON exports, version history, audit explorer, and demo controls.

It runs locally without paid API keys or cloud accounts.

That independence matters. An investigation should remain usable when an optional model is unavailable, a quota is exhausted, or the local blockchain is offline.

There is also an important boundary: user-provided documents are not automatically authenticated. They remain inconclusive pending independent review. Clearly labeled synthetic fixtures demonstrate supporting and contradictory classifications without implying that the platform has independently verified real companies.

We kept the application as a monolith. The challenge did not require independently deployed services; it required clear boundaries and predictable failure behavior.

The architecture separates presentation, orchestration, business rules, persistence, and external adapters.

flowchart TD User[Presenter or authenticated operator] Gateway[Access boundary] UI[React workspace] API[Validated API routes] Cases[Case services] Engine[Deterministic assessment engine] Store[Repository adapter] Audit[Audit service and reconciliation] PDF[Bounded PDF child process] AI[Optional Gemini adapter] User --> Gateway --> UI --> API API --> Cases Cases --> Engine Cases --> Store Cases -.-> AI API --> PDF API --> Audit Audit --> Store Audit --> EVM[Local Solidity registry] Store --> Local[(Local JSON)] Store -.-> Cloud[(Optional Supabase)] The main modules reflect those responsibilities:

This structure made it possible to improve reliability without replacing the framework or introducing a message broker.

Climate Court’s core assessment does not depend on an LLM.

Deterministic extraction identifies explicit quantities, units, reporting years, and qualifiers. It preserves unresolved questions rather than supplying missing context.

The platform can extract the percentage while leaving the reporting period unresolved. It should not silently decide which year “last year” refers to.

The assessment uses these verdict labels:

SUPPORTED PARTIALLY_SUPPORTED CONTRADICTED INSUFFICIENT_EVIDENCE UNRESOLVED Enter fullscreen mode Exit fullscreen mode The current demo conservatively reserves SUPPORTED for independently complete reviewed evidence rather than issuing it automatically.

Evidence coverage is separate from the verdict. It counts completed checks, including source availability, reporting periods, comparable measurements, and independent scope verification.

It is not a probability that the claim is true.

When configured, Gemini suggests ambiguities. It does not decide the verdict, invent sources, execute tools, or submit transactions.

The effective mode and fallback reason are recorded with the investigation. A “configured” integration flag alone cannot establish that a model successfully ran.

Live Gemini behavior was not part of the completed local validation, so the application does not claim otherwise.

One of the most instructive defects appeared in deduplication.

An early normalization step removed punctuation. That made these passages appear identical:

Emissions were 1.2 tonnes. Emissions were 12 tonnes. Enter fullscreen mode Exit fullscreen mode Case folding introduced another scientific problem:

Energy use was 1 MWh. Energy use was 1 mWh. Enter fullscreen mode Exit fullscreen mode Those units are different.

The corrected implementation deliberately uses conservative, exact text deduplication. It normalizes Unicode and whitespace while preserving case, punctuation, signs, units, and negation.

const key = hash ( excerpt. normalize ( " NFC " ). replace ( / \s +/g, " " ). trim () ); Enter fullscreen mode Exit fullscreen mode Similar passages remain separate. The application no longer claims semantic near-duplicate detection.

This was a useful architectural lesson: normalization is a domain decision. A transformation that helps ordinary text matching can destroy scientific meaning.

We also corrected synthetic source passages so their classification explanations refer to details actually present in the excerpt. A synthetic label is disclosure, not a substitute for traceability.

For the local demonstration, we chose a JSON repository rather than introducing a database server.

That keeps setup accessible, but it requires clear limits.

Finalized snapshots are copied separately from working case data. Later evidence additions do not rewrite those snapshots.

This is application-level immutability, not write-once storage. An actor with filesystem access can still alter files; independent hash verification is what makes changes detectable.

The optional Supabase adapter uses revision-based compare-and-swap updates to reject lost-update races. That implementation still requires separate validation with a live Supabase environment.

The lesson was not that JSON is universally sufficient. It was that a small demo benefits from a storage model whose constraints are explicit and tested.

The Solidity registry stores minimal information:

Documents and full reports remain off-chain.

The contract restricts registration to its operator and rejects duplicate case versions and invalid inputs.

Before registration, the application serializes a finalized snapshot using application-defined canonicalization rules and calculates SHA-256 over the UTF-8 result.

Verification retrieves the actual registry record, recreates the snapshot digest, and compares them.

A matching result establishes integrity against that record. It does not establish that the evidence is authentic or the climate claim is correct.

Transaction confirms on-chain ↓ Saving its local metadata fails ↓ User retries ↓ Contract rejects duplicate registration Enter fullscreen mode Exit fullscreen mode Both individual systems behaved as designed. The combined workflow did not recover.

We fixed this with persisted operation states and reconciliation:

PENDING → BROADCAST → CONFIRMED ↘ UNAVAILABLE ↘ STALE ↘ CONFLICT Enter fullscreen mode Exit fullscreen mode Before sending a transaction, the application checks the existing registry record.

This also fixed another misleading behavior: after a chain reset, cached local metadata could previously produce a success response.

The corrected system checks the chain instead of treating its cache as proof.

The original mutation queue included network work.

That meant a stalled blockchain confirmation could delay unrelated case updates.

The revised approach keeps storage critical sections short:

Persist operation intent ↓ Release storage queue ↓ Perform bounded external work ↓ Re-enter queue ↓ Merge result into the intended version Enter fullscreen mode Exit fullscreen mode Investigation updates use revision checks to avoid overwriting newer edits. Audit results are merged into the specific finalized version they belong to.

We tested this by pausing local mining. Anchoring reached its deadline while another case could still be finalized.

The important distinction is that a per-request RPC timeout is not an end-to-end operation deadline. The complete operation needs its own budget.

Keeping keys off the frontend was necessary, but insufficient.

The API can invoke an authorized registry operator. Without access control, a reachable client could exercise that authority indirectly.

The custom Node server binds to loopback and checks the actual peer, Host header, and original forwarding headers before Next.js handles the request.

It adds a random process-private gateway proof. Local API access requires that proof and fails closed without it.

Case reads, reports, mutations, reset controls, and blockchain actions require authorization.

Additional protections include server-only credential modules, sanitized API errors, secret scanning, staged-content checks, request budgets, and browser security headers.

PDF extraction runs in a separate process with a bounded heap, page/text limits, a deadline, and no inherited application credentials. This provides resource isolation; it is not an OS security sandbox.

A health endpoint that always says “ok” is not a readiness check.

The demo preflight also performs a functional case read and checks the local registry and operator.

Operational events contain an operation name, request ID, outcome, allowlisted diagnostic code, and duration. Raw SDK errors, credentials, request bodies, and source documents are excluded.

Recovery is documented and executable. Backup restoration validates the manifest, stored structure, and finalized snapshot digests, while preserving replaced data for inspection.

Local report backups do not restore ephemeral blockchain history. The runbook makes that distinction explicit.

We used established frameworks as review lenses rather than claiming formal certification.

The most useful outcome was a prioritized list of small changes. None required an enterprise-scale rewrite.

A bounded local resource rehearsal used 20 load cases, five 1,000-word sources per case, and three snapshots each.

After warm-up, across 20 measured iterations:

These are local observations, not hosted availability guarantees.

The production dependency audit reported zero vulnerabilities after patching Zod and PostCSS. A separate full-tree audit still identified development-tool advisories; their applicability and residual risks are documented rather than hidden.

The happy path is weak evidence of reliability. The most valuable tests interrupted the workflow between successful steps.

Retries are part of the architecture. An error message cannot compensate for an operation that is unsafe to repeat.

Truthful status is a correctness requirement. “Configured,” “registered,” “confirmed,” and “verified” describe different facts.

Scientific text needs conservative processing. Decimal points, signs, scope, units, and casing can change the meaning of evidence.

AI availability should not determine application availability. A narrow optional adapter made fallback straightforward.

A monolith can have strong boundaries. Separating responsibilities and shortening critical sections solved the demonstrated problems without multiplying infrastructure.

The next useful capability is a human-reviewed evidence classification workflow, with stronger provenance and richer comparisons across reporting periods and emissions scopes.

Other planned work includes live validation of the optional Gemini and Supabase adapters, authenticated collaboration, and public-testnet integration.

Those are separate deployment and product scopes. The current evidence supports a controlled local single-operator demonstration—not a claim of public production readiness.

Climate Court’s goal remains consistent: help users follow the evidence, preserve uncertainty, and make the resulting investigation independently inspectable.

Repository: github.com/scha54/Climate-Court

Quelle: dev.to (dev.to). Original öffnen