Skip to content

~/projects

CogniFlight Cloud

Ground station for an aviation fatigue-monitoring capstone: MQTT telemetry, time-series analytics and a desktop-OS dashboard UI I built for operators.

role
Frontend · pilot-enrolment API
status
archived
date
2025
links
repo ↗
  • React
  • Go
  • Gin
  • WebSockets
  • MQTT
  • InfluxDB
  • Telegraf
  • MongoDB
  • Python
  • Docker
  • Traefik
~/projects/cogniflight-cloud/architecture.txt
six services behind Traefik; the Go API and ML engine talk over a Unix socket
capstone mark
97%
services behind Traefik
6
person capstone team
8

The system

CogniFlight detects pilot fatigue in real time. Edge devices in the cockpit stream fatigue indicators — eye-aspect ratio, yawning, heart rate, heart-rate variability, environmental data — and the cloud platform turns that stream into live dashboards, alerts and analysis for operators on the ground.

Six Docker services behind Traefik make up the ground station: a Go + Gin backend, a Python ML engine, a Mosquitto MQTT broker with custom auth, Telegraf feeding InfluxDB time-series storage, MongoDB, and the React dashboard. This was our final-year capstone, built by a team of eight — it earned 97%.

My part: the operators' desktop

The frontend was my seat. Air-traffic controllers juggle many aircraft at once, so instead of a single scrolling dashboard I built the UI as a windowed desktop: a window manager with taskbar, start menu and notifications, where each concern — live edge-node dashboards, pilot management, file explorer, camera feeds, a terminal — is an app operators arrange side by side.

The centerpiece is the edge-node dashboard: live attitude indicators and telemetry charts streaming over a WebSocket, with the ML engine's fatigue reasoning surfaced inline, so an operator sees "microsleep indicators, EAR below threshold" next to the raw signal that caused it. I also seeded the facial-recognition embedding API used to enroll pilots.

Details that changed how I think

These two designs are the work of our backend lead, Brian Felgate — not mine — but I built against them daily, and they raised my bar for what an interface can be.

The backend is a shell. The browser doesn't call REST endpoints for most things — it opens a WebSocket running an actual command shell with 27 commands (ls, cat, chmod, flux, mqtt, ml-rpc…), frames encoded in MessagePack. The frontend terminal app I built is a real client of the same interface every other app uses.

Permissions are tags, not roles. Every entry in the MongoDB-backed virtual filesystem carries read/write/execute tag lists, matched against the user's tags — a capability-style ACL with a meta-rule that you cannot grant tags you don't hold:

func (p FsEntryPermissions) IsAllowed(mode FsAccessMode, tags []string) bool {
    var check []string
    switch mode {
    case ReadMode:              check = p.ReadTags
    case WriteMode:             check = p.WriteTags
    case ExecuteMode:           check = p.ExecuteTags
    case UpdatePermissionsMode: check = p.UpdatePermissionTags
    }
    for _, tag := range tags {
        if slices.Contains(check, tag) {
            return true
        }
    }
    return false
}

Processes talk over sockets, not networks. The Go backend and the Python ML engine (InsightFace face embeddings, threshold-based fatigue reasoning) speak JSON-RPC 2.0 over a Unix domain socket shared through a Docker volume — no exposed ports, no auth surface.

What it taught me

Working on a team of eight with a service-oriented codebase forced clean contracts: my frontend consumed the WebSocket shell and the telemetry fan-out exactly as any other client would. And building an operating-system metaphor for operators here directly shaped the interface of Exequtech OS — and, in a way, the site you're reading.