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>
8.9 KiB
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-simple → hasql driver |
Up to 50% lower query latency |
| Dev server RAM: 4 GB → 500–800 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
urlTogenerates 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: 10–15 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.
Key Links
- IHP Homepage
- IHP Guide (full docs)
- GitHub: digitallyinduced/ihp
- ihp-boilerplate — template used by
ihp-new - v1.5 release announcement