diff --git a/src/libraries/utils/markdown.ml b/src/libraries/utils/markdown.ml
index 85c3f96613984bb9792d4169d4ec7fd1712af5c0..2d00ab2478c0c64ecef50ab213d88865edd1708a 100644
--- a/src/libraries/utils/markdown.ml
+++ b/src/libraries/utils/markdown.ml
@@ -61,7 +61,6 @@ and element =
   | Raw of string list
   (** Each element of the list is printed as-is on its own line.
       A blank line separates the [Raw] node from the next one. *)
-  | Delayed of (unit -> elements)
   | H1 of text * string option
   | H2 of text * string option
   | H3 of text * string option
@@ -84,10 +83,10 @@ let glue ?sep ls =
   | _ , [] -> []
   | _ , [l] -> l
   | Some s , ls -> (* tailrec *)
-    let rec aux w s = function
+    let rec aux sep w = function
       | [] -> List.rev w
       | [e] -> List.rev_append w e
-      | e::el -> aux s (List.rev_append s (List.rev_append e w)) el
+      | e::el -> aux sep (List.rev_append sep (List.rev_append e w)) el
     in aux s [] ls
 
 (* -------------------------------------------------------------------------- *)
@@ -139,7 +138,6 @@ let description items = [DL items]
 
 let par text = [Block [Text text]]
 let block b = [Block b]
-let delayed f = [Delayed f]
 
 (* -------------------------------------------------------------------------- *)
 (* --- Sectioning                                                         --- *)
@@ -419,7 +417,6 @@ and pp_element ?page fmt e =
       "@[<hv>@[<hov 5><!-- %a@]@ -->@]" Format.pp_print_text s
   | Table table -> pp_table_inlined ?page fmt table
   (* pp_table_extended ?page fmt table *)
-  | Delayed f -> pp_elements ?page fmt (f ())
   | H1(t,lab) -> Format.fprintf fmt "@[<h># %a%a@]" pp_text t pp_lab lab
   | H2(t,lab) -> Format.fprintf fmt "@[<h>## %a%a@]" pp_text t pp_lab lab
   | H3(t,lab) -> Format.fprintf fmt "@[<h>### %a%a@]" pp_text t pp_lab lab
diff --git a/src/libraries/utils/markdown.mli b/src/libraries/utils/markdown.mli
index 6cc566b6c573d66cb457d36baae19a85dcc1416e..79f35f876fbe3662e366dede54295bfcbd883bf3 100644
--- a/src/libraries/utils/markdown.mli
+++ b/src/libraries/utils/markdown.mli
@@ -76,7 +76,6 @@ and element =
   | Raw of string list
   (** Each element of the list is printed as-is on its own line.
       A blank line separates the [Raw] node from the next one. *)
-  | Delayed of (unit -> elements)
   | H1 of text * string option
   | H2 of text * string option
   | H3 of text * string option
@@ -170,9 +169,6 @@ val block : block -> elements
 *)
 val rawfile: string -> elements
 
-(** Delayed element. The content is computed on pretty-printing. *)
-val delayed: (unit -> elements) -> elements
-
 (** {2 Document Structure} *)
 
 (** Creates a document from a list of elements and optional metadatas.
diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml
index 3731b58b8eb4dca606fbb26672de6755e2758204..961a30ae148b87dd286ddc6cb2f2672f622f4ba3 100644
--- a/src/plugins/server/data.ml
+++ b/src/plugins/server/data.ml
@@ -234,41 +234,66 @@ end
 
 module Fmap = Map.Make(String)
 
-module Record( R : Info ) =
+type 'a record = json Fmap.t
+
+type ('r,'a) field = {
+  member : 'r record -> bool ;
+  getter : 'r record -> 'a ;
+  setter : 'r record -> 'a -> 'r record ;
+}
+
+type 'a signature = {
+  page : Doc.page ;
+  name : string ;
+  descr : Markdown.text ;
+  mutable fields : Syntax.field list ;
+  mutable default : 'a record ;
+  mutable published : bool ;
+}
+
+module Record =
 struct
 
