Klaas Vanslambrouck
NLEN

Nidus — case study

The nerve centre of the LOWI platform

This page will become a deeper look at Nidus: architecture, decisions and code. Content is coming soon.

In production · personal use

the choices first, then the screens

Interface

Screenshots

the real interface, with made-up data

This is a rebuilt reconstruction of the real interface with fictional data — not a live screenshot, for privacy reasons (it runs on real household data).

Friday, July 10

Good afternoon, Robin.

AFTERNOON INSIGHT

Your week was calm — no notable spikes in energy or budget. The weekend looks sunny, a good moment to tackle the garden reminders.

WEEKLY REPORT

Boodschappen€142

+18% vs. average €120

Colruyt

Energie€38

+9% vs. average €35

Peak on Wednesday

19°C

Partly cloudy

14°–21°

Tomorrow: 20°C, sunny

Today

  1. Team sync
  2. Pick up groceries

78

Good recovery

HRV54 ms
Resting HR52 bpm
Sleep hours7,2 u

0,38 kW

live

client / api / data / edge / ai

Architecture

Drawn arrows only show connections confirmed in the existing copy.

client

nidus

Next.js · Vercel

Web dashboard: overview and control of the whole platform in the browser.

nidus-mobile

Expo / React Native · iOS

Mobile app for on the go, talks exclusively to nidus-api.

exclusively via nidus-api
api

nidus-api

Fastify 5 / TypeScript · Railway · Frankfurt

Central business logic; the only component with write access to Supabase.

only write access
data

Supabase

PostgreSQL · Frankfurt

Central storage with row-level security and auth.

nidus-ml

FastAPI / Python · Railway

ML inference as a separate service alongside storage.

Databricks

Community Edition · Cloud

Batch ML training and data pipelines.

edge

nidus-pi

Raspberry Pi · Thuisnetwerk

PM2 cron jobs for energy, location and sync.

ai

Claude Haiku

LLM · Anthropic API

Reasoning and generation for Jarvis.

  • nidus-mobile never calls Supabase directly — everything goes through nidus-api.
  • Every table has created_at/updated_at and RLS is always on.
  • Compute is disposable (Railway, Pi); persistence lives exclusively in Supabase.

Decisions

Decision Log

  1. Knowing when to stop: VLamax without blood lactate

    No published formula exists for VLamax based on power or speed alone — confirmed by two independent literature reviews. Rather than continuing to chase a more precise approach, I used a pragmatic proxy (peak 3-second power × a constant, ~21% error margin) and explicitly labelled it 'indicative' in the interface. Sometimes the right technical decision isn't solving the problem, but admitting the available data won't support more precision — and communicating that honestly to the user.

  2. A peer-reviewed formula beats reverse-engineering from data

    The first version of the VO2max model reverse-engineered a constant from five test results, with an error margin over 20%. Switching to a published, speed-based formula (Ji et al., 2021) and calibrating only the individual constant brought that down to 4.71%. Lesson: a validated formula shape with one calibrated parameter beats a fully self-derived model.

  3. Recharts custom SVG

    Custom SVG instead of forcing a chart library

    Training blocks needed to be draggable at second-level precision, with power and heart-rate curves underneath. The existing chart library (Recharts) is used elsewhere in the app but isn't built for precise pointer interaction. Rather than forcing that library, I built a focused custom SVG component with its own pointer events — less reusable, but robust for exactly this problem.

  4. Power curve as JSONB, not a separate table

    When someone drags a training-block boundary, power and heart-rate averages need local recalculation without re-calling the ML model — which means the full curve has to stay available. Instead of a separate table with thousands of rows per session, I stored it as a JSONB column on the session table itself: the data logically belongs to that one session and is never queried on its own.

  5. database browser

    Conversation memory in the browser, not the database

    For this portfolio's AI assistant I had to choose: store conversation history server-side like the assistant in Nidus itself does, or keep it client-side and resend it with every question. For a short, anonymous portfolio visit, the benefit of server-side storage doesn't outweigh the cost: a new database table, a retention policy, and extra privacy surface for unknown visitors. Same technology, a deliberately different architecture for a different context.

Coming soon

Code

Coming soon.