Files
inter-hub/docs/ihp-overview.md
tegwick 8b6ce5bbc8 docs: add specification, reference docs, workplan, and agent guidance
Adds all Phase 0 content that was created but never committed:
- CLAUDE.md and SCOPE.md — agent and developer orientation
- specs/TailwindForInteractionHubs_v0.2.md — IHF Tailwind coding guide
- docs/ — five IHP v1.5 reference guides (overview, data, controllers, realtime, ihf-mapping)
- workplans/IHUB-WP-0001 — Phase 1 implementation plan (12 tasks, state-hub synced)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 02:07:13 +01:00

8.9 KiB
Raw Blame History

IHP Framework Overview

Reference notes for implementing the Interaction Hub Framework (IHF) using IHP. Based on IHP v1.5.0 (released March 2026).


What IHP Is

IHP (Integrated Haskell Platform) is a batteries-included, full-stack web framework built on Haskell and Nix. Its goal is rapid application development with robust, type-safe code.

  • Language: Haskell (GHC 9.10 default; GHC 9.12 experimental in v1.5)
  • Paradigm: Functional, strongly typed, server-rendered with optional realtime
  • Creator: digitally induced (Hamburg). Open-sourced 2020, in production since 2017
  • Current version: v1.5.0 (March 25, 2026) — largest release to date (1,051 commits)
  • License: MIT

v1.5 Headline Changes

Change Impact
postgresql-simplehasql driver Up to 50% lower query latency
Dev server RAM: 4 GB → 500800 MB Practical on smaller machines
Session middleware 3×, URL gen 5×, rendering 2× faster Overall snappier
typedSql quasiquoter Compile-time SQL type checking against live dev DB
fetchPipelined Multiple queries in one network round-trip
Composite primary key support Needed for join-table models
Integration test mode Temporary Postgres DB per test run
15+ modules on Hackage separately ihp-mail, ihp-datasync, etc.

Design Philosophy

  • Type errors at compile time, not runtime
  • Single command (devenv up) starts a fully self-contained environment — Postgres included, managed by Nix. No Docker, no Kubernetes required
  • Optimized for AI-assisted development — the type system automatically verifies generated code

Core Architecture

MVC-Influenced Structure

Layer IHP Location Role
Model Application/Schema.sql + generated types Schema, query builder, relationships
Controller Web/Controller/<Name>.hs Action handlers, parameter binding, DB calls
View Web/View/<Name>/<Action>.hs HSX templates, View typeclass
Routing Web/Routes.hs + Web/FrontController.hs URL ↔ action mapping
Types Web/Types.hs All controller action constructors
Helpers Application/Helper/Controller.hs, Application/Helper/View.hs Shared logic

Multi-application support: a single project can contain multiple sub-apps (Web/, Admin/). new-application admin generates an Admin/ subtree with routes auto-prefixed /admin/.

Type-Safe URL / Action System

The IHP router always maps HTTP requests to data constructors defined in Web/Types.hs:

data WidgetsController
    = WidgetsAction
    | NewWidgetAction
    | ShowWidgetAction     { widgetId :: !(Id Widget) }
    | CreateWidgetAction
    | EditWidgetAction     { widgetId :: !(Id Widget) }
    | UpdateWidgetAction   { widgetId :: !(Id Widget) }
    | DeleteWidgetAction   { widgetId :: !(Id Widget) }
    deriving (Eq, Show, Data)
  • URLs generated from values, not strings: pathTo ShowWidgetAction { widgetId = someId }
  • Compile-time guarantee: broken links are type errors, not 404s
  • urlTo generates absolute URLs (protocol + domain)

Routing

Defined in Web/Routes.hs, registered in Web/FrontController.hs.

AutoRoute (most common): instance AutoRoute WidgetsController — IHP generates RESTful routes from action name prefixes:

Prefix HTTP Method
Delete DELETE
Create, Update POST/PATCH
anything else GET

Custom routes: implement CanRoute (attoparsec parser URL → action) and HasPath (reverse). customRoutes overrides individual AutoRoute entries. Supports SEO slugs like /widgets/my-slug.

HTTP method override for HTML forms: pass _method=DELETE (or PATCH) as a hidden field.


Development Workflow

Installation