-  type t = json Fmap.t
-
-  type 'a field = {
-    member : t -> bool ;
-    getter : t -> 'a ;
-    setter : t -> 'a -> t ;
+  module type S =
+  sig
+    type r
+    include S with type t = r record
+    val default : t
+    val has : (r,'a) field -> t -> bool
+    val get : (r,'a) field -> t -> 'a
+    val set : (r,'a) field -> 'a -> t -> t
+  end
+
+  let signature ~page ~name ~descr () = {
+    page ; name ; descr ;
+    published = false ;
+    fields = [] ;
+    default = Fmap.empty ;
   }
 
-  (* Declared Fields in this Record *)
-  let fdocs = ref []
-  let defaults = ref Fmap.empty
-
-  let default () = !defaults
-  let has fd r = fd.member r
-  let get fd r = fd.getter r
-  let set fd v r = fd.setter r v
-
-  let field (type a) name ~descr ?default (d : a data) : a field =
+  let field (type a r) (s : r signature)
+      ~name ~descr ?default (d : a data) : (r,a) field =
+    if s.published then
+      raise (Invalid_argument "Server.Data.Record.field") ;
     let module D = (val d) in
     begin match default with
       | None -> ()
-      | Some v -> defaults := Fmap.add name (D.to_json v) !defaults
+      | Some v -> s.default <- Fmap.add name (D.to_json v) s.default
     end ;
-    fdocs := Syntax.{ name ; syntax = D.syntax ; descr } :: !fdocs ;
+    let field = Syntax.{ name ; syntax = D.syntax ; descr } in
+    s.fields <- field :: s.fields ;
     let member r = Fmap.mem name r in
     let getter r = D.of_json (Fmap.find name r) in
     let setter r v = Fmap.add name (D.to_json v) r in
     { member ; getter ; setter }
 
-  let option (type a) name ~descr (d : a data) : a option field =
+  let option (type a r) (s : r signature)
+      ~name ~descr (d : a data) : (r,a option) field =
+    if s.published then
+      raise (Invalid_argument "Server.Data.Record.option") ;
     let module D = (val d) in
-    fdocs := Syntax.{ name ; syntax = option D.syntax ; descr } :: !fdocs ;
+    let field = Syntax.{ name ; syntax = option D.syntax ; descr } in
+    s.fields <- field :: s.fields ;
     let member r = Fmap.mem name r in
     let getter r =
       try Some (D.of_json (Fmap.find name r)) with Not_found -> None in
@@ -277,21 +302,36 @@ struct
       | Some v -> Fmap.add name (D.to_json v) r in
     { member ; getter ; setter }
 
-  let fields () = [Syntax.fields ~title:"Field" !fdocs]
-
-  let syntax =
-    Syntax.publish ~page:R.page ~name:R.name
-      ~descr:R.descr
-      ~synopsis:(Syntax.record [])
-      ~details:(Markdown.delayed fields) ()
-
-  let of_json js =
-    List.fold_left
-      (fun r (fd,js) -> Fmap.add fd js r)
-      (default ()) (Ju.to_assoc js)
-
-  let to_json r : json =
-    `Assoc (Fmap.fold (fun fd js fds -> (fd,js) :: fds) r [])
+  let publish (type r) (s : r signature) =
+    if s.published then
+      raise (Invalid_argument "Server.Data.Record.publish") ;
+    let module M =
+    struct
+      type nonrec r = r
+      type t = r record
+      let descr = s.descr
+      let syntax =
+        let fields = Syntax.fields ~title:"Field" (List.rev s.fields) in
+        Syntax.publish ~page:s.page ~name:s.name ~descr
+          ~synopsis:(Syntax.record [])
+          ~details:[fields] ()
+      let default = s.default
+      let has fd r = fd.member r
+      let get fd r = fd.getter r
+      let set fd v r = fd.setter r v
+      let of_json js =
+        List.fold_left
+          (fun r (fd,js) -> Fmap.add fd js r)
+          default (Ju.to_assoc js)
+      let to_json r : json =
+        `Assoc (Fmap.fold (fun fd js fds -> (fd,js) :: fds) r [])
+    end in
+    begin
+      s.default <- Fmap.empty ;
+      s.fields <- [] ;
+      s.published <- true ;
+      (module M : S with type r = r)
+    end
 
 end
 
diff --git a/src/plugins/server/data.mli b/src/plugins/server/data.mli
index 3e10ece5cbfb35d8753129d85d094c118919bab0..52ab09382893efb8fa7d8c3907197bd9affc707f 100644
--- a/src/plugins/server/data.mli
+++ b/src/plugins/server/data.mli
@@ -94,33 +94,62 @@ module Jtext : S with type t = json (** Rich text encoding, see [Jbuffer] *)
 (** {2 Records} *)
 (* -------------------------------------------------------------------------- *)
 
-module Record(R : Info) :
+type 'a record (** Records of type 'a *)
+type 'a signature  (** Opened signature for record of type ['a] *)
+type ('a,'b) field (** Field of type ['b] for a record of type ['a] *)
+
+(** Record factory.
+
+    You shall start by declaring a (ghost) type [r] and call
+    [Record.signature] to create a signature of type [r].
+    Then, populate the record with [Record.field] or [Record.option].
+    Finally, you shall call [Record.publish] to obtain a new data module
+    of type [Record with type r = r], which gives you a [Data] with an opaque
+    type [t = r record] with fields of type [(r,a) field].
+
+    {[
+      (* ---- Exemple of Record Data --- *)
+      type r
+      let s = Record.signature ~page ~kind ~name ~descr () in
+      let fd_a = Record.field s ~name:"a" ~descr:"..." (module A) in
+      let fd_b = Record.field s ~name:"b" ~descr:"..." (module B) in
+
+      module M = (val (Record.publish s) : Record with type r = r)
+
+      let make a b = M.default |> M.set fd_a a |> M.set fd_b b
+    ]}
+*)
+module Record :
 sig
