diff --git a/src/plugins/server/Makefile.in b/src/plugins/server/Makefile.in
index d5b7cf9e8dab5322982b805a00c69533c751197d..0c9644098263ac6ff133193aad18149ea977694a 100644
--- a/src/plugins/server/Makefile.in
+++ b/src/plugins/server/Makefile.in
@@ -79,7 +79,10 @@ include $(FRAMAC_SHARE)/Makefile.dynamic
 ##############
 
 SERVER_API= \
-	doc.mli syntax.mli data.mli request.mli states.mli
+	doc.mli syntax.mli data.mli request.mli states.mli \
+	kernel_main.mli \
+	kernel_ast.mli \
+	kernel_properties.mli
 
 define Capitalize
 $(shell printf "%s%s" \
diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml
index ccc9b9a131f39e4f9e3643d15134d7aa82aa9c5e..fc2c301f10a7b9767efac7374f0807850a768a66 100644
--- a/src/plugins/server/data.ml
+++ b/src/plugins/server/data.ml
@@ -407,7 +407,7 @@ struct
   }
 
   type 'a tag = string
-  type 'a prefix = string -> 'a tag
+  type 'a prefix = string
 
   let tag_name tg = tg
   let tag_label a = function
@@ -432,8 +432,6 @@ struct
   let syntax (d : 'a dictionary) = d.syntax
 
   let tag (d : 'a dictionary) ~name ?label ~descr ?value () : 'a tag =
-    if d.published then
-      invalid d.name (Printf.sprintf "published enum (%s)" name) ;
     if Hashtbl.mem d.values name then
       invalid d.name (Printf.sprintf "duplicate tag (%s)" name) ;
     let tg = Syntax.{
@@ -448,17 +446,18 @@ struct
       | Some v -> Hashtbl.add d.vindex v name
     end ; name
 
-  let prefix (d : 'a dictionary) ~prefix ?(var="*") ?label ~descr
-      () : string -> 'a tag =
-    if d.published then
-      invalid d.name (Printf.sprintf "published enum (%s:*)" prefix) ;
-    let make = Printf.sprintf "%s:%s" prefix in
+  let instance = Printf.sprintf "%s:%s"
+
+  let prefix (d : 'a dictionary) ~prefix ?(var="*") ?label ~descr () =
     let tg = Syntax.{
-        tag_name = make var ;
+        tag_name = instance prefix var ;
         tag_label = tag_label (prefix ^ ".") label ;
         tag_descr = descr ;
       } in
-    d.tags <- tg :: d.tags ; make
+    d.tags <- tg :: d.tags ; prefix
+
+  let extends d prefix ~name ?label ~descr ?value () =
+    tag d ~name:(instance prefix name) ?label ~descr ?value ()
 
   let to_json name vindex v =
     try `String (Hashtbl.find vindex v)
@@ -484,10 +483,9 @@ struct
       type t = a
       let descr = d.descr
       let syntax =
-        let tags = Syntax.tags ~title:d.title (List.rev d.tags) in
+        let tags () = [Syntax.tags ~title:d.title (List.rev d.tags)] in
         Syntax.publish ~page:d.page ~name:d.name ~descr
-          ~synopsis:(Syntax.string)
-          ~details:[tags] ()
+          ~synopsis:(Syntax.string) ~generated:tags ()
       let of_json = of_json d.name d.values
       let to_json =
         match tag with
diff --git a/src/plugins/server/data.mli b/src/plugins/server/data.mli
index 3297d75cbdae383ca6d3d1ac59698bdba41786ed..192bfc5b11cbfe2b68e74141c2a53bd6a24929c5 100644
--- a/src/plugins/server/data.mli
+++ b/src/plugins/server/data.mli
@@ -187,7 +187,7 @@ sig
 
   type 'a dictionary
   type 'a tag
-  type 'a prefix = string -> 'a tag
+  type 'a prefix
 
   val tag_name : 'a tag -> string
 
@@ -201,7 +201,9 @@ sig
       The provided value, if any, will be used for decoding json tags.
       If would be used also for encoding values to json tags if no [~tag]
       function is provided when publishing the dictionnary.
-      Registered values must be hashable with [Hashtbl.hash] function. *)
+      Registered values must be hashable with [Hashtbl.hash] function.
+
+      You may register a new tag {i after} the dictionary has been published. *)
   val tag : 'a dictionary ->
     name:string ->
     ?label:Markdown.text -> descr:Markdown.text ->
@@ -213,13 +215,26 @@ sig
       To decoding from json is provided to prefix tags.
       Encoding is done by emitting tags with form ['prefix:*'].
       The variable part of the prefix is documented as ['prefix:xxx']
-      when [~var:"xxx"] is provided. *)
+      when [~var:"xxx"] is provided.
+
+      You may register a new prefix-tag {i after} the dictionary has
+      been published. *)
   val prefix : 'a dictionary ->
     prefix:string -> ?var:string ->
     ?label:Markdown.text -> descr:Markdown.text ->
     unit -> 'a prefix
 
-  (** Obtain all the tags from the dictionnary. *)
+  (** Returns the tag for a value associated with the given prefix. *)
+  val instance : 'a prefix -> string -> 'a tag
+
+  (** Publish a new instance in the documentation. *)
+  val extends : 'a dictionary -> 'a prefix ->
+    name:string ->
+    ?label:Markdown.text -> descr:Markdown.text ->
+    ?value:'a ->
+    unit -> 'a tag
+
+  (** Obtain all the tags registered in the dictionnary so far. *)
   val tags : 'a dictionary -> Tag.t list
 
   val page : 'a dictionary -> Doc.page
diff --git a/src/plugins/server/doc.ml b/src/plugins/server/doc.ml
index cab11780aec9205349d18ca0595ccef333d7fcd3..92423076609535541797e43caf7a428fba7018ca 100644
--- a/src/plugins/server/doc.ml
+++ b/src/plugins/server/doc.ml
@@ -31,6 +31,8 @@ module Pages = Map.Make(String)
 
 type chapter = [ `Protocol | `Kernel | `Plugin of string ]
 
+type section = (unit -> Markdown.elements)
+
 type page = {
   path : string ;
   rootdir : string ; (* path to document root *)
@@ -38,7 +40,7 @@ type page = {
   title : string ;
   order : int ;
   intro : Markdown.elements ;
-  mutable sections : Markdown.elements list ;
+  mutable sections : section list ;
 }
 
 let order = ref 0
@@ -82,10 +84,15 @@ let page chapter ~title ~filename =
                  sections=[] } in
     pages := Pages.add path page !pages ; page
 
-let publish ~page ?name ?(index=[]) ~title content sections =
+let static () = []
+
+let publish ~page ?name ?(index=[]) ~title
+    ?(contents=[])
+    ?(generated=static)
+    () =
   let id = match name with Some id -> id | None -> title in
   let href = Section( page.path , id ) in
-  let section = Markdown.section ?name ~title (content @ sections) in
+  let section () = Markdown.section ?name ~title (contents @ generated ()) in
   List.iter (fun entry -> entries := (entry , href) :: !entries) index ;
   page.sections <- section :: page.sections ; href
 
@@ -203,12 +210,16 @@ let pp_one_page ~root ~page ~title body =
   with Sys_error e ->
     Senv.fatal "Could not open file %s for writing: %s" full_path e
 
+let rec build contents = function
+  | [] -> contents
+  | s::sections -> build (s () :: contents) sections
+
 let dump ~root ?(meta=true) () =
   begin
     Pages.iter
       (fun path page ->
          Senv.feedback "[doc] Page: '%s'" path ;
-         let body = Markdown.subsections page.intro (List.rev page.sections) in
+         let body = Markdown.subsections page.intro (build [] page.sections) in
          let title = page.title in
          pp_one_page ~root ~page:path ~title body ;
          if meta then
diff --git a/src/plugins/server/doc.mli b/src/plugins/server/doc.mli
index e1b2c6414d3596de73f9c44b60e5b714486721a7..d3f3d2978bef014c929cf07bd9ffd42f40ba0767 100644
--- a/src/plugins/server/doc.mli
+++ b/src/plugins/server/doc.mli
@@ -56,9 +56,9 @@ val publish :
   ?name:string ->
   ?index:string list ->
   title:string ->
-  Markdown.elements ->
-  Markdown.elements ->
-  Markdown.href
+  ?contents:Markdown.elements ->
+  ?generated:(unit -> Markdown.elements) ->
+  unit -> Markdown.href
 
 (** Dumps all published pages of documentations. Unless [~meta:false],
     also generates METADATA for each page in
diff --git a/src/plugins/server/kernel_properties.ml b/src/plugins/server/kernel_properties.ml
index 46e46b0f2507139bee3c1cd647de33d9436991b1..02c7e613c05e7fb111618322a3e2e3145d41a087 100644
--- a/src/plugins/server/kernel_properties.ml
+++ b/src/plugins/server/kernel_properties.ml
@@ -20,9 +20,7 @@
 (*                                                                        *)
 (**************************************************************************)
 
-module Sy = Syntax
 module Md = Markdown
-module Js = Yojson.Basic.Util
 
 open Data
 open Kernel_main
@@ -62,8 +60,6 @@ struct
   let t_decreases = t_clause "decreases"
   let t_assigns = t_clause "assigns"
   let t_froms = t_kind "froms" "Clause `@assigns … \\from …`"
-  let t_ext = Enum.prefix kinds ~prefix:"ext" ~var:"<clause>"
-      ~descr:(Md.plain "ACSL extension `<clause>`") ()
 
   let t_assert = t_clause "assert"
   let t_loop_invariant = t_loop "invariant"
@@ -71,8 +67,6 @@ struct
   let t_loop_variant = t_loop "variant"
   let t_loop_allocates = t_loop "allocates"
   let t_loop_pragma = t_loop "pragma"
-  let t_loop_ext = Enum.prefix kinds ~prefix:"loop-ext" ~var:"<clause>"
-      ~descr:(Md.plain "ACSL loop extension `loop <clause>`") ()
 
   let t_reachable = t_kind "reachable" "Reachable statement"
   let t_code_contract = t_kind "code-contract" "Statement Contract"
@@ -83,7 +77,14 @@ struct
   let t_axiomatic = t_kind "axiomatic" "Axiomatic definitions"
   let t_axiom = t_kind "axiom" "Logical axiom"
   let t_lemma = t_kind "lemma" "Logical lemma"
-  let t_other = Enum.prefix kinds ~prefix:"prop" ~var:"<prop>"
+
+  let p_ext = Enum.prefix kinds ~prefix:"ext" ~var:"<clause>"
+      ~descr:(Md.plain "ACSL extension `<clause>`") ()
+
+  let p_loop_ext = Enum.prefix kinds ~prefix:"loop-ext" ~var:"<clause>"
+      ~descr:(Md.plain "ACSL loop extension `loop <clause>`") ()
+
+  let p_other = Enum.prefix kinds ~prefix:"prop" ~var:"<prop>"
       ~descr:(Md.plain "Plugin Specific properties") ()
 
   open Property
@@ -100,7 +101,7 @@ struct
         | PKEnsures(_,Returns) -> t_returns
         | PKTerminates -> t_terminates
       end
-    | IPExtended { ie_ext={ ext_name } } -> t_ext ext_name
+    | IPExtended { ie_ext={ ext_name } } -> Enum.instance p_ext ext_name
     | IPAxiomatic _ -> t_axiomatic
     | IPAxiom _ -> t_axiom
     | IPLemma _ -> t_lemma
@@ -117,7 +118,7 @@ struct
         | AAssigns _ -> t_loop_assigns
         | AAllocation _ -> t_loop_allocates
         | APragma _ -> t_loop_pragma
-        | AExtended(_,_,{ext_name}) -> t_loop_ext ext_name
+        | AExtended(_,_,{ext_name}) -> Enum.instance p_loop_ext ext_name
       end
     | IPAllocation _ -> t_allocates
     | IPAssigns _ -> t_assigns
@@ -127,7 +128,7 @@ struct
     | IPPropertyInstance { ii_ip } -> tag ii_ip
     | IPTypeInvariant _ -> t_type_invariant
     | IPGlobalInvariant _ -> t_global_invariant
-    | IPOther { io_name } -> t_other io_name
+    | IPOther { io_name } -> Enum.instance p_other io_name
 
   let data = Enum.publish kinds ~tag ()
   let () = Request.dictionary kinds
@@ -135,6 +136,14 @@ struct
   include (val data : S with type t = Property.t)
 end
 
+let register_propkind ~name ~kind ?label ~descr () =
+  let open PropKind in
+  let prefix = match kind with
+    | `Clause -> p_ext
+    | `Loop -> p_loop_ext
+    | `Other -> p_other
+  in ignore @@ Enum.extends kinds prefix ~name ?label ~descr ()
+
 (* -------------------------------------------------------------------------- *)
 (* --- Property Status                                                    --- *)
 (* -------------------------------------------------------------------------- *)
@@ -247,4 +256,6 @@ let array =
     ~add_remove_hook:Property_status.register_property_remove_hook
     model
 
+let reload () = States.reload array
+
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/kernel_properties.mli b/src/plugins/server/kernel_properties.mli
new file mode 100644
index 0000000000000000000000000000000000000000..9e7b4bf1e70f54b10d8de1340029d320cbfcdcdf
--- /dev/null
+++ b/src/plugins/server/kernel_properties.mli
@@ -0,0 +1,34 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Documentation of ACSL extensions for [propkind] server data. *)
+val register_propkind :
+  name:string ->
+  kind:[`Clause | `Loop | `Other] ->
+  ?label:Markdown.text ->
+  descr:Markdown.text ->
+  unit -> unit
+
+(** Trigger a full reload for the table of property status. *)
+val reload : unit -> unit
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/request.ml b/src/plugins/server/request.ml
index eb32d1ed5ca668532e6a39459daaff739775fe9f..cb0de2eb1945f2321397535d0c48b6e741d5545c 100644
--- a/src/plugins/server/request.ml
+++ b/src/plugins/server/request.ml
@@ -99,10 +99,9 @@ let signal ~page ~name ~descr  ?(details=[]) () =
   check_page page name ;
   let title =  Printf.sprintf "`SIG` %s" name in
   let index = [ Printf.sprintf "%s (`SIGNAL`)" name ] in
-  let description = [ Block [Text descr] ; Block details] in
-  let _ =
-    Doc.publish ~page ~name ~title ~index description []
-  in Main.signal name
+  let contents = [ Block [Text descr] ; Block details] in
+  let _ = Doc.publish ~page ~name ~title ~index ~contents () in
+  Main.signal name
 
 let emit = Main.emit
 let on_signal = Main.on_signal
@@ -343,13 +342,11 @@ let register_sig (type a b) (s : (a,b) signature) (process : rq -> a -> b) =
     Syntax.define (plain "Input") (Syntax.text @@ sy_input s.input) in
   let output =
     Syntax.define (plain "Output") (Syntax.text @@ sy_output s.output) in
-  let description =
+  let contents =
     Block ( Text s.descr :: input :: output :: s.details ) ::
     ( doc_input s.input @ doc_output s.output )
   in
-  let _ =
-    Doc.publish ~page:s.page ~name:s.name ~title ~index description []
-  in
+  let _ = Doc.publish ~page:s.page ~name:s.name ~title ~index ~contents () in
   Main.register s.kind s.name processor ;
   s.defined <- true
 
diff --git a/src/plugins/server/states.ml b/src/plugins/server/states.ml
index 2cbb15085cd3322796a92ea0c8c8708ba8690360..bf7a29ee0de8247afb2de2bf1282dbc66915a1df 100644
--- a/src/plugins/server/states.ml
+++ b/src/plugins/server/states.ml
@@ -50,8 +50,8 @@ let register_value (type a) ~page ~name ~descr ?(details=[])
   let open Markdown in
   let title =  Printf.sprintf "`VALUE` %s" name in
   let index = [ Printf.sprintf "%s (`VALUE`)" name ] in
-  let description = [ Block [Text descr] ; Block details] in
-  let h = Doc.publish ~page ~name ~title ~index description [] in
+  let contents = [ Block [Text descr] ; Block details] in
+  let h = Doc.publish ~page ~name ~title ~index ~contents () in
   let signal = Request.signal ~page ~name:(name ^ ".sig")
       ~descr:(plain "Signal for value " @ href h) () in
   Request.register ~page ~kind:`GET ~name:(name ^ ".get")
@@ -70,8 +70,8 @@ let register_state (type a) ~page ~name ~descr ?(details=[])
   let open Markdown in
   let title =  Printf.sprintf "`STATE` %s" name in
   let index = [ Printf.sprintf "%s (`STATE`)" name ] in
-  let description = [ Block [Text descr] ; Block details] in
-  let h = Doc.publish ~page ~name ~title ~index description [] in
+  let contents = [ Block [Text descr] ; Block details] in
+  let h = Doc.publish ~page ~name ~title ~index ~contents () in
   let signal = Request.signal ~page ~name:(name ^ ".sig")
       ~descr:(plain "Signal for state " @ href h) () in
   Request.register ~page ~kind:`GET ~name:(name ^ ".get")
@@ -259,7 +259,7 @@ let register_array ~page ~name ~descr ?(details=[]) ~key
   let title =  Printf.sprintf "`ARRAY` %s" name in
   let index = [ Printf.sprintf "%s (`ARRAY`)" name ] in
   let columns = !model in
-  let description = [
+  let contents = [
     Block [Text descr] ;
     Syntax.fields ~title:"Columns"
       begin
@@ -271,7 +271,7 @@ let register_array ~page ~name ~descr ?(details=[]) ~key
       end ;
     Block details
   ] in
-  let mref = Doc.publish ~page:page ~name:name ~title ~index description [] in
+  let mref = Doc.publish ~page:page ~name:name ~title ~index ~contents () in
   let signal = Request.signal ~page ~name:(name ^ ".sig")
       ~descr:(plain "Signal for array " @ href mref) () in
   let getter = List.map Syntax.(fun (fd,to_js) -> fd.fd_name , to_js) columns in
diff --git a/src/plugins/server/syntax.ml b/src/plugins/server/syntax.ml
index 61f376f45da2ce54d9a51c14046bab5831cbdcce..06abe8c24a54612a466b8d5212054e6b640f9010 100644
--- a/src/plugins/server/syntax.ml
+++ b/src/plugins/server/syntax.ml
@@ -63,7 +63,7 @@ let protect a =
 let define left right =
   Markdown.(Block_quote [Block[Text ( left @ plain ":=" @ right )]])
 
-let publish ~page ~name ~descr ~synopsis ?(details = []) () =
+let publish ~page ~name ~descr ~synopsis ?(details = []) ?generated () =
   check_name name ;
   check_page page name ;
   let id = Printf.sprintf "data-%s" name in
@@ -72,11 +72,11 @@ let publish ~page ~name ~descr ~synopsis ?(details = []) () =
   let dref = Doc.href page id in
   let dlink = Markdown.href ~text:(Markdown.emph name) dref in
   let data = Markdown.(plain "<" @ dlink @ plain ">") in
-  let content = Markdown.(Block(
+  let contents = Markdown.(Block(
       [ Text descr ; define data synopsis.text ]
     )) :: details in
-  let _href = Doc.publish ~page ~name:id ~title ~index content [] in
-  atom dlink
+  let _href = Doc.publish ~page ~name:id ~title ~index ~contents ?generated ()
+  in atom dlink
 
 (* -------------------------------------------------------------------------- *)
 
diff --git a/src/plugins/server/syntax.mli b/src/plugins/server/syntax.mli
index 1e9233d51e3308ecaf82b9c4c63f1c0f077af23a..4f7ef4525cd795e1d947c13279a6c9523c9ce512 100644
--- a/src/plugins/server/syntax.mli
+++ b/src/plugins/server/syntax.mli
@@ -33,7 +33,10 @@ val text : t -> Markdown.text
     the description block. *)
 val publish :
   page:Doc.page -> name:string -> descr:Markdown.text ->
-  synopsis:t -> ?details:Markdown.elements -> unit -> t
+  synopsis:t ->
+  ?details:Markdown.elements ->
+  ?generated:(unit -> Markdown.elements) ->
+  unit -> t
 
 val unit : t
 val any : t