# 1. Install Determinate Nix (with Flakes + lazy-trees)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

# 2. Install ihp-new
nix profile install nixpkgs#ihp-new

# 3. Install direnv and hook into shell
nix profile add nixpkgs#direnv
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc   # or zshrc

Creating a Project

ihp-new my-project
cd my-project
devenv up

First startup: 1015 minutes (downloads GHC, Postgres, all Haskell deps via Nix binary cache). Subsequent starts are fast (under 30s).

Dev Server

devenv up starts everything:

  • Application server on http://localhost:8000
  • IHP IDE + Schema Designer on http://localhost:8001
  • Postgres (managed by Nix; no system Postgres needed)
  • Live reloading (typically sub-50ms after save)

Project File Structure

my-project/
├── Application/
│   ├── Schema.sql              # Single source of truth for all DB types
│   ├── Migration/              # <timestamp>-description.sql files
│   ├── Helper/
│   │   ├── Controller.hs       # Shared controller helpers
│   │   └── View.hs             # Shared view helpers
│   └── Script/                 # One-off scripts / cron job binaries
├── Web/
│   ├── Types.hs                # ALL controller action constructors
│   ├── Routes.hs               # AutoRoute instance declarations
│   ├── FrontController.hs      # WAI entry; dispatch; auth init; default layout
│   ├── Controller/             # One file per controller
│   ├── View/                   # One dir per controller, one file per action
│   │   └── Layout.hs           # Default layout (Html -> Html)
│   └── Component/              # Server-Side Components (optional)
├── Config/
│   ├── Config.hs               # Env vars, secrets, feature flags
│   └── nix/
│       └── hosts/
│           └── production/     # Declarative NixOS server config
├── Test/                       # Integration tests
├── static/                     # CSS, JS, images
├── flake.nix                   # Nix flake — all deps declared here
├── App.cabal                   # Cabal package definition
├── Main.hs                     # Entry point
└── Makefile                    # Build targets

Adding Dependencies

In flake.nix:

ihp = {
  enable = true;
  projectPath = ./.;
  packages = [ pkgs.imagemagick ];    # native deps
  haskellPackages = p: [
    p.ihp
    p.aeson
    p.your-library
  ];
};

Code Generators

Navigate to http://localhost:8001/Generators or right-click a table in Schema Designer → "Generate Controller". Scaffolds controllers, views, routes, and type entries to match the table's fields. Also available: Migration, Job, Mail, Script generators.

Testing

v1.5: integration test mode creates a temporary Postgres DB automatically per test run. Tests live in Test/.


Deployment

Primary: NixOS / deploy-to-nixos

The entire server config (nginx, Let's Encrypt, systemd, app config) lives declaratively in Config/nix/hosts/production/ — version-controlled and reproducible.

deploy-to-nixos production

Runs nixos-rebuild remotely over SSH. AWS EC2: NixOS AMI, t3a.small min (t3a.medium recommended), 60 GiB root disk, ports 22/80/443.

Required env vars:

Variable Purpose
IHP_SESSION_SECRET Session encryption key (new-session-secret to generate)
DATABASE_URL Postgres connection string
IHP_BASEURL External URL (e.g., https://example.com)

Production features built-in:

  • Systemd watchdog (heartbeat 30s; auto-restart after 60s)
  • Socket activation (queues requests during restarts — zero-downtime deploys)
  • Sentry integration via ihp-sentry (IHP Pro)

Docker (IHP Pro)

nix build .#unoptimized-docker-image --option sandbox false
cat result | podman load

Env vars: same as above + IHP_ASSET_VERSION (cache-busting) + IHP_REQUEST_LOGGER_IP_ADDR_SOURCE=FromHeader (behind load balancer). Minimal Docker images lack CA certificates — copy caCertificates and set SSL_CERT_FILE.

Bare Metal Binary

nix build .#optimized-prod-server     # full optimisation
nix build .#unoptimized-prod-server   # faster build, for staging

Runtime: IHP_ENV=Production, DATABASE_URL, PORT (default 8000). GHC RTS tunable via GHCRTS.

CSS/JS Bundling

make static/prod.js static/prod.css

Dev uses individual files; production uses bundled/minified versions. Background jobs require deploying the RunJobs binary alongside the main app.