Back to flin
flin

#136 -- Building phpMyAdmin for FLIN

How we built a full admin console for FLIN -- a phpMyAdmin-style interface for entities, storage, and monitoring.

Juste A. Gnimavo (Thales) & Claude | March 26, 2026 8 min flin
EN/ FR/ ES
flinadmin-consolephpmyadminentity-browserdashboard

Every developer who has built a web application eventually reaches the same moment: data is flowing into the database, but there is no way to see it. No way to browse records, no way to check if the route configuration is correct, no way to tell whether the storage backend is connected. The application runs, but it runs blind.

The standard response is to install a separate tool. phpMyAdmin for MySQL. pgAdmin for PostgreSQL. Prisma Studio for Prisma. Supabase Dashboard for Supabase. Each one requires its own installation, its own configuration, its own port, its own authentication. You are now managing two applications instead of one.

FLIN takes a different approach. Every FLIN application ships with a full admin console embedded in the binary. Navigate to /_flin and you get a 19-page management dashboard with entity browsing, query execution, route inspection, storage management, real-time metrics, AI gateway monitoring, and database backups. Zero installation. Zero configuration. Zero additional dependencies. It is there the moment you run flin dev app.flin.

Why an Embedded Console Matters

The decision to embed the admin console directly into the FLIN runtime was not about convenience -- it was about philosophy. FLIN replaces 47 technologies with a single language. If developers still need to install pgAdmin to inspect their data, we have not truly simplified their stack.

Consider what a typical developer needs when building a web application:

NeedTraditional StackFLIN
Browse database recordsInstall pgAdmin/phpMyAdminNavigate to /_flin/entities
Inspect API routesRead code or install SwaggerNavigate to /_flin/routes
Monitor server metricsInstall Grafana + PrometheusNavigate to /_flin/metrics
View server logsSSH into server, tail logsNavigate to /_flin/logs
Manage file storageOpen cloud provider consoleNavigate to /_flin/storage
Execute ad-hoc queriesOpen database clientNavigate to /_flin/query

Six tools replaced by six pages in a single dashboard that ships with every application.

The Architecture: HTML in a Rust Binary

The console is implemented as a pure HTML/CSS/JavaScript SPA embedded directly into the FLIN Rust binary using include_str!(). No build step. No npm dependencies. No bundler. The entire frontend lives in a resources/embedded/console/ directory:

rust// In src/server/console/mod.rs
const SHELL_HTML: &str = include_str!("../../../resources/embedded/console/shell.html");
const CONSOLE_CSS: &str = include_str!("../../../resources/embedded/console/console.css");
const ROUTER_JS: &str = include_str!("../../../resources/embedded/console/router.js");
const LOGIN_HTML: &str = include_str!("../../../resources/embedded/console/login.html");

// Content fragments loaded dynamically
const INDEX_HTML: &str = include_str!("../../../resources/embedded/console/content/index.html");
const ENTITIES_HTML: &str = include_str!("../../../resources/embedded/console/content/entities.html");
const QUERY_HTML: &str = include_str!("../../../resources/embedded/console/content/query.html");
// ... 16 more content pages

This architecture gives us three critical properties:

  1. Zero dependencies. No node_modules, no CDN requests, no external JavaScript libraries. The console works offline, behind firewalls, on air-gapped networks.
  1. Instant loading. The HTML, CSS, and JavaScript are already in memory -- they were compiled into the binary. Serving them is a pointer dereference, not a file read.
  1. Version consistency. The console version always matches the FLIN runtime version. There is no "upgrade the dashboard separately" step. When you update FLIN, the console updates with it.

The SPA Router: Client-Side Navigation

Rather than serving 19 separate HTML pages (which would require full page reloads), the console uses a shell-and-content architecture. The shell (shell.html) contains the sidebar, header, and navigation chrome. Content pages are loaded as HTML fragments injected into the main content area:

flin// Conceptual model of the console routing
route GET "/_flin" {
    serve shell_html    // SPA shell with sidebar
}

route GET "/_flin/content/:page" {
    // Return just the content fragment
    fragment = match page {
        "index" => index_html,
        "entities" => entities_html,
        "query" => query_html,
        "schema" => schema_html,
        "routes" => routes_html,
        "logs" => logs_html,
        "metrics" => metrics_html,
        _ => not_found_html
    }
    serve fragment
}

The client-side router (router.js) intercepts sidebar link clicks, fetches the corresponding content fragment via AJAX, and swaps it into the DOM. The URL updates via history.pushState(), so browser back/forward navigation works correctly. The result feels like a modern single-page application despite being pure vanilla JavaScript.

The 19-Page Console

The console is organized into five sections, accessible from a persistent sidebar:

MAIN
  Home (Dashboard)          -- Stats, health gauges, activity feed
  Entities                  -- phpMyAdmin-style data browser
  Schema                    -- ER diagram visualizer
  Query Editor              -- Execute FLIN queries interactively

API & ROUTES
  Routes                    -- All app routes with method badges
  REST API                  -- Swagger-like interactive API docs
  Getting Started           -- Quick start guide
  Functions                 -- 387+ built-in functions reference

INFRASTRUCTURE
  Realtime                  -- WebSocket/SSE inspector
  Vector Search             -- Embedding stats and providers
  AI Gateway                -- Multi-provider AI monitoring
  Storage                   -- File browser and upload stats
  Backups                   -- Database snapshot management

MONITORING
  Logs                      -- Real-time server request logs
  Metrics                   -- System gauges and counters
  Analytics                 -- Request analytics with top routes

ADMIN
  Users                     -- User management
  Settings                  -- App config, security, 2FA
  Ecosystem                 -- Links to FLIN resources