-  (** A new type [t] is created for each application of the functor. *)
-  include S
-
-  (** Parametric field. Can only be used with type [t]. *)
-  type 'a field
-
-  (** Field constructor *)
-  val field : string -> descr:Markdown.text -> ?default:'a -> 'a data -> 'a field
-
-  (** Optional field constructor *)
-  val option : string -> descr:Markdown.text -> 'a data -> 'a option field
-
-  (** Field presence. If the field has a default value, it will be always
-      present. *)
-  val has : 'a field -> t -> bool
-
-  (** Field accessor.
-      @raise Not_found if the field is optional and not present *)
-  val get : 'a field -> t -> 'a
-
-  (** Field updator. *)
-  val set : 'a field -> 'a -> t -> t
 
-  (** Contains only the default values. *)
-  val default : unit -> t
+  (** Data with [type t = r record].
+      Also contains getters and setters for fields. *)
+  module type S =
+  sig
+    type r
+    include S with type t = r record
+    val default : t
+    val has : (r,'a) field -> t -> bool
+    val get : (r,'a) field -> t -> 'a
+    val set : (r,'a) field -> 'a -> t -> t
+  end
+
+  (** Create a new, opened record type *)
+  val signature : page:Doc.page -> name:string -> descr:Markdown.text ->
+    unit -> 'a signature
+
+  (** Adds a field to an opened record *)
+  val field : 'r signature ->
+    name:string -> descr:Markdown.text -> ?default:'a -> 'a data ->
+    ('r,'a) field
+
+  (** Adds a optional field to an opened record *)
+  val option : 'r signature ->
+    name:string -> descr:Markdown.text -> 'a data ->
+    ('r,'a option) field
+
+  (** Publish and close an opened record *)
+  val publish : 'a signature -> (module S with type r = 'a)
 
 end
 
diff --git a/src/plugins/server/kernel_main.ml b/src/plugins/server/kernel_main.ml
index 9c18bde3e567481bb4d896806af56e5654d57781..49e11148a95a714eb77d28b4a947e56774c34fca 100644
--- a/src/plugins/server/kernel_main.ml
+++ b/src/plugins/server/kernel_main.ml
@@ -63,6 +63,7 @@ let () =
 module RawSource =
 struct
   type t = Filepath.position
+
   let syntax = Sy.publish ~page ~name:"source"
       ~synopsis:(Sy.record [ "file" , Sy.string ; "line" , Sy.int ])
       ~descr:(Md.plain "Source file positions.")
@@ -114,31 +115,29 @@ module LogKind = Dictionary(RawKind)
 module RawEvent =
 struct
 
-  module R = Record
-      (struct
-        let page = page
-        let name = "log"
-        let descr = Md.plain "Message event record."
-      end)
-
-  let syntax = R.syntax
+  type rlog
 
-  let kind = R.field "kind" ~descr:(Md.plain "Message kind") (module LogKind)
-  let plugin = R.field "plugin" ~descr:(Md.plain "Emitter plugin") (module Jstring)
-  let message = R.field "message" ~descr:(Md.plain "Message text") (module Jstring)
+  let jlog : rlog signature = Record.signature ~page
+      ~name:"log" ~descr:(Md.plain "Message event record.") ()
 
-  let category = R.option "category"
-      ~descr:(Md.plain "Message category (DEBUG or WARNING)")
-      (module Jstring)
+  let kind = Record.field jlog ~name:"kind"
+      ~descr:(Md.plain "Message kind") (module LogKind)
+  let plugin = Record.field jlog ~name:"plugin"
+      ~descr:(Md.plain "Emitter plugin") (module Jstring)
+  let message = Record.field jlog ~name:"message"
+      ~descr:(Md.plain "Message text") (module Jstring)
+  let category = Record.option jlog ~name:"category"
+      ~descr:(Md.plain "Message category (DEBUG or WARNING)") (module Jstring)
+  let source = Record.option jlog ~name:"source"
+      ~descr:(Md.plain "Source file position") (module LogSource)
 
-  let source = R.option "source"
-      ~descr:(Md.plain "Source file position")
-      (module LogSource)
+  module R = (val (Record.publish jlog) : Record.S with type r = rlog)
 
   type t = Log.event
+  let syntax = R.syntax
 
   let to_json evt =
-    R.default () |>
+    R.default |>
     R.set plugin evt.Log.evt_plugin |>
     R.set kind evt.Log.evt_kind |>
     R.set category evt.Log.evt_category |>