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

244 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://github.com/digitallyinduced) (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 → 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`:
```haskell
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
```bash
# 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
```bash
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`:
```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.
```bash
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)
```bash
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
```bash
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
```bash
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](https://ihp.digitallyinduced.com/)
- [IHP Guide (full docs)](https://ihp.digitallyinduced.com/Guide/)
- [GitHub: digitallyinduced/ihp](https://github.com/digitallyinduced/ihp)
- [ihp-boilerplate](https://github.com/digitallyinduced/ihp-boilerplate) — template used by `ihp-new`
- [v1.5 release announcement](https://discourse.haskell.org/t/ihp-v1-5-has-been-released/13846)