2026-03-27 01:36:18 +00:00
{
inputs = {
ihp . url = " g i t h u b : d i g i t a l l y i n d u c e d / i h p / v 1 . 5 " ;
nixpkgs . follows = " i h p / n i x p k g s " ;
nixpkgs-nixos . follows = " i h p / n i x p k g s - n i x o s " ;
flake-parts . follows = " i h p / f l a k e - p a r t s " ;
devenv . follows = " i h p / d e v e n v " ;
systems . follows = " i h p / s y s t e m s " ;
devenv-root = {
url = " f i l e + f i l e : / / / d e v / n u l l " ;
flake = false ;
} ;
} ;
outputs = inputs @ { self , nixpkgs , nixpkgs-nixos , ihp , flake-parts , systems , . . . }:
flake-parts . lib . mkFlake { inherit inputs ; } {
systems = import systems ;
imports = [ ihp . flakeModules . default ] ;
2026-04-30 17:01:02 +02:00
perSystem = { pkgs , config , lib , . . . }: {
2026-03-27 01:36:18 +00:00
ihp = {
appName = " i n t e r - h u b " ;
enable = true ;
projectPath = ./. ;
packages = with pkgs ; [
2026-04-04 09:55:12 +00:00
tailwindcss
2026-03-27 01:36:18 +00:00
] ;
haskellPackages = p : with p ; [
# Haskell dependencies go here
p . ihp
base
wai
text
# ihp-mail # Email support: https://ihp.digitallyinduced.com/Guide/mail.html
# ihp-datasync # Real-time DataSync
# ihp-job-dashboard # Job dashboard UI
# ihp-typed-sql # Type-safe SQL queries
# ihp-pglistener # PostgreSQL LISTEN/NOTIFY
feat(P5): IHF Phase 5 complete — agent-assisted distillation
Adds bounded AI support to the IHF governance loop. All AI outputs are
attributed (model_ref), reviewable (AgentReviewRecord), and reversible.
No autonomous decisions; no silent requirement promotion.
- T01: Schema — agent_proposals, agent_review_records,
confidence_annotations (migration 1743379200)
- T02: AgentProposalsController (index/show/accept/reject, idempotent
review guard), global nav "Agent" link
- T03: SummarizeClusterAction — Claude API cluster summary on widget show
- T04: DraftRequirementAction — AI requirement draft; acceptance creates
RequirementCandidate (human-gated)
- T05: DetectDuplicatesAction — duplicate_flag proposal on candidate show
- T06: DetectPolicySensitivityAction — policy_flag with
ConfidenceAnnotations per concern scope
- T07: ProposeImplementationAction — impl_proposal from decision show
- T08: AgentAuditDashboardAction — autoRefresh; KPI row, unreviewed queue,
recent proposals, attribution log matrix
- T09: integration tests, SCOPE.md updated, phase5-summary.md, flake.nix
adds http-conduit/aeson/string-conversions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:54:33 +00:00
# Phase 5: Anthropic API calls
http-conduit
aeson
string-conversions
feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Delivers the full Phase 9 external API layer:
- Versioned REST API (/api/v2/) with OpenAPI 3.1 spec; enum arrays for
widget_type, event_type, annotation category drawn live from registry tables
- OAuth 2.0 client credentials flow (/api/v2/token); hub:*:write scopes
gated on active HubCapabilityManifest FK
- API key management: SHA256-hashed tokens, key_prefix for display,
one-time reveal on creation, revocation support
- TypeScript and Python consumer SDKs generated from registry tables
(/api/v2/sdk/ihf-client.ts, /api/v2/sdk/ihf-client.py)
- Webhook delivery: HMAC-SHA256 signing, append-only webhook_deliveries,
fire-and-forget dispatch via forkIO, 3-retry logic
- Admin API dashboard with 24h stats (request count, error rate, last seen)
- Rate limiting (per-minute) and daily quota enforcement via api_request_log
- Schema migration: api_consumers, api_keys, webhook_subscriptions (CHECK
constraint on 6 framework lifecycle topics), webhook_deliveries
(append-only trigger), api_request_log
- ARCHITECTURE-LAYERS.md scorecard: 3.34 → 3.41 (approaching Strong)
- contracts/functional/interaction-reporting-v1.md extended with Phase 9
endpoint catalogue and 422 validation error format
GAAF: no bare TEXT discriminators; webhook event_type uses CHECK constraint
over 6 allowed framework lifecycle topic strings (not widget event types).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 19:52:20 +00:00
# Phase 9: External API, crypto, SDK generation
cryptohash-sha256
base16-bytestring
random-bytestring
yaml
network-uri
2026-03-27 01:36:18 +00:00
] ;
devHaskellPackages = p : with p ; [
cabal-install
hlint
hspec
ihp-hspec
] ;
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
# Hoogle documentation server — disabled to save ~400 MB on constrained host
withHoogle = false ;
2026-03-27 01:36:18 +00:00
# Disable relation type machinery for faster compilation
# relationSupport = false;
# Skip tests/haddock for specific packages to speed up builds
# dontCheckPackages = [ "my-package" ];
# doJailbreakPackages = [ "my-package" ];
# dontHaddockPackages = [ "my-package" ];
# Production build tuning
# optimizationLevel = "2"; # Default: "1", use "2" for more optimized production binaries
# rtsFlags = "-A96m -N"; # GHC runtime flags for compiled binaries
# Mount additional directories under /static/ in production builds
# static.extraDirs = {
# # Frontend = self.packages.${system}.frontend;
# };
# static.makeBundling = true; # Set false if not using Makefile for CSS/JS bundling
} ;
2026-04-29 23:16:44 +02:00
# OCI container image for Kubernetes deployment (Railiance01).
2026-04-29 16:07:27 +02:00
# Build: nix build .#docker
2026-04-29 23:16:44 +02:00
# Push: skopeo copy docker-archive:result docker://92.205.130.254:32166/coulomb/inter-hub:SHA
# Uses IHP's built-in unoptimized image; binary is /bin/RunProdServer.
packages . docker = config . packages . unoptimized-docker-image ;
2026-04-29 16:07:27 +02:00
2026-03-27 01:36:18 +00:00
# Custom configuration that will start with `devenv up`
devenv . shells . default = {
# Start Mailhog on local development to catch outgoing emails
# services.mailhog.enable = true;
# PostgreSQL extensions
# services.postgres.extensions = extensions: [ extensions.postgis ];
2026-04-30 17:01:02 +02:00
# GHC 9.10.3 crash fix: Generated.Types imports 119 modules, exceeding
# the ~287 MB interface-file binary-deserialization limit.
#
# pkgs is built from `import nixpkgs { overlays = devenv.shells.default.overlays; }`.
# IHP adds ihp.overlays.default to this list, which sets
# pkgs.ghc = haskellPackages.override { overrides = ihpOverrides }.
# We extend pkgs.ghc with a mkDerivation override (lib.mkAfter ensures
# we run after IHP's overlay, so prev.ghc is already IHP's package set).
2026-04-30 22:52:10 +02:00
# 2-way split (60 entities) still crashes — TypesPart1.hi itself hits 287 MB.
# 4-way split (~30 entities, ~150 MB .hi each) stays safely under the limit.
2026-04-30 17:01:02 +02:00
# When pname == "inter-hub-models", postUnpack replaces the monolithic
2026-04-30 23:18:52 +02:00
# Types.hs → empty stub (not compiled); inter-hub-lib imports TypesPart1-8 directly.
2026-04-30 17:01:02 +02:00
overlays = lib . mkAfter [
( final : prev : {
ghc = prev . ghc . extend ( hfinal : hprev : {
mkDerivation = args :
let drv = hprev . mkDerivation args ;
in if ( args . pname or " " ) = = " i n t e r - h u b - m o d e l s "
then drv . overrideAttrs ( old : {
2026-05-01 21:51:47 +02:00
# GHC 9.10.3 crash: Data.Binary.Get.runGet at position 287686318.
# Invariant regardless of flags. Workaround: split the 476-module
# inter-hub-models into two Cabal library components so GHC runs
# two separate --make invocations instead of one giant one.
#
# models-inner (~63 modules): Generated.ActualTypes.* + Enums.
# Pure type definitions; no inter-hub-models deps.
# main library (~413 modules): entity ops + Include instances.
# Depends on models-inner.
#
# Long-term intent: explicit module boundaries reduce build cost,
# isolate changes, and make diagnostics cheaper across the board.
2026-05-01 16:48:56 +02:00
configureFlags = ( old . configureFlags or [ ] ) ++ [
" - - g h c - o p t i o n = - O 0 "
" - - g h c - o p t i o n = - f o m i t - i n t e r f a c e - p r a g m a s "
] ;
2026-04-30 17:01:02 +02:00
postUnpack = ( old . postUnpack or " " ) + ''
_cabal = $ ( ls " $ s o u r c e R o o t " /* . c a b a l | h e a d - 1 )
2026-05-01 21:51:47 +02:00
_pname = $ ( grep ' ^ name:' " $ _ c a b a l " | awk ' { print $ 2 } ' )
# Classify exposed-modules into inner vs outer.
# Inner: Generated.ActualTypes.X (capital X) and Generated.Enums
# — these have zero inter-hub-models dependencies.
# Outer: everything else except Generated.Types (empty stub).
_inner = $ ( awk '
/ ^ exposed-modules:/ { e = 1 ; next }
e && / ^ / { m = $ 1 ;
if ( m ~ / ^ Generated \ . ActualTypes \ . [ A-Z ] / || m = = " G e n e r a t e d . E n u m s " )
print m ;
next }
e { e = 0 }
' " $ _ c a b a l " )
_outer = $ ( awk '
/ ^ exposed-modules:/ { e = 1 ; next }
e && / ^ / { m = $ 1 ;
if ( ! ( m ~ / ^ Generated \ . ActualTypes \ . [ A-Z ] / ) &&
m != " G e n e r a t e d . E n u m s " && m != " G e n e r a t e d . T y p e s " )
print m ;
next }
e { e = 0 }
' " $ _ c a b a l " )
# Rewrite the cabal file with two library stanzas.
# Hard-coded deps/extensions match IHP default.nix template
# (pinned flake — these won't drift without a flake update).
cat > " $ _ c a b a l " < < CABAL_EOF
2026-05-01 22:18:03 +02:00
cabal-version : 3 .0
2026-05-01 21:51:47 +02:00
name : $ _pname
version : 0 .1 .0
build-type : Simple
library models-inner
default-language : GHC2021
hs-source-dirs : build
build-depends :
base
, ihp
, basic-prelude
, text
, bytestring
, time
, uuid
, aeson
, postgresql-simple
, deepseq
, data-default
, scientific
, string-conversions
, hasql
, hasql-dynamic-statements
, hasql-implicits
, hasql-mapping
, hasql-postgresql-types
, hasql-pool
, unordered-containers
, postgresql-types
exposed-modules :
$ ( echo " $ _ i n n e r " | sed ' s / ^ / / ' )
default-extensions :
OverloadedStrings
NoImplicitPrelude
ImplicitParams
TypeSynonymInstances
FlexibleInstances
FlexibleContexts
InstanceSigs
MultiParamTypeClasses
TypeFamilies
DataKinds
TypeOperators
UndecidableInstances
ConstraintKinds
StandaloneDeriving
DuplicateRecordFields
OverloadedLabels
OverloadedRecordDot
ghc-options : - Wno-unused-imports - Wno-dodgy-imports - Wno-unused-matches
library
default-language : GHC2021
hs-source-dirs : build
build-depends :
$ _pname : models-inner
, base
, ihp
, basic-prelude
, text
, bytestring
, time
, uuid
, aeson
, postgresql-simple
, deepseq
, data-default
, scientific
, string-conversions
, hasql
, hasql-dynamic-statements
, hasql-implicits
, hasql-mapping
, hasql-postgresql-types
, hasql-pool
, unordered-containers
, postgresql-types
exposed-modules :
$ ( echo " $ _ o u t e r " | sed ' s / ^ / / ' )
default-extensions :
OverloadedStrings
NoImplicitPrelude
ImplicitParams
TypeSynonymInstances
FlexibleInstances
FlexibleContexts
InstanceSigs
MultiParamTypeClasses
TypeFamilies
DataKinds
TypeOperators
UndecidableInstances
ConstraintKinds
StandaloneDeriving
DuplicateRecordFields
OverloadedLabels
OverloadedRecordDot
ghc-options : - Wno-unused-imports - Wno-dodgy-imports - Wno-unused-matches
CABAL_EOF
# Stub out Generated.Types (kept as file for inter-hub-lib)
printf ' % s \ n' ' module Generated . Types ( ) where' \
> " $ s o u r c e R o o t / b u i l d / G e n e r a t e d / T y p e s . h s "
2026-04-30 17:01:02 +02:00
'' ;
} )
2026-04-30 23:18:52 +02:00
else if ( args . pname or " " ) = = " i n t e r - h u b - l i b "
then drv . overrideAttrs ( old : {
2026-05-01 16:48:56 +02:00
# Generated.Types is an empty stub in models — no re-export hub.
# Replace every bare `import Generated.Types` with direct imports
# of all 119 individual entity modules (read from the original
# Generated/Types.hs before it was replaced in models postUnpack —
# each package gets its own unpacked sourceRoot, so Types.hs is
# still intact here). Individual entity .hi files are ~9 MB each.
2026-04-30 23:18:52 +02:00
postUnpack = ( old . postUnpack or " " ) + ''
2026-05-01 16:48:56 +02:00
_types = " $ s o u r c e R o o t / b u i l d / G e n e r a t e d / T y p e s . h s "
_imp = $ ( mktemp )
awk ' / ^ import Generated \ . / { print " i m p o r t " $ 2 } ' " $ _ t y p e s " > " $ _ i m p "
find " $ s o u r c e R o o t " - name " * . h s " | while IFS = read - r _f ; do
2026-04-30 23:18:52 +02:00
if grep - qE " ^ i m p o r t G e n e r a t e d \. T y p e s $ " " $ _ f " ; then
2026-05-01 16:48:56 +02:00
awk - v imp = " $ _ i m p " '
/ ^ import Generated \ . Types $ / {
while ( ( getline ln < imp ) > 0 ) print ln
close ( imp )
2026-04-30 23:18:52 +02:00
next
2026-05-01 16:48:56 +02:00
}
{ print } ' " $ _ f " > " $ _ f . n e w " && mv " $ _ f . n e w " " $ _ f "
2026-04-30 23:18:52 +02:00
fi
done
2026-05-01 16:48:56 +02:00
rm - f " $ _ i m p "
2026-04-30 23:18:52 +02:00
'' ;
} )
2026-04-30 17:01:02 +02:00
else drv ;
} ) ;
} )
] ;
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
# Resource limits for constrained host (2 CPU, ~3.8 GiB RAM).
# -A32m: smaller minor heap (reduces GC pressure).
# -M2g: hard heap ceiling (prevents OOM on large compiles).
# Note: -N1 is intentionally omitted — it requires -threaded and
# would break build-generated-code and similar tools.
# GHC parallel module compilation is capped via -j1 in .ghci.
env . GHCRTS = " - A 3 2 m - M 2 g " ;
2026-03-27 01:36:18 +00:00
# Custom processes that don't appear in https://devenv.sh/reference/options/
processes = {
2026-04-04 09:55:12 +00:00
tailwind . exec = " t a i l w i n d c s s - c t a i l w i n d / t a i l w i n d . c o n f i g . j s - i . / t a i l w i n d / a p p . c s s - o s t a t i c / a p p . c s s - - w a t c h = a l w a y s " ;
2026-03-27 01:36:18 +00:00
} ;
} ;
} ;
# Adding the new NixOS configuration for "production"
# See https://ihp.digitallyinduced.com/Guide/deployment.html#deploying-with-deploytonixos for more info
# Used to deploy the IHP application
flake . nixosConfigurations . " p r o d u c t i o n " = import ./Config/nix/hosts/production/host.nix { inherit inputs ; } ;
} ;
# The following configuration speeds up build times by using the devenv, cachix and digitallyinduced binary caches
# You can add your own cachix cache here to speed up builds. For that uncomment the following lines and replace `CHANGE-ME` with your cachix cache name
nixConfig = {
extra-substituters = [
" h t t p s : / / d e v e n v . c a c h i x . o r g "
" h t t p s : / / c a c h i x . c a c h i x . o r g "
" h t t p s : / / d i g i t a l l y i n d u c e d . c a c h i x . o r g "
# "https://CHANGE-ME.cachix.org"
] ;
extra-trusted-public-keys = [
" d e v e n v . c a c h i x . o r g - 1 : w 1 c L U i 8 d v 3 h n o S P G A u i b Q v + f 9 T Z L r 6 c v / H m 9 X g U 5 0 c w = "
" c a c h i x . c a c h i x . o r g - 1 : e W N H Q l d w U O 7 G 2 V k j p n j D b W w y 4 K Q / H N x h t 7 H 4 S S o M c k M = "
" d i g i t a l l y i n d u c e d . c a c h i x . o r g - 1 : y + w Q v r n x Q + P d E s C t 9 1 r m v v 3 9 q R C Y z E g G Q a l d K 2 6 h C K E = "
# "CHANGE-ME.cachix.org-1:CHANGE-ME-PUBLIC-KEY"
] ;
} ;
}