Every page shows real data. Not mock data, not placeholder content. Session 320 removed the last seven "Coming Soon" badges and connected every page to live API endpoints backed by the FLIN runtime.

The API Layer: 30+ Endpoints

Behind the console pages sits a complete REST API under the /_flin/api/ namespace. The API is implemented in Rust in two files: mod.rs for route dispatching and api.rs for handler functions.

rust// Route dispatcher in src/server/console/mod.rs
pub fn handle_console_request(
    path: &str,
    method: &str,
    body: &[u8],
    db: &ZeroCore,
    session: Option<&str>,
) -> Response {
    // Authentication check (all API routes except login/setup)
    if !is_public_route(path) && !is_valid_session(session) {
        return unauthorized_response();
    }

    match (method, path) {
        // Dashboard
        ("GET", "/_flin/api/stats") => api::get_stats(db),
        ("GET", "/_flin/api/health") => api::get_health(db),
        ("GET", "/_flin/api/activity") => api::get_activity(db),

        // Entity CRUD
        ("GET", p) if p.starts_with("/_flin/api/entities") =>
            api::handle_entity_get(p, db),
        ("POST", p) if p.starts_with("/_flin/api/entities") =>
            api::handle_entity_post(p, body, db),

        // Query execution
        ("POST", "/_flin/api/query") => api::execute_query(body, db),

        // Observability
        ("GET", "/_flin/api/logs") => api::get_logs(),
        ("GET", "/_flin/api/metrics") => api::get_metrics(),
        ("GET", "/_flin/api/analytics") => api::get_analytics(),

        // ... 20+ more routes
        _ => not_found_response(),
    }
}

The API follows REST conventions: GET for retrieval, POST for creation, PUT for updates, DELETE for removal. JSON responses use proper HTTP status codes (201 for creation, 400 for validation errors, 401 for authentication failures). Console API traffic is excluded from the application's own analytics to avoid inflating metrics with admin panel requests.

Comparison With Existing Tools

The FLIN console occupies a unique position in the ecosystem:

FeaturePocketBaseSupabaseFirebaseFLIN Console
Embedded (ships with app)YesNo (cloud)No (cloud)Yes
Data browser + CRUDYesYesYesYes
Query editorNoSQL onlyNoYes (FLIN syntax)
Schema visualizerNoYesNoYes (ER diagram)
REST API auto-docsNoYesNoYes (Swagger-like)
Functions referenceNoNoNoYes (387+)
AI gateway monitoringNoNoNoYes
2FA for adminNoYesYesYes (email OTP)

PocketBase comes closest in philosophy -- it also embeds an admin UI in the binary. But PocketBase is a backend-as-a-service; FLIN is a programming language. The console is not the product. The console is a debugging and management tool for the product you build with FLIN.

The Road to Production-Ready

Building the console took 17 sessions across January and February 2026. The journey was not linear. Session 252 laid the initial infrastructure with storage and database UI shells. Session 259 built the dashboard. Session 260 fixed sidebar inconsistencies. Session 261 made it functional with login and query execution. Sessions 262-263 built the entity browser with full CRUD. Session 266 added the functions reference. Sessions 300-301 polished the UI for scale (90+ entities) and added entity definition management. Session 320 was the production milestone: every mock data source was replaced with real API endpoints.

The result is a console that would have been a standalone product at most companies. At FLIN, it is a feature that ships for free with every application, embedded in a single binary, accessible at a URL that never changes.

The next article dives into the first page every developer sees: the admin console dashboard, with its stats cards, health gauges, and real-time activity feed.


This is Part 136 of the "How We Built FLIN" series, documenting how a CEO in Abidjan and an AI CTO built a full admin console into a programming language runtime.

Series Navigation: - [135] Previous article - [136] Building phpMyAdmin for FLIN (you are here) - [137] The Admin Console Dashboard

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles

Thales & Claude thales

Thirteen Agents, Forty-Three Minutes: The First Claude Fable 5 Workflow Session, And What A Deterministic Orchestration Script Changes About Multi-Agent Builds

One prompt, thirteen agents, forty-three minutes: the first production session with Claude Fable 5 and Claude Code's Workflow tool shipped a complete seven-page production website plus a backend lead-capture endpoint in a single commit. The build log: the deterministic orchestration script, the contract-injection pattern between phases, the per-agent economics of the parallel fan-out, and the session-limit cliffhanger the resume journal turned into a non-event.

20 min Jun 12, 2026
claude-fable-5claude-codeworkflow-toolmulti-agent +10
Thales & Claude casp

The gate caught its own drift: one day inside CASP with Claude Fable 5

We handed the most autonomous Claude model yet the keys to CASP — the open-source CLI that keeps AI coding agents honest against git — with the authority to reject our own roadmap. It rejected five things, found two real bugs in the validator by dogfooding it, fixed them under a two-auditor gate, and left casp check fully green on its own repo for the first time. CASP 0.3.0 is the result.

14 min Jun 10, 2026
caspzerosuiteworkflowai-cto +9
Thales & Claude zerosuite

The CASP Transplant: How The Six-File Discipline Moved From Conductor To An Anti-Fraud Transport ERP, What The /next Skill Adds When The Operator Just Types 'next', And Why The Cost Of CASP Drift Rises When The Project Is Someone Else's Cash

The CASP discipline that ran thirty-five Conductor sessions is product-agnostic. The build log of transplanting it to KASSIA, an anti-fraud transport ERP for a Côte d'Ivoire fleet operator: what moved, what did not (the bespoke validator — and what its absence costs), what the /next skill adds when the operator types one word, and where the CASP stops — the deployment bug it could not see because it records intent, not infrastructure reality.

20 min Jun 8, 2026
kassiaerp-kassia-transport-logistiquezerosuiteCASP +15