diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 332575587e4abd3f9be73902219966d25549b287..8ab105a892f39a38343b71583a1747e06ea07ffb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,6 +38,7 @@ test:
   needs:
     - build
   script:
+    - ./gradlew --console=plain --no-daemon test
     - ./gradlew --console=plain --no-daemon allTests
   artifacts:
     when: always
diff --git a/pages/docs/extending.md b/pages/docs/extending.md
index 61cabca224b7834f08cca7d96f3913c7cb9feb37..14fa8c8dbb525900f26c0ccbf9f7b45ac17544e5 100644
--- a/pages/docs/extending.md
+++ b/pages/docs/extending.md
@@ -1,32 +1,24 @@
 # Extending `RagConnect`
 
-To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol):
+To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol).
 
-Within `ragconnect.base/src/main/resources`:
+### Within `ragconnect.base/src/main/resources`
 
 {% raw %}
-- Add a new handler `ABCHandler`, if appropriate, similar to the existing handlers
-    - If further methods are needed for handler initialization, add a new template `abc.mustache` containing those procedures. Add `{{#usesABC}}{{> abc}}{{/usesABC}}` at the top of `ragconnect.mustache` to use this template
-- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statement defining the logic to happen for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`.
+- Add a new handler `ABCHandler.jadd`, similar to the existing handlers. 
+- In `handler.mustache`, add further methods if needed for handler usage in the application code (similar to `{{rootNodeName}}.{{SetupWaitUntilReadyMethodName}}` for `mqtt`)
+- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statements defining the logic to happen upon connect and disconnect for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`.
 {% endraw %}
 
-Within `ragconnect.base/src/main/jastadd`:
+### Within `ragconnect.base/src/main/jastadd`
 
-- In `backend/Configuration`:
-    - Add a new static boolean flag `usesABC` to indicate whether the protocol is used
-- In `backend/Generation`:
-    - Add new attributes for type `MRagConnect` for handler attribute and handler field, if needed
-    - Add attributes for newly introduced references in changed mustache templates, if any
-    - Add a newly constructed handler within the definition of `RagConnect.toMustache` with the needed fields (class name, construction snippet, handler attribute, handler field, the boolean flag you just added to Configuration)
-- In `backend/MustacheNodesToYAML`:
-    - Add key-value-pair for `usesABC` (and handler, if any)
-    - Add key-value-pairs for newly introduced referemces in changed mustache templates, if any
+In `Handlers.jrag`: Add a new attribute `RagConnect.abcHandler()` returning the resolved handler
 
-In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`:
+### Within `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler`
 
+In `Compiler.java`:
 - Add a new choice for `--protocols` similar to the existing ones
-- Set the flag `usesABC` if the choice is given.
-- Add code to add the handler to the list `handlers` if the choice is given, i.e., if `ASTNode.usesABC`
+- Add a newly constructed handler in `setConfiguration`  with the needed fields (definition file name within `resources` directory, commonly `ABCHandler.jadd`; class name of the handler; unique name for the protocol; whether the handler is used, i.e., if it was given in `--protocols`)
 
 Furthermore, new test cases are appreciated, see [below](#writing-tests).
 
@@ -58,7 +50,7 @@ All tests are required to run both locally, and within the CI.
 
 ### build.gradle
 
-Use the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task:
+Using the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task:
 
 ```groovy
 task compileTreeAllowedTokens(type: RagConnectTest) {
diff --git a/pages/docs/inner-workings.md b/pages/docs/inner-workings.md
index 6d8b41e13847f2ae337f08c65068cbef6085e065..a36a62654511e88a4063a52d356f69fc9125a27f 100644
--- a/pages/docs/inner-workings.md
+++ b/pages/docs/inner-workings.md
@@ -4,12 +4,18 @@ Please see [API documentation](ragdoc/index.html) for more details.
 
 ![ros2rag-process](img/ros2rag-process.png)
 
-`RagConnect` uses the [relast-preprocessor](https://git-st.inf.tu-dresden.de/jastadd/relast-preprocessor) to parse `.relast` grammar files. This results in an ASTNode of type `Program`.
-It further uses a dedicated parser for `.connect` files containing endpoint-, mapping-, and dependency-definitions. This results in an ASTNode of type `RagConnect`.
+`RagConnect` uses the [relast-preprocessor](https://git-st.inf.tu-dresden.de/jastadd/relast-preprocessor) to parse `.relast` grammar files.
+This results in an ASTNode of type `Program`.
+It further uses a dedicated parser for `.connect` files containing endpoint-, mapping-, and dependency-definitions.
+This results in an ASTNode of type `RagConnect`.
 The goal is to generate an aspect file containing setters and getters of tokens referred to by endpoint-definitions
-We use [mustache](https://mustache.github.io/) (currently its [Java version](https://github.com/spullara/mustache.java)) making use of partials resulting in a set of `.mustache` files located in `ragconnect.base/src/main/resources`.
-The generation process uses an intermediate NTA of type `MRagConnect` defined in `MustacheNodes.relast` to separate this generation concern from the content of the DSL
+We use [mustache](https://mustache.github.io/) (currently its [Java version](https://github.com/spullara/mustache.java)) making use of partials to separate concerns.
+The `.mustache` files are located in `ragconnect.base/src/main/resources`.
+The generation process uses intermediates NTAs (whose types are defined in `Intermediate.relast`) to ease the definition of two main generation "problems".
+Those problems are differentiation on both kinds of an endpoint (send/receive and type/token/list), and attributes depending on position in n-ary relations.
 
-There are aspect files for `Navigation` (mainly isX/asX attributes), `Analysis` (lookup attributes), `Printing`, `backend/Mappings` (default mappings)
-One of the main aspects is `backend/Generation` containing attributes to construct the `MRagConnect` NTA and all necessary attributes used within the mustache templates
-The other main aspect (which is currently not really used) is `backend/MustacheNodesToYAML.jrag` containing the transformation from a `MRagConnect` subtree to a `Document` subtree defined by `YAML.relast`. This is used to generate a YAML file containing the data used by mustache. It can be used by the default mustache implementation together with the templates.
+There are aspect files for `Navigation` (mainly isX/asX attributes), `Analysis` (static analysis attributes), `Printing`, `Mappings` (default mappings).
+One of the main aspects is `Intermediate` containing all attributes consumed by `mustache` and other attributes the former depend on.
+The other main aspect (which is currently not really used) is `IntermediateToYAML` containing the transformation from a `RagConnect` subtree to a `Document` subtree defined by `Mustache.relast` (located in `relast-preprocessor` submodule).
+This is used to generate a YAML file containing the data used by mustache.
+It can be used by the default mustache implementation together with the templates.
diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle
index c1ee98d52184b358a411a17a359f75a4fa601e8e..f039c8bc129e8f2ca58ed389c1cc5bf5cd8e92e9 100644
--- a/ragconnect.base/build.gradle
+++ b/ragconnect.base/build.gradle
@@ -24,6 +24,9 @@ repositories {
     mavenCentral()
     jcenter()
 }
+tasks.compileJava {
+    options.release.set(11)
+}
 
 dependencies {
     implementation project(':relast-preprocessor')
@@ -33,6 +36,12 @@ dependencies {
     api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 }
 
+java {
+    toolchain {
+        languageVersion.set(JavaLanguageVersion.of(11))
+    }
+}
+
 def versionFile = 'src/main/resources/ragConnectVersion.properties'
 def props = new Properties()
 
@@ -77,7 +86,7 @@ jar {
 
 File preprocessorGrammar = file('../relast-preprocessor/src/main/jastadd/RelAst.relast')
 File ragConnectGrammar = file('./src/main/jastadd/RagConnect.relast')
-File intermediateGrammar = file('./src/main/jastadd/intermediate/MustacheNodes.relast')
+File intermediateGrammar = file('./src/main/jastadd/Intermediate.relast')
 File mustacheGrammar = file('../relast-preprocessor/src/main/jastadd/mustache/Mustache.relast')
 task relast(type: JavaExec) {
     group = 'Build'
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index ac891b0e819b1e57e135afced64495a76396db5e..66e6a5a81aff0823c07e4ec3079551c0740b4963 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -1,59 +1,29 @@
 aspect Analysis {
-  // --- lookupTokenEndpointDefinition ---
-  inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
-  eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
-  syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
-    java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
-      if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) {
-        result.add(def.asTokenEndpointDefinition());
-      }
-    }
-    return result;
-  }
-
-  // --- lookupTypeEndpointDefinition ---
-  inh java.util.List<TypeEndpointDefinition> TypeEndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
-  eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
-  syn java.util.List<TypeEndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
-    java.util.List<TypeEndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
-      if (def.isTypeEndpointDefinition() && def.asTypeEndpointDefinition().getType().equals(type)) {
-        result.add(def.asTypeEndpointDefinition());
-      }
-    }
-    return result;
-  }
-
-  // --- lookupDependencyDefinition ---
-  inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
-  eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
-    for (DependencyDefinition def : allDependencyDefinitionList()) {
-      if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) {
-        return def;
-      }
-    }
-    return null;
-  }
-
   // --- isAlreadyDefined ---
-  syn boolean TokenEndpointDefinition.isAlreadyDefined() {
+  syn boolean EndpointDefinition.isAlreadyDefined() = getEndpointTarget().isAlreadyDefined();
+  syn boolean EndpointTarget.isAlreadyDefined();
+  eq TokenEndpointTarget.isAlreadyDefined() {
     return lookupTokenEndpointDefinitions(getToken()).stream()
-      .filter(this::matchesType)
-      .count() > 1;
+        .filter(containingEndpointDefinition()::matchesType)
+        .count() > 1;
+  }
+  eq TypeEndpointTarget.isAlreadyDefined() {
+    return lookupTypeEndpointDefinitions(getType()).stream()
+        .filter(containingEndpointDefinition()::matchesType)
+        .count() > 1;
   }
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
 
   // --- matchesType ---
-  syn boolean TokenEndpointDefinition.matchesType(TokenEndpointDefinition other);
-  eq ReceiveTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isReceiveTokenEndpointDefinition();
-  eq SendTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isSendTokenEndpointDefinition();
+  syn boolean EndpointDefinition.matchesType(EndpointDefinition other) = this.getSend() == other.getSend();
 
   // --- assignableTo ---
   syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
   eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target);
   eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) {
-    if (!target.getName().endsWith("[]")) { return false; }
+    if (!target.getName().endsWith("[]")) {
+      return false;
+    }
     return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", "")));
   }
   syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) {
@@ -61,31 +31,39 @@ aspect Analysis {
     return target.primitivePrettyPrint().equals(this.primitivePrettyPrint());
   }
   syn String JavaTypeUse.primitivePrettyPrint() {
-    switch(getName()) {
+    switch (getName()) {
       case "boolean":
-      case "Boolean": return "boolean";
+      case "Boolean":
+        return "boolean";
       case "int":
-      case "Integer": return "int";
+      case "Integer":
+        return "int";
       case "short":
-      case "Short": return "short";
+      case "Short":
+        return "short";
       case "long":
-      case "Long": return "long";
+      case "Long":
+        return "long";
       case "float":
-      case "Float": return "float";
+      case "Float":
+        return "float";
       case "double":
-      case "Double": return "double";
+      case "Double":
+        return "double";
       case "char":
-      case "Character": return "char";
-      default: return getName();
+      case "Character":
+        return "char";
+      default:
+        return getName();
     }
   }
 
-  // --- shouldSendValue ---
-  syn boolean TokenEndpointDefinition.shouldSendValue() = isSendTokenEndpointDefinition() && !getToken().getNTA();
-  syn boolean TypeEndpointDefinition.shouldSendValue() = isSendTypeEndpointDefinition() && !getType().getNTA();
+  syn boolean EndpointTarget.entityIsNormalAttribute();
+  eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA();
+  eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA();
 
   // --- needProxyToken ---
-  syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointDefinitionList().stream().anyMatch(TokenEndpointDefinition::shouldSendValue);
+  syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointTargetList().stream().map(EndpointTarget::containingEndpointDefinition).anyMatch(EndpointDefinition::shouldSendValue);
 
   // --- effectiveUsedAt ---
   coll Set<EndpointDefinition> MappingDefinition.effectiveUsedAt()
@@ -95,10 +73,4 @@ aspect Analysis {
     to MappingDefinition.effectiveUsedAt()
     for each effectiveMappings();
 
-  // --- typeIsList ---
-  syn boolean EndpointDefinition.typeIsList() = false;
-  eq TypeEndpointDefinition.typeIsList() {
-    return getType().isListComponent();
-  }
-
 }
diff --git a/ragconnect.base/src/main/jastadd/Configuration.jadd b/ragconnect.base/src/main/jastadd/Configuration.jadd
index 8fd0e64b14bbd9a05c7cbf9b7de59ed0cd216d78..9a4645b515e3d690ae4c4b2cdecaac26da516a88 100644
--- a/ragconnect.base/src/main/jastadd/Configuration.jadd
+++ b/ragconnect.base/src/main/jastadd/Configuration.jadd
@@ -1,11 +1,3 @@
-aspect Configuration {
-  public static boolean ASTNode.loggingEnabledForReads = false;
-  public static boolean ASTNode.loggingEnabledForWrites = false;
-  public static boolean ASTNode.loggingEnabledForIncremental = false;
-  public static TypeDecl ASTNode.rootNode;
-  public static String ASTNode.JastAddList = "List";
-  public static boolean ASTNode.usesMqtt;
-  public static boolean ASTNode.usesRest;
-  public static boolean ASTNode.incrementalOptionActive;
-  public static boolean ASTNode.experimentalJastAdd329;
+aspect ConfigurationShortcuts {
+  syn TypeDecl RagConnect.rootNode() = getConfiguration().getRootNode();
 }
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index 770e506cc5c0145a0ba9979d57a1086a399ed39e..61f966507a1d793ad711bb0e3dde085ee0b46ed6 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -3,38 +3,37 @@ aspect Errors {
     [new TreeSet<ErrorMessage>()]
     root RagConnect;
 
-    ReceiveTokenEndpointDefinition contributes error("Receive definition already defined for " + getToken().getName())
-      when isAlreadyDefined()
-      to RagConnect.errors();
+  EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName())
+    when isAlreadyDefined()
+    to RagConnect.errors();
 
-    ReceiveTokenEndpointDefinition contributes error("Receiving target token must not be an NTA token!")
-      when getToken().getNTA()
-      to RagConnect.errors();
+  EndpointDefinition contributes error("Receiving target token must not be an NTA token!")
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA()
+    to RagConnect.errors();
 
-    // if first mapping is null, then suitableDefaultMapping() == null
-    ReceiveTokenEndpointDefinition contributes error("No suitable default mapping found for type " +
+  // if first mapping is null, then suitableDefaultMapping() == null
+  EndpointDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
-        ? getToken().effectiveJavaTypeUse().prettyPrint()
-        : getMappingList().get(0).getFromType().prettyPrint()))
-      when effectiveMappings().get(0) == null
-      to RagConnect.errors();
-
-    ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!")
-      when !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
-          getToken().effectiveJavaTypeUse())
-      to RagConnect.errors();
-
-    SendTokenEndpointDefinition contributes error("Send definition already defined for " + getToken().getName())
-      when isAlreadyDefined()
-      to RagConnect.errors();
-
-    DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
-      when isAlreadyDefined()
-      to RagConnect.errors();
-
-    DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source")
-      when isAlreadyDefinedAsList()
-      to RagConnect.errors();
+          ? token().effectiveJavaTypeUse().prettyPrint()
+          : getMappingList().get(0).getFromType().prettyPrint()))
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null
+    to RagConnect.errors();
+
+  EndpointDefinition contributes error("to-type of last mapping (" +
+      effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() +
+      ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!")
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() &&
+      !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
+          token().effectiveJavaTypeUse())
+    to RagConnect.errors();
+
+  DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
+    when isAlreadyDefined()
+    to RagConnect.errors();
+
+  DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source")
+    when isAlreadyDefinedAsList()
+    to RagConnect.errors();
 }
 
 aspect ErrorHelpers {
@@ -46,7 +45,6 @@ aspect ErrorHelpers {
     }
     return false;
   }
-  syn String TokenComponent.parentTypeDeclAndName() = containingTypeDecl().getName() + "." + getName();
 }
 
 aspect ErrorMessage {
@@ -68,12 +66,15 @@ aspect ErrorMessage {
     public ASTNode getNode() {
       return node;
     }
+
     public int getLine() {
       return line;
     }
+
     public int getCol() {
       return col;
     }
+
     public String getMessage() {
       return message;
     }
@@ -94,7 +95,7 @@ aspect ErrorMessage {
         return n;
       }
 
-      n = col-err.col;
+      n = col - err.col;
       if (n != 0) {
         return n;
       }
diff --git a/ragconnect.base/src/main/jastadd/Handlers.jrag b/ragconnect.base/src/main/jastadd/Handlers.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..57c7ccda67ae38524dc46de4f1db192cc345cb91
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Handlers.jrag
@@ -0,0 +1,14 @@
+aspect RagConnectHandlers {
+  syn Handler RagConnect.mqttHandler() = resolveHandlerByName("mqtt");
+  syn Handler RagConnect.restHandler() = resolveHandlerByName("rest");
+
+  private Handler RagConnect.resolveHandlerByName(String uniqueName) {
+    for (Handler handler : getHandlerList()) {
+      if (uniqueName.equals(handler.getUniqueName())) {
+        return handler;
+      }
+    }
+    System.err.println("Could not find handler with name '" + uniqueName + "'");
+    return null;
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..3729453a4123d34514ad3475b439139bd7ef7136
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -0,0 +1,449 @@
+/*
+Design considerations
+- InnerMappingDefinition needed for iteration attribute (first, last) - not possible with list-relation
+- no complete intermediate structure, but instead single nodes where applicable/needed
+*/
+aspect SharedMustache {
+  // === RagConnect ===
+  syn boolean RagConnect.configIncrementalOptionActive() = getConfiguration().getIncrementalOptionActive();
+
+  syn boolean RagConnect.configLoggingEnabledForIncremental() = getConfiguration().getLoggingEnabledForIncremental();
+
+  syn boolean RagConnect.configExperimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329();
+
+  syn String RagConnect.rootNodeName() = getConfiguration().getRootNode().getName();
+
+  // === EndpointDefinition ===
+  syn String EndpointDefinition.lastResult() = lastDefinition().outputVarName();
+}
+
+aspect MustacheDependencyDefinition {
+  // === DependencyDefinition ===
+  syn String DependencyDefinition.dependencyMethodName() = "add" + capitalize(getID());
+
+  syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID();
+
+  syn String DependencyDefinition.sourceParentTypeName() = getSource().containingTypeDecl().getName();
+
+  syn String DependencyDefinition.targetParentTypeName() = getTarget().containingTypeDecl().getName();
+}
+
+aspect MustacheHandler {
+  // === RagConnect ===
+  syn String RagConnect.closeMethodName() = "ragconnectCloseConnections";
+
+  syn boolean RagConnect.hasRootTypeComponents() = !rootTypeComponents().isEmpty();
+
+  syn List<TypeComponent> RagConnect.rootTypeComponents() {
+    List<TypeComponent> result = new ArrayList<>();
+    for (Component child : rootNode().getComponentList()) {
+      if (child.isTypeComponent()) {
+        result.add(child.asTypeComponent());
+      }
+    }
+    return result;
+  }
+
+  // === Handler ===
+  syn String Handler.attributeName() = "_" + getUniqueName() + "Handler";
+
+  syn String Handler.constructionSnippet() = "new " + getClassName() + "(\"Handler for " + ragconnect().rootNodeName() + ".\" + this.hashCode())";
+
+  syn String Handler.fieldName() = "_" + getUniqueName() + "Handler";
+
+  syn String Handler.setupWaitUntilReadyMethodName() = "ragconnectSetup" + capitalize(getUniqueName()) + "WaitUntilReady";
+}
+
+aspect MustacheHandleUri { /* empty */ }
+
+aspect MustacheListAspect {
+  // === RagConnect ===
+  syn String RagConnect.configJastAddList() = getConfiguration().getJastAddList();
+
+  syn boolean RagConnect.hasTreeListEndpoints() {
+    for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
+      if (endpointDef.typeIsList()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  syn Set<TypeDecl> RagConnect.typesForReceivingListEndpoints() {
+    Set<TypeDecl> result = new HashSet<>();
+    for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
+      if (endpointDef.typeIsList() && !endpointDef.getSend()) {
+        result.add(endpointDef.type().getTypeDecl());
+      }
+    }
+    return result;
+  }
+}
+
+aspect MustacheMappingApplicationAndDefinition {
+  // === EndpointDefinition ===
+  syn String EndpointDefinition.condition() {
+    // TODO [OLD] probably, this has to be structured in a better way
+    if (lastDefinition().getMappingDefinition().getToType().isArray()) {
+      return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
+    }
+    if (getEndpointTarget().isTokenEndpointTarget() && token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) {
+      return preemptiveExpectedValue() + " == " + lastResult();
+    }
+    if (!getSend() && getEndpointTarget().isTypeEndpointTarget() && getWithAdd()) {
+      // only check if received list is not null
+      return lastResult() + " == null";
+    }
+    if (getEndpointTarget().isTypeEndpointTarget() && type().isOptComponent()) {
+      // use "hasX()" instead of "getX() != null" for optionals
+      return "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
+    }
+    if (lastDefinition().getMappingDefinition().getToType().isPrimitiveType() || lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) {
+      return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
+    }
+    return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
+  }
+
+  syn JastAddList<MInnerMappingDefinition> EndpointDefinition.innerMappingDefinitions() = toMustache().getInnerMappingDefinitionList();
+
+  syn String EndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
+
+  syn String EndpointDefinition.preemptiveReturn() = toMustache().preemptiveReturn();
+
+  // === (MInner)MappingDefinition ===
+  syn String MappingDefinition.fromType() = getFromType().prettyPrint();
+
+  inh boolean MInnerMappingDefinition.isLast();
+  eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() {
+    return i == getNumInnerMappingDefinition() - 1;
+  }
+
+  inh String MInnerMappingDefinition.inputVarName();
+  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() {
+    return i == 0
+        ? firstInputVarName()
+        : getInnerMappingDefinition(i - 1).outputVarName();
+  }
+
+  syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName();
+  syn String MappingDefinition.methodName() = "_apply_" + getID();
+
+  syn String MInnerMappingDefinition.outputVarName() = "result" + methodName();  // we do not need "_" in between here, because methodName begins with one
+
+  syn String MInnerMappingDefinition.toType() = getMappingDefinition().toType();
+  syn String MappingDefinition.toType() = getToType().prettyPrint();
+
+  // === attributes needed for computing above ones ===
+  syn String EndpointDefinition.preemptiveExpectedValue() = toMustache().preemptiveExpectedValue();
+  syn String EndpointDefinition.firstInputVarName() = toMustache().firstInputVarName();
+
+  syn String MEndpointDefinition.preemptiveExpectedValue();
+  syn String MEndpointDefinition.preemptiveReturn();
+  syn String MEndpointDefinition.firstInputVarName();
+
+  eq MTokenReceiveDefinition.firstInputVarName() = "message";
+  eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethodName() + "()";
+  eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
+
+  eq MTokenSendDefinition.firstInputVarName() = getterMethodName() + "()";
+  eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTokenSendDefinition.preemptiveReturn() = "return false;";
+
+  eq MTypeReceiveDefinition.firstInputVarName() = "message";
+  eq MTypeReceiveDefinition.preemptiveExpectedValue() = getterMethodName() + "()";
+  eq MTypeReceiveDefinition.preemptiveReturn() = "return;";
+
+  eq MTypeSendDefinition.firstInputVarName() = getterMethodName() + "()";
+  eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTypeSendDefinition.preemptiveReturn() = "return false;";
+
+  syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName();
+
+  syn String MEndpointDefinition.getterMethodName() = getEndpointDefinition().getterMethodName();
+}
+
+aspect MustacheRagConnect {
+  // === RagConnect ===
+  syn List<DependencyDefinition> RagConnect.allDependencyDefinitionList() {
+    List<DependencyDefinition> result = new ArrayList<>();
+    for (ConnectSpecification spec : getConnectSpecificationFileList()) {
+      spec.getDependencyDefinitionList().forEach(result::add);
+    }
+    return result;
+  }
+
+  syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() {
+    List<EndpointDefinition> result = new ArrayList<>();
+    for (ConnectSpecification spec : getConnectSpecificationFileList()) {
+      spec.getEndpointDefinitionList().forEach(result::add);
+    }
+    return result;
+  }
+
+  // > allMappingDefinitions in Mappings.jrag
+
+  syn List<TokenComponent> RagConnect.tokenComponentsThatNeedProxy() {
+    List<TokenComponent> result = new ArrayList<>();
+    for (TokenComponent token : getProgram().allTokenComponents()) {
+      if (token.needProxyToken()) {
+        result.add(token);
+      }
+    }
+    return result;
+  }
+
+  // === MappingDefinition ===
+  syn boolean MappingDefinition.isUsed() = !effectiveUsedAt().isEmpty();
+}
+
+aspect MustacheReceiveAndSendAndHandleUri {
+  // === EndpointDefinition ===
+  syn String EndpointDefinition.connectMethodName() = "connect" + entityName();
+
+  syn String EndpointDefinition.connectParameterName() = "uriString";
+
+  syn String EndpointDefinition.disconnectMethodName() {
+    // if both (send and receive) are defined for an endpoint, ensure methods with different names
+    String extra;
+    if (getEndpointTarget().isTokenEndpointTarget()) {
+      extra = lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
+    } else if (getEndpointTarget().isTypeEndpointTarget()) {
+      extra = lookupTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
+    } else {
+      extra = "";
+    }
+    return "disconnect" + extra + entityName();
+  }
+
+  syn String EndpointDefinition.entityName() = getEndpointTarget().entityName();
+
+  syn String EndpointDefinition.getterMethodName() = getEndpointTarget().getterMethodName();
+
+  syn String EndpointDefinition.parentTypeName() = getEndpointTarget().parentTypeName();
+
+  // === attributes needed for computing above ones ===
+  syn String EndpointTarget.getterMethodName();
+  syn String EndpointTarget.parentTypeName();
+  syn String EndpointTarget.entityName();
+
+  eq TokenEndpointTarget.getterMethodName() = "get" + getToken().getName();
+  eq TokenEndpointTarget.parentTypeName() = getToken().containingTypeDecl().getName();
+  eq TokenEndpointTarget.entityName() = getToken().getName();
+
+  eq TypeEndpointTarget.getterMethodName() = "get" + getType().getName() + (typeIsList() ? "List" : "");
+  eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName();
+  eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "");
+
+}
+
+aspect MustacheReceiveDefinition {
+  // === RagConnect ===
+  syn boolean RagConnect.configLoggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads();
+
+  // === EndpointDefinition ===
+  syn boolean EndpointDefinition.hasTypeEndpointTarget() = getEndpointTarget().isTypeEndpointTarget();
+
+  syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList";
+
+  syn String EndpointDefinition.internalConnectMethodName() = "_internal_" + connectMethodName();
+
+  syn String EndpointDefinition.resolveInListMethodName() = "_ragconnect_resolve" + entityName() + "InList";
+
+  syn boolean EndpointDefinition.typeIsList() = getEndpointTarget().typeIsList();
+
+  // === attributes needed for computing above ones ===
+  syn boolean EndpointTarget.typeIsList() = false;
+  eq TypeEndpointTarget.typeIsList() {
+    return getType().isListComponent();
+  }
+
+  syn String EndpointDefinition.uniqueSuffix() = getSend() ? "Send" : "Receive";
+}
+
+aspect MustacheSendDefinition {
+  // === RagConnect ===
+  syn boolean RagConnect.configLoggingEnabledForWrites() = getConfiguration().getLoggingEnabledForWrites();
+
+  // === EndpointDefinition ===
+  syn String EndpointDefinition.lastValue() = senderName() + ".lastValue";
+
+  syn String EndpointDefinition.senderName() = getEndpointTarget().senderName();
+
+  syn boolean EndpointDefinition.shouldSendValue() = getSend() && getEndpointTarget().entityIsNormalAttribute();
+
+  syn String EndpointDefinition.tokenResetMethodName() = getterMethodName() + "_reset";
+
+  syn String EndpointDefinition.updateMethodName() = toMustache().updateMethodName();
+
+  syn String EndpointDefinition.writeMethodName() = toMustache().writeMethodName();
+
+  // === attributes needed for computing above ones ===
+  syn String EndpointTarget.senderName();
+  eq TokenEndpointTarget.senderName() = "_sender_" + getToken().getName();
+  eq TypeEndpointTarget.senderName() = "_sender_" + getType().getName();
+
+  syn String MEndpointDefinition.updateMethodName();
+  syn String MEndpointDefinition.writeMethodName();
+
+  // MTokenReceiveDefinition
+  eq MTokenReceiveDefinition.updateMethodName() = null;
+  eq MTokenReceiveDefinition.writeMethodName() = null;
+
+  // MTokenSendDefinition
+  eq MTokenSendDefinition.updateMethodName() = "_update_" + tokenName();
+  eq MTokenSendDefinition.writeMethodName() = "_writeLastValue_" + tokenName();
+
+  // MTypeReceiveDefinition
+  eq MTypeReceiveDefinition.updateMethodName() = null;
+  eq MTypeReceiveDefinition.writeMethodName() = null;
+
+  // MTypeSendDefinition
+  eq MTypeSendDefinition.updateMethodName() = "_update_" + typeName();
+  eq MTypeSendDefinition.writeMethodName() = "_writeLastValue_" + typeName();
+
+  syn String EndpointDefinition.tokenName() = token().getName();
+  syn String MEndpointDefinition.tokenName() = getEndpointDefinition().tokenName();
+
+  syn String EndpointDefinition.typeName() = type().getName();
+  syn String MEndpointDefinition.typeName() = getEndpointDefinition().typeName();
+}
+
+aspect MustacheTokenComponent {
+  // === TokenComponent ===
+  syn String TokenComponent.internalName() = needProxyToken() ? "_internal_" + getName() : getName();
+
+  syn String TokenComponent.javaType() = effectiveJavaTypeUse().prettyPrint();
+
+  syn EndpointDefinition TokenComponent.normalTokenSendDef() {
+    for (EndpointTarget target : getTokenEndpointTargetList()) {
+      if (target.isTokenEndpointTarget() && target.containingEndpointDefinition().shouldSendValue()) {
+        return target.containingEndpointDefinition();
+      }
+    }
+    return null;
+  }
+
+  syn String TokenComponent.parentTypeName() = containingTypeDecl().getName();
+
+  // > see MustacheDependencyDefinition for internalRelationPrefix, targetParentTypeName
+
+  // === DependencyDefinition ===
+  syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() {
+    // resolve definition in here, as we do not need resolveMethod in any other place (yet)
+    for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) {
+      if (!endpointDefinition.getSend()) {
+        continue;
+      }
+      EndpointTarget endpointTarget = endpointDefinition.getEndpointTarget();
+      if (endpointTarget.isTokenEndpointTarget() &&
+          endpointTarget.asTokenEndpointTarget().getToken().equals(this.getTarget())) {
+        return endpointDefinition;
+      }
+      if (endpointTarget.isTypeEndpointTarget() &&
+          endpointTarget.asTypeEndpointTarget().getType().equals(this.getTarget())) {
+        return endpointDefinition;
+      }
+    }
+    return null;
+  }
+
+  // > see MustacheSend for updateMethodName, writeMethodName
+}
+
+aspect AttributesForMustache {
+  syn String MEndpointDefinition.lastValue() = getEndpointDefinition().lastValue();
+
+  // token and type are potentially dangerous because asXEndpointTarget can return null
+  syn TokenComponent EndpointDefinition.token() = getEndpointTarget().asTokenEndpointTarget().getToken();
+  syn TypeComponent EndpointDefinition.type() = getEndpointTarget().asTypeEndpointTarget().getType();
+
+  syn MInnerMappingDefinition EndpointDefinition.lastDefinition() = toMustache().lastDefinition();
+  syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
+
+  syn nta MEndpointDefinition EndpointDefinition.toMustache() {
+    final MEndpointDefinition result;
+    if (getEndpointTarget().isTokenEndpointTarget()) {
+      if (getSend()) {
+        result = new MTokenSendDefinition();
+      } else {
+        result = new MTokenReceiveDefinition();
+      }
+    } else if (getEndpointTarget().isTypeEndpointTarget()) {
+      if (getSend()) {
+        result = new MTypeSendDefinition();
+      } else {
+        result = new MTypeReceiveDefinition();
+      }
+    } else {
+      throw new RuntimeException("Unknown endpoint target type: " + getEndpointTarget());
+    }
+    result.setEndpointDefinition(this);
+    for (MappingDefinition def : effectiveMappings()) {
+      MInnerMappingDefinition inner = new MInnerMappingDefinition();
+      inner.setMappingDefinition(def);
+      result.addInnerMappingDefinition(inner);
+    }
+    return result;
+  }
+}
+
+aspect GrammarGeneration {
+  syn java.util.List<Relation> RagConnect.additionalRelations() {
+    java.util.List<Relation> result = new java.util.ArrayList<>();
+    for (DependencyDefinition dd : allDependencyDefinitionList()) {
+      result.add(dd.getRelationToCreate());
+    }
+    return result;
+  }
+
+  syn nta Relation DependencyDefinition.getRelationToCreate() {
+    BidirectionalRelation result = new BidirectionalRelation();
+    NavigableRole left = new ListRole(internalRelationPrefix() + "Source");
+    left.setType(getTarget().containingTypeDecl());
+    NavigableRole right = new ListRole(internalRelationPrefix() + "Target");
+    right.setType(getSource().containingTypeDecl());
+    result.setLeft(left);
+    result.setRight(right);
+    result.addComment(new WhitespaceComment("\n"));
+    return result;
+  }
+
+  syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() {
+    java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>();
+    for (EndpointDefinition def : allEndpointDefinitionList()) {
+      if (def.getTokenToCreate() != null) {
+        result.put(def.type().getTypeDecl(), def.getTokenToCreate());
+      }
+    }
+    return result;
+  }
+
+  syn TokenComponent EndpointDefinition.getTokenToCreate() {
+    if (typeIsList() && getIndexBasedListAccess()) {
+      TokenComponent result = new TokenComponent();
+      result.setName(idTokenName());
+      result.setNTA(false);
+      result.setJavaTypeUse(new SimpleJavaTypeUse("String"));
+      return result;
+    } else {
+      return null;
+    }
+  }
+}
+
+aspect GrammarExtension {
+  refine BackendAbstractGrammar public void TokenComponent.generateAbstractGrammar(StringBuilder b) {
+    if (getNTA()) {
+      b.append("/");
+    }
+    b.append("<");
+    if (!getName().equals("")) {
+      b.append(internalName()).append(":");
+    }
+    effectiveJavaTypeUse().generateAbstractGrammar(b);
+    b.append(">");
+    if (getNTA()) {
+      b.append("/");
+    }
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.relast b/ragconnect.base/src/main/jastadd/Intermediate.relast
new file mode 100644
index 0000000000000000000000000000000000000000..d7afd2ea942558f6999c8fa64c91bfdc480d659c
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Intermediate.relast
@@ -0,0 +1,12 @@
+abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
+rel MEndpointDefinition.EndpointDefinition -> EndpointDefinition;
+
+abstract MTokenEndpointDefinition : MEndpointDefinition;
+MTokenReceiveDefinition : MTokenEndpointDefinition;
+MTokenSendDefinition : MTokenEndpointDefinition;
+abstract MTypeEndpointDefinition : MEndpointDefinition;
+MTypeReceiveDefinition : MTypeEndpointDefinition;
+MTypeSendDefinition : MTypeEndpointDefinition;
+
+MInnerMappingDefinition;
+rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
diff --git a/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..67ccf2738e32f3f91f2fbac3eec8349d348228d6
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
@@ -0,0 +1,206 @@
+aspect IntermediateToYAML {
+  syn Document RagConnect.toYAML() {
+    Document doc = new Document();
+    MappingElement root = new MappingElement();
+    // grammar children
+    ListElement handlersElement = new ListElement();
+    for (Handler handler : getHandlerList()) {
+      handlersElement.add(handler.toYAML());
+    }
+    root.put("Handlers" , handlersElement);
+
+    // shared
+    root.put("configIncrementalOptionActive" , configIncrementalOptionActive());
+    root.put("configLoggingEnabledForIncremental" , configLoggingEnabledForIncremental());
+    root.put("configExperimentalJastAdd329" , configExperimentalJastAdd329());
+    root.put("rootNodeName" , rootNodeName());
+
+    // handler
+    root.put("closeMethodName" , closeMethodName());
+    root.put("hasRootTypeComponents" , hasRootTypeComponents());
+    ListElement rootTypeComponentsElement = new ListElement();
+    for (TypeComponent comp : rootTypeComponents()) {
+      rootTypeComponentsElement.addElement(comp.toYAML());
+    }
+    root.put("rootTypeComponents" , rootTypeComponentsElement);
+
+    // listAspect
+    root.put("configJastAddList" , configJastAddList());
+    root.put("hasTreeListEndpoints" , hasTreeListEndpoints());
+    ListElement typesForReceivingListEndpointsElement = new ListElement();
+    for (TypeDecl typeDecl : typesForReceivingListEndpoints()) {
+      typesForReceivingListEndpointsElement.addElement(typeDecl.toYAML());
+    }
+    root.put("typesForReceivingListEndpoints" , typesForReceivingListEndpointsElement);
+
+    // ragconnect
+    ListElement dependencyDefinitionsElement = new ListElement();
+    for (DependencyDefinition def : allDependencyDefinitionList()) {
+      dependencyDefinitionsElement.addElement(def.toYAML());
+    }
+    root.put("allDependencyDefinitionList" , dependencyDefinitionsElement);
+    ListElement endpointDefinitionsElement = new ListElement();
+    for (EndpointDefinition def : allEndpointDefinitionList()) {
+      endpointDefinitionsElement.addElement(def.toYAML());
+    }
+    root.put("allEndpointDefinitionList" , endpointDefinitionsElement);
+    ListElement mappingDefinitionsElement = new ListElement();
+    for (MappingDefinition def : allMappingDefinitions()) {
+      mappingDefinitionsElement.addElement(def.toYAML());
+    }
+    root.put("allMappingDefinitions" , mappingDefinitionsElement);
+    ListElement tokenComponentsElement = new ListElement();
+    for (TokenComponent comp : tokenComponentsThatNeedProxy()) {
+      tokenComponentsElement.addElement(comp.toYAML());
+    }
+    root.put("tokenComponentsThatNeedProxy" , tokenComponentsElement);
+
+    // receive
+    root.put("configLoggingEnabledForReads" , configLoggingEnabledForReads());
+
+    // send
+    root.put("configLoggingEnabledForWrites" , configLoggingEnabledForWrites());
+
+    // from Handlers.jrag
+    root.put("mqttHandler" , mqttHandler().toYAML());
+    root.put("restHandler" , restHandler().toYAML());
+
+    doc.setRootElement(root);
+    return doc;
+  }
+
+  syn MappingElement EndpointDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    // grammar children
+    result.put("AlwaysApply" , getAlwaysApply());
+    result.put("IndexBasedListAccess" , getIndexBasedListAccess());
+    result.put("WithAdd" , getWithAdd());
+    result.put("Send" , getSend());
+
+    // shared
+    result.put("lastResult" , lastResult());
+
+    // mapping
+    result.put("condition" , sanitizeValueForYAML(condition()));
+    ListElement innerMappingDefinitions = new ListElement();
+    for (MInnerMappingDefinition def : innerMappingDefinitions()) {
+      innerMappingDefinitions.addElement(def.toYAML());
+    }
+    result.put("innerMappingDefinitions" , innerMappingDefinitions);
+    result.put("lastDefinitionToType" , lastDefinitionToType());
+    result.put("preemptiveReturn" , preemptiveReturn());
+
+    // receive + send
+    result.put("connectMethodName" , connectMethodName());
+    result.put("connectParameterName" , connectParameterName());
+    result.put("disconnectMethodName" , disconnectMethodName());
+    result.put("entityName" , entityName());
+    result.put("getterMethodName" , getterMethodName());
+    result.put("parentTypeName" , parentTypeName());
+
+    if (getSend()) {
+      result.put("lastValue" , lastValue());
+      result.put("senderName" , senderName());
+      result.put("shouldSendValue" , shouldSendValue());
+      result.put("tokenResetMethodName" , tokenResetMethodName());
+      result.put("updateMethodName" , updateMethodName());
+      result.put("writeMethodName" , writeMethodName());
+    } else {
+      result.put("hasTypeEndpointTarget" , hasTypeEndpointTarget());
+      result.put("idTokenName" , idTokenName());
+      result.put("internalConnectMethodName" , internalConnectMethodName());
+      result.put("resolveInListMethodName" , resolveInListMethodName());
+      result.put("typeIsList" , getEndpointTarget().typeIsList());
+    }
+    return result;
+  }
+
+  void UntypedEndpointTarget.addToYAML(MappingElement result) {
+    // empty
+  }
+
+  syn Element MInnerMappingDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.put("last" , isLast());
+    result.put("inputVarName" , inputVarName());
+    result.put("methodName" , methodName());
+    result.put("outputVarName" , outputVarName());
+    result.put("toType" , toType());
+    return result;
+  }
+
+  syn Element MappingDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    // grammar children
+    result.put("FromVariableName" , getFromVariableName());
+    result.put("Content" , sanitizeValueForYAML(getContent()));
+
+    // mapping
+    result.put("fromType" , fromType());
+    result.put("methodName" , methodName());
+    result.put("toType" , toType());
+
+    // ragconnect
+    result.put("isUsed" , isUsed());
+    return result;
+  }
+
+  syn Element DependencyDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    // dependencyDefinition
+    result.put("dependencyMethodName" , dependencyMethodName());
+    result.put("internalRelationPrefix" , internalRelationPrefix());
+    result.put("sourceParentTypeName" , sourceParentTypeName());
+    result.put("targetParentTypeName" , targetParentTypeName());
+
+    // tokenComponent
+    result.put("targetEndpointDefinition" , targetEndpointDefinition().toYAML());
+    return result;
+  }
+
+  syn Element TokenComponent.toYAML() {
+    MappingElement result = new MappingElement();
+    result.put("Name" , getName());
+    ListElement dependencySourceDefinitions = new ListElement();
+    for (DependencyDefinition def : getDependencySourceDefinitionList()) {
+      dependencySourceDefinitions.add(def.toYAML());
+    }
+    result.put("DependencySourceDefinitions" , dependencySourceDefinitions);
+
+    result.put("javaType" , javaType());
+    result.put("normalTokenSendDef" , normalTokenSendDef().toYAML());
+    result.put("parentTypeName" , parentTypeName());
+    return result;
+  }
+
+  syn MappingElement Handler.toYAML() {
+    MappingElement result = new MappingElement();
+    result.put("ClassName" , getClassName());
+    result.put("InUse" , getInUse());
+
+    result.put("attributeName" , attributeName());
+    result.put("constructionSnippet" , constructionSnippet());
+    result.put("fieldName" , fieldName());
+    result.put("setupWaitUntilReadyMethodName" , setupWaitUntilReadyMethodName());
+    return result;
+  }
+
+  syn MappingElement TypeComponent.toYAML() {
+    return new MappingElement().put("Name", getName());
+  }
+
+  syn MappingElement TypeDecl.toYAML() {
+    return new MappingElement().put("Name", getName());
+  }
+
+  protected StringElement ASTNode.sanitizeValueForYAML(String value) {
+    return StringElement.of(value.replace("\"" , "\\\"").replace("\n" , "\\n"));
+  }
+}
+
+aspect Navigation {
+  eq Document.getChild().program() = null;
+  eq Document.getChild().ragconnect() = null;
+  eq Document.getChild().containedFile() = null;
+  eq Document.containedFileName() = getFileName();
+}
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag
similarity index 62%
rename from ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
rename to ragconnect.base/src/main/jastadd/Mappings.jrag
index 9d122978ee65307972063cb33e111e951b979569..093f4cc1d4f1cb0eecf1ffe7914c20e6fa715b27 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/Mappings.jrag
@@ -47,50 +47,50 @@ aspect DefaultMappings {
 
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToTreeMapping(String typeName) {
     return treeDefaultMappingDefinition("byte[]", typeName,
-      "String content = new String(input);\n" +
-      "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
-      typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" +
-      "parser.close();\n" +
-      "return result;"
+        "String content = new String(input);\n" +
+            "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
+            typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" +
+            "parser.close();\n" +
+            "return result;"
     );
   }
   syn nta DefaultMappingDefinition RagConnect.defaultTreeToBytesMapping(String typeName) {
     return treeDefaultMappingDefinition(typeName, "byte[]",
-      "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+
-      "input.serialize(generator);\n" +
-      "generator.flush();\n" +
-      "return outputStream.toString().getBytes();"
+        "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" +
+            "input.serialize(generator);\n" +
+            "generator.flush();\n" +
+            "return outputStream.toString().getBytes();"
     );
   }
 
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) {
-    return treeDefaultMappingDefinition("byte[]", JastAddList + "<" + typeName + ">",
-      "String content = new String(input);\n" +
-      "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
-      JastAddList + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
-      "parser.close();\n" +
-      "return result;"
+    return treeDefaultMappingDefinition("byte[]", configJastAddList() + "<" + typeName + ">",
+        "String content = new String(input);\n" +
+            "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
+            configJastAddList() + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
+            "parser.close();\n" +
+            "return result;"
     );
   }
   syn nta DefaultMappingDefinition RagConnect.defaultListTreeToBytesMapping() {
-    return treeDefaultMappingDefinition(JastAddList, "byte[]",
-      "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+
-      "input.serialize(generator);\n" +
-      "generator.flush();\n" +
-      "return outputStream.toString().getBytes();"
+    return treeDefaultMappingDefinition(configJastAddList(), "byte[]",
+        "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" +
+            "input.serialize(generator);\n" +
+            "generator.flush();\n" +
+            "return outputStream.toString().getBytes();"
     );
   }
 
   syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition(
-        "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();");
+      "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();");
   syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition(
       "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();");
   syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition(
@@ -141,7 +141,7 @@ aspect Mappings {
   // --- effectiveMappings ---
   syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings() {
     java.util.List<MappingDefinition> result;
-    if (isReceiveTokenEndpointDefinition() || isReceiveTypeEndpointDefinition()) {
+    if (!getSend()) {
       // if no mappings are specified, or if first mapping is not suitable.
       // then prepend the suitable default mapping
       if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) {
@@ -151,7 +151,7 @@ aspect Mappings {
       } else {
         result = getMappingList();
       }
-    } else if (isSendTokenEndpointDefinition() || isSendTypeEndpointDefinition()) {
+    } else {
       // if no mappings are specified, or if last mapping is not suitable
       // then append the suitable default mapping
       if (getMappingList().isEmpty() || !getMappingList().get(getMappingList().size() - 1).getToType().isByteArray()) {
@@ -160,8 +160,6 @@ aspect Mappings {
       } else {
         result = getMappingList();
       }
-    } else {
-      throw new RuntimeException("Unknown endpoint definition: " + this);
     }
     return result;
   }
@@ -170,7 +168,7 @@ aspect Mappings {
   syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
-    switch(getName()) {
+    switch (getName()) {
       case "boolean":
       case "int":
       case "short":
@@ -178,8 +176,10 @@ aspect Mappings {
       case "float":
       case "double":
       case "char":
-      case "byte": return true;
-      default: return false;
+      case "byte":
+        return true;
+      default:
+        return false;
     }
   }
   syn boolean MappingDefinitionType.isPrimitiveType() = false;
@@ -191,153 +191,164 @@ aspect Mappings {
 
   // --- suitableReceiveDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableReceiveDefaultMapping() {
+    if (getEndpointTarget().isTypeEndpointTarget()) {
+      try {
+        TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
+        return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
+      } catch (Exception ignore) {
+      }
+    }
     switch (targetTypeName()) {
       case "boolean":
-      case "Boolean": return ragconnect().defaultBytesToBooleanMapping();
+      case "Boolean":
+        return ragconnect().defaultBytesToBooleanMapping();
       case "int":
-      case "Integer": return ragconnect().defaultBytesToIntMapping();
+      case "Integer":
+        return ragconnect().defaultBytesToIntMapping();
       case "short":
-      case "Short": return ragconnect().defaultBytesToShortMapping();
+      case "Short":
+        return ragconnect().defaultBytesToShortMapping();
       case "long":
-      case "Long": return ragconnect().defaultBytesToLongMapping();
+      case "Long":
+        return ragconnect().defaultBytesToLongMapping();
       case "float":
-      case "Float": return ragconnect().defaultBytesToFloatMapping();
+      case "Float":
+        return ragconnect().defaultBytesToFloatMapping();
       case "double":
-      case "Double": return ragconnect().defaultBytesToDoubleMapping();
+      case "Double":
+        return ragconnect().defaultBytesToDoubleMapping();
       case "char":
-      case "Character": return ragconnect().defaultBytesToCharMapping();
-      case "String": return ragconnect().defaultBytesToStringMapping();
+      case "Character":
+        return ragconnect().defaultBytesToCharMapping();
+      case "String":
+        return ragconnect().defaultBytesToStringMapping();
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
           // TODO: also support list-types, if list is first type
           return ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
-        } catch (Exception ignore) {}
+        } catch (Exception ignore) {
+        }
         System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
         return null;
     }
   }
-  eq TypeEndpointDefinition.suitableReceiveDefaultMapping() {
-    try {
-      TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
-      return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
-    } catch (Exception ignore) {}
-    return super.suitableReceiveDefaultMapping();
-  }
 
   // --- suitableSendDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() {
+    if (getEndpointTarget().isTypeEndpointTarget()) {
+      try {
+        TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
+        return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
+      } catch (Exception ignore) {
+      }
+    }
     switch (targetTypeName()) {
       case "boolean":
-      case "Boolean": return ragconnect().defaultBooleanToBytesMapping();
+      case "Boolean":
+        return ragconnect().defaultBooleanToBytesMapping();
       case "int":
-      case "Integer": return ragconnect().defaultIntToBytesMapping();
+      case "Integer":
+        return ragconnect().defaultIntToBytesMapping();
       case "short":
-      case "Short": return ragconnect().defaultShortToBytesMapping();
+      case "Short":
+        return ragconnect().defaultShortToBytesMapping();
       case "long":
-      case "Long": return ragconnect().defaultLongToBytesMapping();
+      case "Long":
+        return ragconnect().defaultLongToBytesMapping();
       case "float":
-      case "Float": return ragconnect().defaultFloatToBytesMapping();
+      case "Float":
+        return ragconnect().defaultFloatToBytesMapping();
       case "double":
-      case "Double": return ragconnect().defaultDoubleToBytesMapping();
+      case "Double":
+        return ragconnect().defaultDoubleToBytesMapping();
       case "char":
-      case "Character": return ragconnect().defaultCharToBytesMapping();
-      case "String": return ragconnect().defaultStringToBytesMapping();
+      case "Character":
+        return ragconnect().defaultCharToBytesMapping();
+      case "String":
+        return ragconnect().defaultStringToBytesMapping();
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
           // TODO: also support list-types, if list is last type
           return ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
-        } catch (Exception ignore) {}
+        } catch (Exception ignore) {
+        }
         System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
         return null;
     }
   }
-  eq TypeEndpointDefinition.suitableSendDefaultMapping() {
-    try {
-      TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
-      return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
-    } catch (Exception ignore) {}
-    return super.suitableSendDefaultMapping();
-  }
 
   // --- targetTypeName ---
-  syn String EndpointDefinition.targetTypeName();
-  eq ReceiveTokenEndpointDefinition.targetTypeName() {
-    return getMappingList().isEmpty() ?
-           getToken().effectiveJavaTypeUse().getName() :
-           getMappingList().get(0).getFromType().prettyPrint();
-  }
-  eq ReceiveTypeEndpointDefinition.targetTypeName() {
-    return getMappingList().isEmpty() ?
-           getType().getTypeDecl().getName() :
-           getMappingList().get(0).getFromType().prettyPrint();
-  }
-  eq SendTokenEndpointDefinition.targetTypeName() {
-    return getMappingList().isEmpty() ?
-           getToken().effectiveJavaTypeUse().getName() :
-           getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
-  }
-  eq SendTypeEndpointDefinition.targetTypeName() {
-    return getMappingList().isEmpty() ?
-           getType().getTypeDecl().getName() :
-           getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
+  syn String EndpointDefinition.targetTypeName() {
+    if (getMappingList().isEmpty()) {
+      return getEndpointTarget().targetTypeName();
+    } else {
+      if (getSend()) {
+        return getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
+      } else {
+        return getMappingList().get(0).getFromType().prettyPrint();
+      }
+    }
   }
+  syn String EndpointTarget.targetTypeName();
+  eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
+  eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
 
-//  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
-//    String typeName = getMappingList().isEmpty() ?
-//        getToken().getJavaTypeUse().getName() :
-//        getMappingList().get(0).getFromType().prettyPrint();
-//    switch(typeName) {
-//      case "int":
-//      case "Integer": return ragconnect().defaultStringToIntMapping();
-//      case "short":
-//      case "Short": return ragconnect().defaultStringToShortMapping();
-//      case "long":
-//      case "Long": return ragconnect().defaultStringToLongMapping();
-//      case "float":
-//      case "Float": return ragconnect().defaultStringToFloatMapping();
-//      case "double":
-//      case "Double": return ragconnect().defaultStringToDoubleMapping();
-//      case "char":
-//      case "Character": return ragconnect().defaultStringToCharMapping();
-//      default: return null;
-//    }
-//  }
-//  eq SendToRestDefinition.suitableDefaultMapping() {
-//    String typeName = getMappingList().isEmpty() ?
-//        getToken().getJavaTypeUse().getName() :
-//        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
-//    switch(typeName) {
-//      case "int":
-//      case "Integer": return ragconnect().defaultIntToStringMapping();
-//      case "short":
-//      case "Short": return ragconnect().defaultShortToStringMapping();
-//      case "long":
-//      case "Long": return ragconnect().defaultLongToStringMapping();
-//      case "float":
-//      case "Float": return ragconnect().defaultFloatToStringMapping();
-//      case "double":
-//      case "Double": return ragconnect().defaultDoubleToStringMapping();
-//      case "char":
-//      case "Character": return ragconnect().defaultCharToStringMapping();
-//      default: return null;
-//    }
-//  }
+  //  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
+  //    String typeName = getMappingList().isEmpty() ?
+  //        getToken().getJavaTypeUse().getName() :
+  //        getMappingList().get(0).getFromType().prettyPrint();
+  //    switch(typeName) {
+  //      case "int":
+  //      case "Integer": return ragconnect().defaultStringToIntMapping();
+  //      case "short":
+  //      case "Short": return ragconnect().defaultStringToShortMapping();
+  //      case "long":
+  //      case "Long": return ragconnect().defaultStringToLongMapping();
+  //      case "float":
+  //      case "Float": return ragconnect().defaultStringToFloatMapping();
+  //      case "double":
+  //      case "Double": return ragconnect().defaultStringToDoubleMapping();
+  //      case "char":
+  //      case "Character": return ragconnect().defaultStringToCharMapping();
+  //      default: return null;
+  //    }
+  //  }
+  //  eq SendToRestDefinition.suitableDefaultMapping() {
+  //    String typeName = getMappingList().isEmpty() ?
+  //        getToken().getJavaTypeUse().getName() :
+  //        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
+  //    switch(typeName) {
+  //      case "int":
+  //      case "Integer": return ragconnect().defaultIntToStringMapping();
+  //      case "short":
+  //      case "Short": return ragconnect().defaultShortToStringMapping();
+  //      case "long":
+  //      case "Long": return ragconnect().defaultLongToStringMapping();
+  //      case "float":
+  //      case "Float": return ragconnect().defaultFloatToStringMapping();
+  //      case "double":
+  //      case "Double": return ragconnect().defaultDoubleToStringMapping();
+  //      case "char":
+  //      case "Character": return ragconnect().defaultCharToStringMapping();
+  //      default: return null;
+  //    }
+  //  }
 
   // --- isByteArray ---
   syn boolean MappingDefinitionType.isByteArray() = false;
   eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte");
 
-//  // --- isString ---
-//  syn boolean MappingDefinitionType.isString() = false;
-//  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
+  //  // --- isString ---
+  //  syn boolean MappingDefinitionType.isString() = false;
+  //  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
 
   // --- allMappingDefinitions ---
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
     java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
     // user-defined mappings
-    allMappingDefinitionList().iterator().forEachRemaining(result::add);
+    givenMappingDefinitionList().iterator().forEachRemaining(result::add);
     // byte[] <-> primitive conversion
     result.add(defaultBytesToBooleanMapping());
     result.add(defaultBytesToIntMapping());
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index 80525b590d886af4db3c0dd1916a73d356df2244..9b37ee7f93ea9f31414f61a3a70b01a1ba5ca489 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -1,4 +1,42 @@
 aspect RagConnectNameResolution {
+  // --- lookupTokenEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token);
+  eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
+  syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
+    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
+    for (EndpointTarget target : allEndpointTargetList()) {
+      if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
+        result.add(target.containingEndpointDefinition());
+      }
+    }
+    return result;
+  }
+
+  // --- lookupTypeEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
+  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
+    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
+    for (EndpointTarget target : allEndpointTargetList()) {
+      if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
+        result.add(target.containingEndpointDefinition());
+      }
+    }
+    return result;
+  }
+
+  // --- lookupDependencyDefinition ---
+  inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
+  eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
+    for (DependencyDefinition def : allDependencyDefinitionList()) {
+      if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) {
+        return def;
+      }
+    }
+    return null;
+  }
 
   // rel EndpointDefinition.Mapping* -> MappingDefinition
   refine RefResolverStubs eq EndpointDefinition.resolveMappingByToken(String id, int position) {
@@ -9,7 +47,7 @@ aspect RagConnectNameResolution {
     return result;
   }
   syn MappingDefinition EndpointDefinition.tryResolveMappingByToken(String id) {
-    for (MappingDefinition mappingDefinition : ragconnect().allMappingDefinitionList()) {
+    for (MappingDefinition mappingDefinition : ragconnect().givenMappingDefinitionList()) {
       if (mappingDefinition.getID().equals(id)) {
         return mappingDefinition;
       }
@@ -64,7 +102,7 @@ aspect RagConnectNameResolution {
   }
 
   // rel ___ -> TokenComponent (from relast-preprocessor)
-  // refine here to have an attribute without writing on stderr if not found
+  // refine from relast PP here to have an attribute that does not write on stderr if no TokenComponent was found
   refine NameResolution eq ASTNode.globallyResolveTokenComponentByToken(String id) {
     TokenComponent result = tryGloballyResolveTokenComponentByToken(id);
     if (result == null) {
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index 437f65196c2dc3b8c4f5a112a23e80608084a063..7419f3d880b592e28ae4a45b7a9451849b855368 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -1,25 +1,66 @@
+aspect NewStuff {
+
+  /** Tests if EndpointTarget is a TokenEndpointTarget.
+  *  @return 'true' if this is a TokenEndpointTarget, otherwise 'false'
+  */
+  syn boolean EndpointTarget.isTokenEndpointTarget() = false;
+  eq TokenEndpointTarget.isTokenEndpointTarget() = true;
+
+  /** Tests if EndpointTarget is a TypeEndpointTarget.
+  *  @return 'true' if this is a TypeEndpointTarget, otherwise 'false'
+  */
+  syn boolean EndpointTarget.isTypeEndpointTarget() = false;
+  eq TypeEndpointTarget.isTypeEndpointTarget() = true;
+
+  /** Tests if EndpointTarget is a UntypedEndpointTarget.
+  *  @return 'true' if this is a UntypedEndpointTarget, otherwise 'false'
+  */
+  syn boolean EndpointTarget.isUntypedEndpointTarget() = false;
+  eq UntypedEndpointTarget.isUntypedEndpointTarget() = true;
+
+  /** casts a EndpointTarget into a TokenEndpointTarget if possible.
+   *  @return 'this' cast to a TokenEndpointTarget or 'null'
+   */
+  syn TokenEndpointTarget EndpointTarget.asTokenEndpointTarget();
+  eq EndpointTarget.asTokenEndpointTarget() = null;
+  eq TokenEndpointTarget.asTokenEndpointTarget() = this;
+
+  /** casts a EndpointTarget into a TypeEndpointTarget if possible.
+   *  @return 'this' cast to a TypeEndpointTarget or 'null'
+   */
+  syn TypeEndpointTarget EndpointTarget.asTypeEndpointTarget();
+  eq EndpointTarget.asTypeEndpointTarget() = null;
+  eq TypeEndpointTarget.asTypeEndpointTarget() = this;
+
+  /** casts a EndpointTarget into a UntypedEndpointTarget if possible.
+   *  @return 'this' cast to a UntypedEndpointTarget or 'null'
+   */
+  syn UntypedEndpointTarget EndpointTarget.asUntypedEndpointTarget();
+  eq EndpointTarget.asUntypedEndpointTarget() = null;
+  eq UntypedEndpointTarget.asUntypedEndpointTarget() = this;
+}
 aspect RagConnectNavigation {
 
   // --- program ---
   eq RagConnect.getChild().program() = getProgram();
-  eq MRagConnect.getChild().program() = getRagConnect().program();
 
   // --- ragconnect ---
   inh RagConnect ASTNode.ragconnect();
   eq RagConnect.getChild().ragconnect() = this;
-  eq MRagConnect.getChild().ragconnect() = getRagConnect();
 
   // --- containedConnectSpecification ---
   inh ConnectSpecification ASTNode.containedConnectSpecification();
   eq RagConnect.getChild().containedConnectSpecification() = null;
-  eq MRagConnect.getChild().containedConnectSpecification() = null;
   eq Document.getChild().containedConnectSpecification() = null;
   eq Program.getChild().containedConnectSpecification() = null;
   eq ConnectSpecification.getChild().containedConnectSpecification() = this;
 
+  // --- containingEndpointDefinition ---
+  inh EndpointDefinition EndpointTarget.containingEndpointDefinition();
+  eq EndpointDefinition.getEndpointTarget().containingEndpointDefinition() = this;
+
   // --- containedFile
   eq RagConnect.getChild().containedFile() = null;
-  eq MRagConnect.getChild().containedFile() = null;
 
   // --- containedFileName ---
   eq ConnectSpecificationFile.containedFileName() = getFileName();
@@ -31,97 +72,26 @@ aspect RagConnectNavigation {
 //    return containedFile().getFileName();
   }
 
-  //--- allEndpointDefinitionList ---
-  syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() {
-    List<EndpointDefinition> result = new ArrayList<>();
-    for (var spec : getConnectSpecificationFileList()) {
-      spec.getEndpointDefinitionList().forEach(result::add);
-    }
-    return result;
-  }
 
-  //--- allDependencyDefinitionList ---
-  syn List<DependencyDefinition> RagConnect.allDependencyDefinitionList() {
-    List<DependencyDefinition> result = new ArrayList<>();
-    for (var spec : getConnectSpecificationFileList()) {
-      spec.getDependencyDefinitionList().forEach(result::add);
+  //--- allEndpointTargetList ---
+  syn List<EndpointTarget> RagConnect.allEndpointTargetList() {
+    List<EndpointTarget> result = new ArrayList<>();
+    for (ConnectSpecification spec : getConnectSpecificationFileList()) {
+      spec.getEndpointDefinitionList().forEach(endpointDef -> result.add(endpointDef.getEndpointTarget()));
     }
     return result;
   }
 
-  //--- allMappingDefinitionList ---
-  syn List<MappingDefinition> RagConnect.allMappingDefinitionList() {
+
+  //--- givenMappingDefinitionList ---
+  syn List<MappingDefinition> RagConnect.givenMappingDefinitionList() {
     List<MappingDefinition> result = new ArrayList<>();
-    for (var spec : getConnectSpecificationFileList()) {
+    for (ConnectSpecification spec : getConnectSpecificationFileList()) {
       spec.getMappingDefinitionList().forEach(result::add);
     }
     return result;
   }
 
-  // --- isTokenEndpointDefinition ---
-  syn boolean EndpointDefinition.isTokenEndpointDefinition() = false;
-  eq TokenEndpointDefinition.isTokenEndpointDefinition() = true;
-
-  // --- asTokenEndpointDefinition ---
-  syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null;
-  eq TokenEndpointDefinition.asTokenEndpointDefinition() = this;
-
-  // --- isTypeEndpointDefinition ---
-  syn boolean EndpointDefinition.isTypeEndpointDefinition() = false;
-  eq TypeEndpointDefinition.isTypeEndpointDefinition() = true;
-
-  // --- asTypeEndpointDefinition ---
-  syn TypeEndpointDefinition EndpointDefinition.asTypeEndpointDefinition() = null;
-  eq TypeEndpointDefinition.asTypeEndpointDefinition() = this;
-
-  // --- isReceiveTokenEndpointDefinition ---
-  syn boolean EndpointDefinition.isReceiveTokenEndpointDefinition() = false;
-  eq ReceiveTokenEndpointDefinition.isReceiveTokenEndpointDefinition() = true;
-
-  // --- asReceiveTokenEndpointDefinition ---
-  syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null;
-  eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this;
-
-  // --- isSendTokenEndpointDefinition ---
-  syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false;
-  eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true;
-
-  // --- asSendTokenEndpointDefinition ---
-  syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null;
-  eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this;
-
-  // --- isReceiveTypeEndpointDefinition ---
-  syn boolean EndpointDefinition.isReceiveTypeEndpointDefinition() = false;
-  eq ReceiveTypeEndpointDefinition.isReceiveTypeEndpointDefinition() = true;
-
-  // --- asReceiveTypeEndpointDefinition ---
-  syn ReceiveTypeEndpointDefinition EndpointDefinition.asReceiveTypeEndpointDefinition() = null;
-  eq ReceiveTypeEndpointDefinition.asReceiveTypeEndpointDefinition() = this;
-
-  // --- isSendTypeEndpointDefinition ---
-  syn boolean EndpointDefinition.isSendTypeEndpointDefinition() = false;
-  eq SendTypeEndpointDefinition.isSendTypeEndpointDefinition() = true;
-
-  // --- asSendTypeEndpointDefinition ---
-  syn SendTypeEndpointDefinition EndpointDefinition.asSendTypeEndpointDefinition() = null;
-  eq SendTypeEndpointDefinition.asSendTypeEndpointDefinition() = this;
-
-  // --- targetEndpointDefinition ---
-  syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() {
-    // resolve definition in here, as we do not need resolveMethod in any other place (yet)
-    for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) {
-      if (endpointDefinition.isSendTokenEndpointDefinition() &&
-          endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) {
-        return endpointDefinition;
-      }
-      if (endpointDefinition.isSendTypeEndpointDefinition() &&
-          endpointDefinition.asSendTypeEndpointDefinition().getType().equals(this.getTarget())) {
-        return endpointDefinition;
-      }
-    }
-    return null;
-  }
-
   // --- effectiveJavaTypeUse (should be in preprocessor) ---
   syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String");
 
@@ -129,13 +99,6 @@ aspect RagConnectNavigation {
   syn boolean MappingDefinition.isDefaultMappingDefinition() = false;
   eq DefaultMappingDefinition.isDefaultMappingDefinition() = true;
 
-  // --- mragconnect ---
-  inh MRagConnect MHandler.mragconnect();
-  eq MRagConnect.getHandler().mragconnect() = this;
-
-  // --- rootTypeComponents ---
-  syn JastAddList<MTypeComponent> MHandler.rootTypeComponents() = mragconnect().getRootTypeComponents();
-
   // --- isListComponent --- (defined in PP, but only on TypeComponent)
   syn boolean Component.isListComponent() = false;
 }
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index 4301c70e77435e46fb131b093e9009577109d680..30e6d5ad6a034f9b35de570414e8d4e904751ca2 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -1,35 +1,39 @@
-RagConnect ::= ConnectSpecificationFile* Program ;
+RagConnect ::= ConnectSpecificationFile* Program Handler* Configuration;
 
-abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ;
-ConnectSpecificationFile : ConnectSpecification ::= <FileName> ;
-
-abstract EndpointDefinition ::= <AlwaysApply:boolean> ;
+abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition*;
+ConnectSpecificationFile : ConnectSpecification ::= <FileName>;
 
+EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget;
 rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
 
-abstract TokenEndpointDefinition : EndpointDefinition;
-rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*;
-
-ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
-SendTokenEndpointDefinition : TokenEndpointDefinition;
-
-abstract TypeEndpointDefinition : EndpointDefinition ::= <IndexBasedListAccess:boolean> ;
-rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*;
-
-ReceiveTypeEndpointDefinition : TypeEndpointDefinition ::= <WithAdd:boolean>;
-SendTypeEndpointDefinition : TypeEndpointDefinition;
+abstract EndpointTarget;
+TokenEndpointTarget : EndpointTarget;
+rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
+TypeEndpointTarget : EndpointTarget;
+rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
+UntypedEndpointTarget : EndpointTarget ::= <TokenOrType>;  // only used by parser
+// to be integrated:
+//AttributeEndpointTarget : EndpointTarget ::= <Name> ;
+//RelationEndpointTarget : EndpointTarget ;
+//rel RelationEndpointTarget.Role <-> Role.RelationEndpointTarget* ;
 
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
 rel DependencyDefinition.Target -> Component;
 
-MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ;
+MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content>;
 abstract MappingDefinitionType ::= ;
-JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-DefaultMappingDefinition : MappingDefinition ;
-
-// only used by parser
-abstract UntypedEndpointDefinition : EndpointDefinition ::= <TokenOrType> <Indexed:boolean> <WithAdd:boolean> ;
-ReceiveUntypedEndpointDefinition : UntypedEndpointDefinition;
-SendUntypedEndpointDefinition : UntypedEndpointDefinition;
+JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
+JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
+DefaultMappingDefinition : MappingDefinition;
+
+Handler ::= <DefinitionFileName> <ClassName> <UniqueName> <InUse:boolean>;
+
+Configuration ::=
+<LoggingEnabledForReads:boolean>
+<LoggingEnabledForWrites:boolean>
+<LoggingEnabledForIncremental:boolean>
+<JastAddList:String>
+<IncrementalOptionActive:boolean>
+<ExperimentalJastAdd329:boolean>;
+rel Configuration.RootNode -> TypeDecl ;
diff --git a/ragconnect.base/src/main/jastadd/Util.jadd b/ragconnect.base/src/main/jastadd/Util.jadd
index 3b59b13c53a8d0e1ca2a05feba43294797a4577e..81b820c0b4610e17f82aa31f97c44c97405443e0 100644
--- a/ragconnect.base/src/main/jastadd/Util.jadd
+++ b/ragconnect.base/src/main/jastadd/Util.jadd
@@ -2,4 +2,6 @@ aspect Util {
   static String ASTNode.capitalize(String s) {
     return Character.toUpperCase(s.charAt(0)) + s.substring(1);
   }
+  protected T JastAddList.firstChild() { return getChild(0); }
+  protected T JastAddList.lastChild() { return getChild(getNumChild() - 1); }
 }
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
deleted file mode 100644
index f4045d7aab8c587970e9645f3cb13e36cb617db4..0000000000000000000000000000000000000000
--- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
-Design considerations
-- InnerMappingDefinition needed for iteration attribute (first, last) - not easily possible with list-relation
-*/
-
-aspect AttributesForMustache {
-  // --- EndpointDefinition ---
-  syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList";
-
-  // --- MRagConnect ---
-  eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0;
-
-  syn String MRagConnect.closeMethod() = "ragconnectCloseConnections";
-  syn String MRagConnect.mqttHandlerAttribute() = "_mqttHandler";
-  syn String MRagConnect.mqttHandlerField() = "_mqttHandler";
-  syn String MRagConnect.mqttSetupWaitUntilReadyMethod() = "ragconnectSetupMqttWaitUntilReady";
-
-  syn String MRagConnect.restHandlerAttribute() = "_restHandler";
-  syn String MRagConnect.restHandlerField() = "_restHandler";
-
-  syn boolean MRagConnect.hasTreeListEndpoints() = !sendingTreeListEndpoints().isEmpty() || !receivingTreeListEndpoints().isEmpty();
-  syn List<MTypeEndpointDefinition> MRagConnect.sendingTreeListEndpoints() {
-    List<MTypeEndpointDefinition> result = new ArrayList<>();
-    for (var mEndpointDef : getTypeSendDefinitionList()) {
-      if (mEndpointDef.typeIsList()) {
-        result.add(mEndpointDef);
-      }
-    }
-    return result;
-  }
-  syn List<MTypeEndpointDefinition> MRagConnect.receivingTreeListEndpoints() {
-    List<MTypeEndpointDefinition> result = new ArrayList<>();
-    for (var mEndpointDef : getTypeReceiveDefinitionList()) {
-      if (mEndpointDef.typeIsList()) {
-        result.add(mEndpointDef);
-      }
-    }
-    return result;
-  }
-  syn List<TypeDecl> MRagConnect.typesForReceivingListEndpoints() {
-    return receivingTreeListEndpoints().stream()
-      .map(mEndpointDef -> mEndpointDef.type().getTypeDecl())
-      .distinct()
-      .collect(java.util.stream.Collectors.toList());
-  }
-
-  // --- MEndpointDefinition ---
-  syn String MEndpointDefinition.preemptiveExpectedValue();
-  syn String MEndpointDefinition.preemptiveReturn();
-  syn EndpointDefinition MEndpointDefinition.endpointDef();
-  syn String MEndpointDefinition.firstInputVarName();
-  syn String MEndpointDefinition.parentTypeName();
-  syn String MEndpointDefinition.entityName();
-  syn String MEndpointDefinition.updateMethod();
-  syn String MEndpointDefinition.writeMethod();
-  syn String MEndpointDefinition.getterMethod();
-
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1;
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName();
-
-  syn String MEndpointDefinition.connectParameterName() = "uriString";
-  syn String MEndpointDefinition.connectMethod() = "connect" + entityName();
-  syn String MEndpointDefinition.internalConnectMethod() = "_internal_" + connectMethod();
-  syn boolean MEndpointDefinition.isTypeEndpointDefinition() = endpointDef().isTypeEndpointDefinition();
-
-  syn String MEndpointDefinition.disconnectMethod() {
-    // if both (send and receive) are defined for an endpoint, ensure methods with different names
-    String extra;
-    if (endpointDef().isTokenEndpointDefinition()) {
-      extra = endpointDef().asTokenEndpointDefinition().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
-    } else if (endpointDef().isTypeEndpointDefinition()) {
-      extra = endpointDef().asTypeEndpointDefinition().lookupTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
-    } else {
-      extra = "";
-    }
-    return "disconnect" + extra + entityName();
-  }
-
-  syn String MEndpointDefinition.uniqueSuffix();
-  eq MTokenSendDefinition.uniqueSuffix() = "Send";
-  eq MTokenReceiveDefinition.uniqueSuffix() = "Receive";
-  eq MTypeSendDefinition.uniqueSuffix() = "Send";
-  eq MTypeReceiveDefinition.uniqueSuffix() = "Receive";
-
-  // TODO potentially dangerous because asXEndpointDefinition can return null
-  syn TokenComponent MEndpointDefinition.token() = endpointDef().asTokenEndpointDefinition().getToken();
-  syn TypeComponent MEndpointDefinition.type() = endpointDef().asTypeEndpointDefinition().getType();
-  syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply();
-  syn boolean MEndpointDefinition.typeIsList() = endpointDef().typeIsList();
-  syn String MEndpointDefinition.tokenName() = token().getName();
-  syn String MEndpointDefinition.typeName() = type().getName();
-  syn String MEndpointDefinition.typeDeclName() = type().getTypeDecl().getName();
-  syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
-  syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
-  syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName();
-  syn String MEndpointDefinition.condition() {
-    // TODO probably, this has to be structured in a better way
-    if (lastDefinition().mappingDef().getToType().isArray()) {
-      return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
-    }
-    if (endpointDef().isTokenEndpointDefinition() && token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
-      return preemptiveExpectedValue() + " == " + lastResult();
-    }
-    if (endpointDef().isReceiveTypeEndpointDefinition() && endpointDef().asReceiveTypeEndpointDefinition().getWithAdd()) {
-      // only check if received list is not null
-      return lastResult() + " == null";
-    }
-    if (endpointDef().isTypeEndpointDefinition() && type().isOptComponent()) {
-      // use "hasX()" instead of "getX() != null" for optionals
-      return "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
-    }
-    if (lastDefinition().mappingDef().getToType().isPrimitiveType() || lastDefinition().mappingDef().isDefaultMappingDefinition()) {
-      return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
-    }
-    return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
-  }
-  syn String MEndpointDefinition.sender() = null; // only for M*SendDefinitions
-  syn String MEndpointDefinition.lastValue() = sender() + ".lastValue"; // only for M*SendDefinitions
-
-  // --- MTokenEndpointDefinition ---
-  eq MTokenEndpointDefinition.getterMethod() = "get" + tokenName();
-  eq MTokenEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName();
-  eq MTokenEndpointDefinition.entityName() = tokenName();
-
-  // --- MTypeEndpointDefinition ---
-  syn boolean MTypeEndpointDefinition.isWithAdd() = endpointDef().isReceiveTypeEndpointDefinition() ? endpointDef().asReceiveTypeEndpointDefinition().getWithAdd() : false;
-  syn boolean MTypeEndpointDefinition.isIndexBasedListAccess() = endpointDef().asTypeEndpointDefinition().getIndexBasedListAccess();
-  eq MTypeEndpointDefinition.getterMethod() = "get" + typeName() + (typeIsList() ? "List" : "");
-  eq MTypeEndpointDefinition.parentTypeName() = type().containingTypeDecl().getName();
-  eq MTypeEndpointDefinition.entityName() = typeName() + (typeIsList() && !isIndexBasedListAccess() ? "List" : "");
-
-  // --- MInnerMappingDefinition ---
-  inh boolean MInnerMappingDefinition.isLast();
-  inh String MInnerMappingDefinition.inputVarName();
-  syn String MInnerMappingDefinition.toType() = mappingDef().getToType().prettyPrint();
-  syn String MInnerMappingDefinition.methodName() = getMMappingDefinition().methodName();
-  syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition();
-  syn String MInnerMappingDefinition.outputVarName() = "result" + methodName();  // we do not need "_" in between here, because methodName begins with one
-
-  // --- MTokenReceiveDefinition ---
-  eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethod() + "()";
-  eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
-  eq MTokenReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
-  eq MTokenReceiveDefinition.firstInputVarName() = "message";
-  eq MTokenReceiveDefinition.updateMethod() = null;
-  eq MTokenReceiveDefinition.writeMethod() = null;
-
-  // --- MTokenSendDefinition ---
-  eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue();
-  eq MTokenSendDefinition.preemptiveReturn() = "return false;";
-  eq MTokenSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
-  eq MTokenSendDefinition.firstInputVarName() = getterMethod() + "()";
-  eq MTokenSendDefinition.updateMethod() = "_update_" + tokenName();
-  eq MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
-
-  eq MTokenSendDefinition.sender() = "_sender_" + tokenName();
-  syn String MTokenSendDefinition.tokenResetMethod() = getterMethod() + "_reset";
-  syn boolean MTokenSendDefinition.shouldSendValue() = endpointDef().asTokenEndpointDefinition().shouldSendValue();
-
-  // MTypeReceiveDefinition
-  eq MTypeReceiveDefinition.preemptiveExpectedValue() = getterMethod() + "()";
-  eq MTypeReceiveDefinition.preemptiveReturn() = "return;";
-  eq MTypeReceiveDefinition.endpointDef() = getReceiveTypeEndpointDefinition();
-  eq MTypeReceiveDefinition.firstInputVarName() = "message";
-  eq MTypeReceiveDefinition.updateMethod() = null;
-  eq MTypeReceiveDefinition.writeMethod() = null;
-
-  syn String MTypeReceiveDefinition.resolveInListMethodName() = "_ragconnect_resolve" + entityName() + "InList";
-  syn String MTypeReceiveDefinition.idTokenName() = endpointDef().idTokenName();
-
-  // MTypeSendDefinition
-  eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
-  eq MTypeSendDefinition.preemptiveReturn() = "return false;";
-  eq MTypeSendDefinition.endpointDef() = getSendTypeEndpointDefinition();
-  eq MTypeSendDefinition.firstInputVarName() = getterMethod() + "()";
-  eq MTypeSendDefinition.updateMethod() = "_update_" + typeName();
-  eq MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName();
-
-  eq MTypeSendDefinition.sender() = "_sender_" + typeName();
-  syn String MTypeSendDefinition.tokenResetMethod() = getterMethod() + "_reset";
-  syn boolean MTypeSendDefinition.shouldSendValue() = endpointDef().asTypeEndpointDefinition().shouldSendValue();
-
-  // --- MMappingDefinition ---
-  syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint();
-  syn String MMappingDefinition.methodName() = "_apply_" + getMappingDefinition().getID();
-  syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint();
-  syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName();
-  syn String MMappingDefinition.content() = getMappingDefinition().getContent();
-  syn boolean MMappingDefinition.isUsed() = !getMappingDefinition().effectiveUsedAt().isEmpty();
-
-  // --- MDependencyDefinition ---
-  syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName();
-  syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID());
-  syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
-  syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID();
-  syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() {
-    return getDependencyDefinition().targetEndpointDefinition().toMustache();
-  }
-
-  // --- MTypeComponent ---
-  syn String MTypeComponent.name() = getTypeComponent().getName();
-  inh boolean MTypeComponent.isFirst();
-
-  // --- MTokenComponent ---
-  syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName();
-  syn String MTokenComponent.name() = getTokenComponent().getName();
-  syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint();
-  syn String MTokenComponent.internalName() = getTokenComponent().needProxyToken() ? "_internal_" + name() : externalName();
-  syn String MTokenComponent.externalName() = name();
-  syn MTokenSendDefinition MTokenComponent.normalTokenSendDef() {
-    for (TokenEndpointDefinition endpointDef : getTokenComponent().getTokenEndpointDefinitionList()) {
-      if (endpointDef.shouldSendValue()) {
-        return endpointDef.asSendTokenEndpointDefinition().toMustache();
-      }
-    }
-    return null;
-  }
-
-  // --- toMustache ---
-  syn lazy MRagConnect RagConnect.toMustache() {
-    MRagConnect result = new MRagConnect();
-    result.setRagConnect(this);
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
-      if (def.isReceiveTokenEndpointDefinition()) {
-        result.addTokenReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
-      } else if (def.isSendTokenEndpointDefinition()) {
-        result.addTokenSendDefinition(def.asSendTokenEndpointDefinition().toMustache());
-      } else if (def.isReceiveTypeEndpointDefinition()) {
-        result.addTypeReceiveDefinition(def.asReceiveTypeEndpointDefinition().toMustache());
-      } else if (def.isSendTypeEndpointDefinition()) {
-        result.addTypeSendDefinition(def.asSendTypeEndpointDefinition().toMustache());
-      } else {
-        throw new RuntimeException("Unknown endpoint definition: " + def);
-      }
-    }
-    for (MappingDefinition def : allMappingDefinitions()) {
-      result.addMappingDefinition(def.toMustache());
-    }
-    for (DependencyDefinition def : allDependencyDefinitionList()) {
-      result.addDependencyDefinition(def.toMustache());
-    }
-    for (TokenComponent token : getProgram().allTokenComponents()) {
-      if (token.needProxyToken()) {
-        result.addTokenComponent(token.toMustache());
-      }
-    }
-    for (Component child : rootNode.getComponentList()) {
-      if (child.isTypeComponent()) {
-        result.addRootTypeComponent(child.asTypeComponent().toMustache());
-      }
-    }
-    // MHandler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>;
-    result.addHandler(new MHandler("MqttServerHandler", "new MqttServerHandler(\"RagConnectMQTT\")",
-                                   result.mqttHandlerAttribute(), result.mqttHandlerField(), usesMqtt));
-    result.addHandler(new MHandler("RestServerHandler", "new RestServerHandler(\"RagConnectREST\")",
-                                   result.restHandlerAttribute(), result.restHandlerField(), usesRest));
-    return result;
-  }
-
-  protected void MEndpointDefinition.addInnerMappings() {
-    for (MappingDefinition def : endpointDef().effectiveMappings()) {
-      MInnerMappingDefinition inner = new MInnerMappingDefinition();
-      inner.setMMappingDefinition(def.toMustache());
-      addInnerMappingDefinition(inner);
-    }
-  }
-
-  public abstract MEndpointDefinition EndpointDefinition.toMustache();
-  syn lazy MTokenReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
-    MTokenReceiveDefinition result = new MTokenReceiveDefinition();
-    result.setReceiveTokenEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
-
-  syn lazy MTokenSendDefinition SendTokenEndpointDefinition.toMustache() {
-    MTokenSendDefinition result = new MTokenSendDefinition();
-    result.setSendTokenEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
-
-  syn lazy MTypeReceiveDefinition ReceiveTypeEndpointDefinition.toMustache() {
-    MTypeReceiveDefinition result = new MTypeReceiveDefinition();
-    result.setReceiveTypeEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
-
-  syn lazy MTypeSendDefinition SendTypeEndpointDefinition.toMustache() {
-    MTypeSendDefinition result = new MTypeSendDefinition();
-    result.setSendTypeEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
-
-  syn lazy MMappingDefinition MappingDefinition.toMustache() {
-    MMappingDefinition result = new MMappingDefinition();
-    result.setMappingDefinition(this);
-    return result;
-  }
-
-  syn lazy MDependencyDefinition DependencyDefinition.toMustache() {
-    MDependencyDefinition result = new MDependencyDefinition();
-    result.setDependencyDefinition(this);
-    return result;
-  }
-
-  syn lazy MTypeComponent TypeComponent.toMustache() {
-    MTypeComponent result = new MTypeComponent();
-    result.setTypeComponent(this);
-    return result;
-  }
-
-  syn lazy MTokenComponent TokenComponent.toMustache() {
-    MTokenComponent result = new MTokenComponent();
-    result.setTokenComponent(this);
-    for (DependencyDefinition def : getDependencySourceDefinitionList()) {
-      result.addDependencyDefinition(def.toMustache());
-    }
-    return result;
-  }
-}
-
-aspect AspectGeneration {
-  // --- rootNodeName ---
-  syn String ASTNode.rootNodeName() = rootNode.getName();
-
-  public String RagConnect.generateAspect(String rootNodeName) {
-    rootNode = getProgram().resolveTypeDecl(rootNodeName);
-    return toMustache().generateAspect();
-  }
-
-  public String MRagConnect.generateAspect() {
-    StringBuilder sb = new StringBuilder();
-    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
-      @Override
-      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
-        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
-          @Override
-          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
-            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
-            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
-              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
-            }
-            return wrapper;
-          }
-        };
-      }
-    };
-    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
-    mf.setObjectHandler(roh);
-    com.github.mustachejava.Mustache m = mf.compile("ragconnect.mustache");
-    m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), this);
-    return sb.toString();
-  }
-}
-
-aspect GrammarGeneration {
-  syn java.util.List<Relation> RagConnect.additionalRelations() {
-    java.util.List<Relation> result = new java.util.ArrayList<>();
-    for (DependencyDefinition dd : allDependencyDefinitionList()) {
-      result.add(dd.getRelationToCreate());
-    }
-    return result;
-  }
-
-  syn nta Relation DependencyDefinition.getRelationToCreate() {
-    String internalRelationPrefix = toMustache().internalRelationPrefix();
-    BidirectionalRelation result = new BidirectionalRelation();
-    NavigableRole left = new ListRole(internalRelationPrefix + "Source");
-    left.setType(getTarget().containingTypeDecl());
-    NavigableRole right = new ListRole(internalRelationPrefix + "Target");
-    right.setType(getSource().containingTypeDecl());
-    result.setLeft(left);
-    result.setRight(right);
-    result.addComment(new WhitespaceComment("\n"));
-    return result;
-  }
-
-//  coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect;
-
-//  TypeEndpointDefinition contributes getTokenToCreate()
-//    when typeIsList() && !getUseList()
-//    to RagConnect.additionalTokens()
-////    for ragconnect()
-//    ;
-
-  syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() {
-    java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>();
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
-      if (def.isTypeEndpointDefinition() && def.getTokenToCreate() != null) {
-        result.put(def.asTypeEndpointDefinition().getType().getTypeDecl(), def.getTokenToCreate());
-      }
-    }
-    return result;
-  }
-
-  syn TokenComponent EndpointDefinition.getTokenToCreate() = null;
-  eq TypeEndpointDefinition.getTokenToCreate() {
-    if (typeIsList() && getIndexBasedListAccess()) {
-      TokenComponent result = new TokenComponent();
-      result.setName(idTokenName());
-      result.setNTA(false);
-      result.setJavaTypeUse(new SimpleJavaTypeUse("String"));
-      return result;
-    } else {
-      return null;
-    }
-  }
-}
-
-aspect GrammarExtension {
-  refine BackendAbstractGrammar public void TokenComponent.generateAbstractGrammar(StringBuilder b) {
-    if (getNTA()) {
-      b.append("/");
-    }
-    b.append("<");
-    if (!getName().equals("")) {
-      b.append(toMustache().internalName()).append(":");
-    }
-    effectiveJavaTypeUse().generateAbstractGrammar(b);
-    b.append(">");
-    if (getNTA()) {
-      b.append("/");
-    }
-  }
-}
diff --git a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast
deleted file mode 100644
index 8255cfe5ad9e67e00e53e95b4ddc110a480dce28..0000000000000000000000000000000000000000
--- a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast
+++ /dev/null
@@ -1,28 +0,0 @@
-MRagConnect ::= TokenReceiveDefinition:MTokenReceiveDefinition* TokenSendDefinition:MTokenSendDefinition* TypeReceiveDefinition:MTypeReceiveDefinition* TypeSendDefinition:MTypeSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent* Handler:MHandler*;
-
-abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
-abstract MTokenEndpointDefinition : MEndpointDefinition;
-MTokenReceiveDefinition : MTokenEndpointDefinition;
-MTokenSendDefinition : MTokenEndpointDefinition;
-abstract MTypeEndpointDefinition : MEndpointDefinition;
-MTypeReceiveDefinition : MTypeEndpointDefinition;
-MTypeSendDefinition : MTypeEndpointDefinition;
-
-MMappingDefinition;
-MInnerMappingDefinition;
-MDependencyDefinition;
-MTypeComponent;
-MTokenComponent;
-MHandler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>;
-
-rel MRagConnect.RagConnect -> RagConnect;
-rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition;
-rel MTokenReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition;
-rel MTokenSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition;
-rel MTypeReceiveDefinition.ReceiveTypeEndpointDefinition -> ReceiveTypeEndpointDefinition;
-rel MTypeSendDefinition.SendTypeEndpointDefinition -> SendTypeEndpointDefinition;
-rel MMappingDefinition.MappingDefinition -> MappingDefinition;
-rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition;
-rel MTypeComponent.TypeComponent -> TypeComponent;
-rel MTokenComponent.TokenComponent -> TokenComponent;
-rel MTokenComponent.DependencyDefinition* -> MDependencyDefinition;
diff --git a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag b/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag
deleted file mode 100644
index 04b9cf89c248c39740677c88835b56c5325493bf..0000000000000000000000000000000000000000
--- a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag
+++ /dev/null
@@ -1,212 +0,0 @@
-aspect MustacheNodesToYAML {
-  syn Document MRagConnect.toYAML() {
-    Document doc = new Document();
-    MappingElement root = new MappingElement();
-    root.put("rootNodeName", rootNodeName());
-    root.put("closeMethod", closeMethod());
-    root.put("usesMqtt", usesMqtt);
-    root.put("usesRest", usesRest);
-    // mqtt
-    root.put("mqttHandlerField", mqttHandlerField());
-    root.put("mqttHandlerAttribute", mqttHandlerAttribute());
-    root.put("mqttSetupWaitUntilReadyMethod", mqttSetupWaitUntilReadyMethod());
-
-    // rootTypeComponents
-    ListElement rootTypeComponents = new ListElement();
-    for (MTypeComponent comp : getRootTypeComponentList()) {
-      MappingElement inner = new MappingElement();
-      inner.put("first", comp.isFirst());
-      inner.put("name", comp.name());
-      rootTypeComponents.addElement(inner);
-    }
-    root.put("RootTypeComponents", rootTypeComponents);
-
-    // rest
-    root.put("restHandlerField", restHandlerField());
-    root.put("restHandlerAttribute", restHandlerAttribute());
-
-    // TokenReceiveDefinitions
-    ListElement receiveDefinitions = new ListElement();
-    for (MTokenReceiveDefinition def : getTokenReceiveDefinitionList()) {
-      receiveDefinitions.addElement(def.toYAML());
-    }
-    root.put("TokenReceiveDefinitions", receiveDefinitions);
-
-    // TokenSendDefinitions
-    ListElement sendDefinitions = new ListElement();
-    for (MTokenSendDefinition def : getTokenSendDefinitionList()) {
-      sendDefinitions.addElement(def.toYAML());
-    }
-    root.put("TokenSendDefinitions", sendDefinitions);
-
-    // TypeReceiveDefinitions
-    ListElement typeReceiveDefinitions = new ListElement();
-    for (MTypeReceiveDefinition def : getTypeReceiveDefinitionList()) {
-      typeReceiveDefinitions.addElement(def.toYAML());
-    }
-    root.put("TypeReceiveDefinitions", typeReceiveDefinitions);
-
-    // TypeSendDefinitions
-    ListElement typeSendDefinitions = new ListElement();
-    for (MTypeSendDefinition def : getTypeSendDefinitionList()) {
-      typeSendDefinitions.addElement(def.toYAML());
-    }
-    root.put("TypeSendDefinitions", typeSendDefinitions);
-
-    // MappingDefinitions
-    ListElement mappingDefinitions = new ListElement();
-    for (MMappingDefinition def : getMappingDefinitionList()) {
-      mappingDefinitions.addElement(def.toYAML());
-    }
-    root.put("MappingDefinitions", mappingDefinitions);
-
-    // DependencyDefinitions
-    ListElement dependencyDefinitions = new ListElement();
-    for (MDependencyDefinition def : getDependencyDefinitionList()) {
-      dependencyDefinitions.addElement(def.toYAML());
-    }
-    root.put("DependencyDefinitions", dependencyDefinitions);
-
-    // TokenComponents
-    ListElement tokenComponents = new ListElement();
-    for (MTokenComponent comp : getTokenComponentList()) {
-      tokenComponents.addElement(comp.toYAML());
-    }
-    root.put("TokenComponents", tokenComponents);
-
-    // Handlers
-    ListElement handlers = new ListElement();
-    for (MHandler handler : getHandlerList()) {
-      handlers.add(handler.toYAML()
-                          .put("rootTokenComponents", rootTypeComponents.treeCopy()) );
-    }
-    root.put("Handlers", handlers);
-
-    doc.setRootElement(root);
-    return doc;
-  }
-
-  syn MappingElement MEndpointDefinition.toYAML() {
-    MappingElement result = new MappingElement();
-    result.put("parentTypeName", parentTypeName());
-    result.put("connectMethod", connectMethod());
-    result.put("connectParameterName", connectParameterName());
-    result.put("lastDefinitionToType", lastDefinitionToType());
-    result.put("preemptiveReturn", preemptiveReturn());
-    result.put("alwaysApply", alwaysApply());
-    result.put("condition",
-    condition().replace("\"", "\\\"").replace("\n", "\\n"));
-    result.put("lastResult", lastResult());
-    result.put("tokenName", tokenName());
-    result.put("InnerMappingDefinitions", innerMappingDefinitionsAsListElement());
-    return result;
-  }
-
-  syn MappingElement MTokenReceiveDefinition.toYAML() {
-    MappingElement result = super.toYAML();
-    result.put("loggingEnabledForReads", loggingEnabledForReads);
-    return result;
-  }
-
-  syn MappingElement MTokenSendDefinition.toYAML() {
-    MappingElement result = super.toYAML();
-    result.put("sender", sender());
-    result.put("lastValue", lastValue());
-    result.put("loggingEnabledForWrites", loggingEnabledForWrites);
-    result.put("updateMethod", updateMethod());
-    result.put("writeMethod", writeMethod());
-    result.put("tokenResetMethod", tokenResetMethod());
-    return result;
-  }
-
-  syn MappingElement MTypeReceiveDefinition.toYAML() {
-    MappingElement result = super.toYAML();
-    result.put("typeIsList", typeIsList());
-    result.put("loggingEnabledForReads", loggingEnabledForReads);
-    return result;
-  }
-
-  syn MappingElement MTypeSendDefinition.toYAML() {
-    MappingElement result = super.toYAML();
-    result.put("typeIsList", typeIsList());
-    result.put("sender", sender());
-    result.put("lastValue", lastValue());
-    result.put("loggingEnabledForWrites", loggingEnabledForWrites);
-    result.put("updateMethod", updateMethod());
-    result.put("writeMethod", writeMethod());
-    result.put("tokenResetMethod", tokenResetMethod());
-    return result;
-  }
-
-  syn Element MMappingDefinition.toYAML() {
-    MappingElement result = new MappingElement();
-    result.put("toType", toType());
-    result.put("methodName", methodName());
-    result.put("fromType", fromType());
-    result.put("fromVariableName", fromVariableName());
-    result.put("content",
-        content().replace("\"", "\\\"").replace("\n", "\\n"));
-    return result;
-  }
-
-  syn Element MDependencyDefinition.toYAML() {
-    MappingElement result = new MappingElement();
-    result.put("targetParentTypeName", targetParentTypeName());
-    result.put("dependencyMethod", dependencyMethod());
-    result.put("sourceParentTypeName", sourceParentTypeName());
-    result.put("internalRelationPrefix", internalRelationPrefix());
-    return result;
-  }
-
-  syn Element MTokenComponent.toYAML() {
-    MappingElement result = new MappingElement();
-    result.put("parentTypeName", parentTypeName());
-    result.put("name", name());
-    result.put("javaType", javaType());
-    result.put("internalName", internalName());
-    ListElement dependencyDefinitions = new ListElement();
-    for (MDependencyDefinition def : getDependencyDefinitionList()) {
-      MappingElement inner = new MappingElement();
-      inner.put("targetParentTypeName", def.targetParentTypeName());
-      inner.put("internalRelationPrefix", def.internalRelationPrefix());
-      MappingElement targetEndpointDefinition = new MappingElement();
-    targetEndpointDefinition.put("updateMethod", def.targetEndpointDefinition().updateMethod());
-    targetEndpointDefinition.put("writeMethod", def.targetEndpointDefinition().writeMethod());
-      inner.put("targetEndpointDefinition", targetEndpointDefinition);
-      dependencyDefinitions.addElement(inner);
-    }
-    result.put("DependencyDefinitions", dependencyDefinitions);
-    return result;
-  }
-
-  ListElement MEndpointDefinition.innerMappingDefinitionsAsListElement() {
-    ListElement innerMappingDefinitions = new ListElement();
-    for (MInnerMappingDefinition def : getInnerMappingDefinitionList()) {
-      MappingElement inner = new MappingElement();
-      inner.put("toType", def.toType());
-      inner.put("methodName", def.methodName());
-      inner.put("inputVarName", def.inputVarName());
-      inner.put("outputVarName", def.outputVarName());
-      inner.put("last", def.isLast());
-      innerMappingDefinitions.addElement(inner);
-    }
-    return innerMappingDefinitions;
-  }
-
-  syn MappingElement MHandler.toYAML() {
-    MappingElement result = new MappingElement();
-    result.put("ClassName", getClassName());
-    result.put("Construction", getConstruction());
-    result.put("AttributeName", getAttributeName());
-    result.put("FieldName", getFieldName());
-    result.put("InUse", getInUse());
-    return result;
-  }
-}
-
-aspect Navigation {
-  eq Document.getChild().program() = null;
-  eq Document.getChild().ragconnect() = null;
-  eq Document.getChild().containedFile() = null;
-  eq Document.containedFileName() = getFileName();
-}
diff --git a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
index faaf35ad579259148d06fc7d2f8975f740f2730b..bbf0c11b7ba08c1f7c172cf8fd31420e7fd74561 100644
--- a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
+++ b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
@@ -1,68 +1,24 @@
 aspect ParserRewrites {
-  rewrite SendUntypedEndpointDefinition {
+  rewrite UntypedEndpointTarget {
     when (tryGloballyResolveTypeComponentByToken(getTokenOrType()) != null)
-    to SendTypeEndpointDefinition {
-      SendTypeEndpointDefinition result = new SendTypeEndpointDefinition();
-      result.applyFrom(this);
-      result.setIndexBasedListAccess(this.getIndexed());
+    to TypeEndpointTarget {
+      TypeEndpointTarget result = new TypeEndpointTarget();
+      result.setType(TypeComponent.createRef(this.getTokenOrType()));
       return result;
     }
-  }
-
-  rewrite ReceiveUntypedEndpointDefinition {
-    when (tryGloballyResolveTypeComponentByToken(getTokenOrType()) != null)
-    to ReceiveTypeEndpointDefinition {
-      ReceiveTypeEndpointDefinition result = new ReceiveTypeEndpointDefinition();
-      result.applyFrom(this);
-      result.setWithAdd(this.getWithAdd());
-      result.setIndexBasedListAccess(this.getIndexed());
-      return result;
-    }
-  }
-
-  rewrite SendUntypedEndpointDefinition {
-    when (tryGloballyResolveTokenComponentByToken(getTokenOrType()) != null)
-    to SendTokenEndpointDefinition {
-      SendTokenEndpointDefinition result = new SendTokenEndpointDefinition();
-      result.applyFrom(this);
-      return result;
-    }
-  }
-
-  rewrite ReceiveUntypedEndpointDefinition {
     when (tryGloballyResolveTokenComponentByToken(getTokenOrType()) != null)
-    to ReceiveTokenEndpointDefinition {
-      ReceiveTokenEndpointDefinition result = new ReceiveTokenEndpointDefinition();
-      result.applyFrom(this);
+    to TokenEndpointTarget {
+      TokenEndpointTarget result = new TokenEndpointTarget();
+      result.setToken(TokenComponent.createRef(this.getTokenOrType()));
       return result;
     }
   }
 
-  protected void TypeEndpointDefinition.applyFrom(UntypedEndpointDefinition def) {
-    this.setAlwaysApply(def.getAlwaysApply());
-    this.setType(TypeComponent.createRef(def.getTokenOrType()));
-    this.moveMappingsFrom(def);
-  }
-
-  protected void TokenEndpointDefinition.applyFrom(UntypedEndpointDefinition def) {
-    this.setAlwaysApply(def.getAlwaysApply());
-    this.setToken(TokenComponent.createRef(def.getTokenOrType()));
-    this.moveMappingsFrom(def);
-  }
-
-  protected void EndpointDefinition.moveMappingsFrom(UntypedEndpointDefinition def) {
-    // can safely iterate over list as we get an unmodifyable list
-    for (MappingDefinition mapping : def.getMappingList().toArray(new MappingDefinition[0])) {
-      def.removeMapping(mapping);
-      this.addMapping(mapping);
-    }
-  }
-
-  private void UntypedEndpointDefinition.clearMappings() {
-  }
-
-  eq UntypedEndpointDefinition.targetTypeName() = "<unknown>";
-  syn MEndpointDefinition UntypedEndpointDefinition.toMustache() {
-    throw new RuntimeException("UntypedEndpoint can not be transformed using toMustache!");
-  }
+  eq UntypedEndpointTarget.senderName() = "<untyped.senderName>";
+  eq UntypedEndpointTarget.getterMethodName() = "<untyped.getterMethodName>";
+  eq UntypedEndpointTarget.parentTypeName() = "<untyped.parentTypeName>";
+  eq UntypedEndpointTarget.entityName() = "<untyped.entityName>";
+  eq UntypedEndpointTarget.isAlreadyDefined() = false;
+  eq UntypedEndpointTarget.entityIsNormalAttribute() = false;
+  eq UntypedEndpointTarget.targetTypeName() = "<untyped.targetTypeName>";
 }
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 1088ba51ac2de82cc2baaa7b618b0b05d84da4cb..5ddef630253006a13e64ea84cd4df30dc4b6aa2b 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -22,10 +22,20 @@ ConnectSpecificationFile connect_specification_file
   private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) {
     return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator();
   }
-  private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) {
-    def.setAlwaysApply(true);
-    return def;
-  }
+//  private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) {
+//    def.setAlwaysApply(true);
+//    return def;
+//  }
+  private EndpointDefinition createEndpointDefinition(
+    String type_name, String child_name, boolean send,
+    boolean indexBasedListAccess, boolean withAdd) {
+      EndpointDefinition result = new EndpointDefinition();
+      result.setSend(send);
+      result.setIndexBasedListAccess(indexBasedListAccess);
+      result.setWithAdd(withAdd);
+      result.setEndpointTarget(new UntypedEndpointTarget(type_name + "." + child_name));
+      return result;
+    }
 :} ;
 
 EndpointDefinition endpoint_definition
@@ -45,44 +55,27 @@ EndpointDefinition endpoint_definition
 EndpointDefinition endpoint_definition_type
   = SEND ID.type_name DOT ID.child_name
     {:
-      SendUntypedEndpointDefinition result = new SendUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      return result;
-    :}
-  | SEND INDEXED ID.type_name DOT ID.child_name
-    {:
-      SendUntypedEndpointDefinition result = new SendUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      result.setIndexed(true);
-      return result;
+      return createEndpointDefinition(type_name, child_name, true, false, false);
     :}
+//  | SEND INDEXED ID.type_name DOT ID.child_name
+//    {:
+//      return createEndpointDefinition(type_name, child_name, true, true, false);
+//    :}
   | RECEIVE ID.type_name DOT ID.child_name
     {:
-      ReceiveUntypedEndpointDefinition result = new ReceiveUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      return result;
+      return createEndpointDefinition(type_name, child_name, false, false, false);
     :}
   | RECEIVE INDEXED ID.type_name DOT ID.child_name
     {:
-      ReceiveUntypedEndpointDefinition result = new ReceiveUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      result.setIndexed(true);
-      return result;
+      return createEndpointDefinition(type_name, child_name, false, true, false);
     :}
   | RECEIVE WITH ADD ID.type_name DOT ID.child_name
     {:
-      ReceiveUntypedEndpointDefinition result = new ReceiveUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      result.setWithAdd(true);
-      return result;
+      return createEndpointDefinition(type_name, child_name, false, false, true);
     :}
   | RECEIVE INDEXED WITH ADD ID.type_name DOT ID.child_name
     {:
-      ReceiveUntypedEndpointDefinition result = new ReceiveUntypedEndpointDefinition();
-      result.setTokenOrType(type_name + "." + child_name);
-      result.setIndexed(true);
-      result.setWithAdd(true);
-      return result;
+      return createEndpointDefinition(type_name, child_name, false, true, true);
     :}
 ;
 
diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
index 11f73da45e957a008b32fbddc050bb619f58f100..8a1eaec1e8d95c0d8786f9f44168d1f060b27cfa 100644
--- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
+++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
@@ -5,8 +5,6 @@
 "maps"       { return sym(Terminals.MAPS); }
 "to"         { return sym(Terminals.TO); }
 "as"         { return sym(Terminals.AS); }
-//"tree"       { return sym(Terminals.TREE); }
-//"list"       { return sym(Terminals.LIST); }
 "with"       { return sym(Terminals.WITH); }
 "indexed"    { return sym(Terminals.INDEXED); }
 "add"        { return sym(Terminals.ADD); }
diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
index 915d9f92d49cf3a847f05d0b23393bce1832fc1a..b94e0ddf2f97b18721084ae1c26c4687b53b3a3e 100644
--- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
+++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
@@ -20,7 +20,7 @@ import java.util.logging.Logger;
 
 public class Compiler extends AbstractCompiler {
 
-//  private ValueOption optionOutputDir;
+  //  private ValueOption optionOutputDir;
   private ValueOption optionRootNode;
   private ValueOption optionProtocols;
   private BooleanOption optionPrintYaml;
@@ -68,6 +68,7 @@ public class Compiler extends AbstractCompiler {
     }
 
     RagConnect ragConnect = parseProgram(getConfiguration().getFiles());
+    setConfiguration(ragConnect);
 
     if (!ragConnect.errors().isEmpty()) {
       StringBuilder sb = new StringBuilder("Errors:\n");
@@ -79,23 +80,19 @@ public class Compiler extends AbstractCompiler {
     }
 
     if (optionPrintYaml.value()) {
-      ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
-      String yamlContent = ragConnect.toMustache().toYAML().prettyPrint();
-      System.out.println(yamlContent);
+      String yamlContent = ragConnect.toYAML().prettyPrint();
+      LOGGER.fine(yamlContent);
       writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent);
       return 0;
     }
 
     LOGGER.fine("Writing output files");
-    final List<String> handlers = new ArrayList<>();
-    if (ASTNode.usesMqtt) {
-      handlers.add("MqttHandler.jadd");
-    }
-    if (ASTNode.usesRest) {
-      handlers.add("RestHandler.jadd");
-    }
     // copy handlers into outputDir
-    for (String handlerFileName : handlers) {
+    for (Handler handler : ragConnect.getHandlerList()) {
+      if (!handler.getInUse()) {
+        continue;
+      }
+      String handlerFileName = handler.getDefinitionFileName();
       try {
         InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName);
         if (inputStream == null) {
@@ -112,7 +109,8 @@ public class Compiler extends AbstractCompiler {
       Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName());
       writeToFile(outputFile, grammarFile.generateAbstractGrammar());
     }
-    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value()));
+    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"),
+        generateAspect(ragConnect));
     return 0;
   }
 
@@ -128,14 +126,14 @@ public class Compiler extends AbstractCompiler {
 
   /**
    * Reads the version string.
-   *
+   * <p>
    * The version string is read from the property file
    * src/main/resources/Version.properties. This
    * file should be generated during the build process. If it is missing
    * then there is some problem in the build script.
    *
-   * @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
    * @return the read version string, or <code>version ?</code>
+   * @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
    */
   private String readVersion() {
     try {
@@ -158,26 +156,26 @@ public class Compiler extends AbstractCompiler {
     super.initOptions();
     optionRootNode = addOption(
         new ValueOption("rootNode", "root node in the base grammar.")
-        .acceptAnyValue()
-        .needsValue(true));
+            .acceptAnyValue()
+            .needsValue(true));
     optionProtocols = addOption(
         new ValueOption("protocols", "Protocols to enable")
-        .acceptMultipleValues(true)
-        .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
-        .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
+            .acceptMultipleValues(true)
+            .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
+            .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
     );
     optionPrintYaml = addOption(
-        new BooleanOption("printYaml", "Print out YAML instead of generating files")
-        .defaultValue(false));
+        new BooleanOption("printYaml", "Print out YAML instead of generating files and exit.")
+            .defaultValue(false));
     optionVerbose = addOption(
         new BooleanOption("verbose", "Print more messages while compiling.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogReads = addOption(
         new BooleanOption("logReads", "Enable logging for every read.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogWrites = addOption(
         new BooleanOption("logWrites", "Enable logging for every write.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogIncremental = addOption(
         new BooleanOption("logIncremental", "Enable logging for observer in incremental dependency tracking.")
             .defaultValue(false));
@@ -233,20 +231,7 @@ public class Compiler extends AbstractCompiler {
 
     ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration);
     ragConnect.additionalTokens().forEach(TypeDecl::addComponent);
-    ASTNode.loggingEnabledForReads = optionLogReads.value();
-    ASTNode.loggingEnabledForWrites = optionLogWrites.value();
-    ASTNode.loggingEnabledForIncremental = optionLogIncremental.value();
-    ASTNode.experimentalJastAdd329 = optionExperimentalJastAdd329.value();
-
-    // reuse "--incremental" option of JastAdd
-    ASTNode.incrementalOptionActive = getConfiguration().incremental() && getConfiguration().traceFlush();
-    LOGGER.fine(() -> "ASTNode.incrementalOptionActive = " + ASTNode.incrementalOptionActive);
 
-    // reuse "--List" option of JastAdd
-    ASTNode.JastAddList = getConfiguration().listType();
-
-    ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT);
-    ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST);
     return ragConnect;
   }
 
@@ -279,6 +264,7 @@ public class Compiler extends AbstractCompiler {
 
   /**
    * Extracts the basename of the given file, with file extension
+   *
    * @param filename the given filename
    * @return the basename
    */
@@ -286,9 +272,49 @@ public class Compiler extends AbstractCompiler {
     return new File(filename).getName();
   }
 
-//  protected void printUsage() {
-//    System.out.println("Usage: java -jar ragconnect.jar [--option1] [--option2=value] ...  <filename1> <filename2> ... ");
-//    System.out.println("Options:");
-//    System.out.print(commandLine.printOptionHelp());
-//  }
+  private void setConfiguration(RagConnect ragConnect) {
+    ragConnect.setConfiguration(new Configuration());
+    ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value());
+    ragConnect.getConfiguration().setLoggingEnabledForWrites(optionLogWrites.value());
+    ragConnect.getConfiguration().setLoggingEnabledForIncremental(optionLogIncremental.value());
+    ragConnect.getConfiguration().setExperimentalJastAdd329(optionExperimentalJastAdd329.value());
+
+    // reuse "--incremental" and "--trace=flush" options of JastAdd
+    boolean incrementalOptionActive = this.getConfiguration().incremental() && this.getConfiguration().traceFlush();
+    ragConnect.getConfiguration().setIncrementalOptionActive(incrementalOptionActive);
+    LOGGER.fine(() -> "ragConnect.getConfiguration().IncrementalOptionActive = " + incrementalOptionActive);
+
+    // reuse "--List" option of JastAdd
+    ragConnect.getConfiguration().setJastAddList(this.getConfiguration().listType());
+
+    ragConnect.getConfiguration().setRootNode(ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()));
+
+    // Handler ::= <ClassName> <UniqueName> <InUse:boolean>;
+    ragConnect.addHandler(new Handler("MqttHandler.jadd", "MqttServerHandler", "mqtt", optionProtocols.hasValue(OPTION_PROTOCOL_MQTT)));
+    ragConnect.addHandler(new Handler("RestHandler.jadd", "RestServerHandler", "rest", optionProtocols.hasValue(OPTION_PROTOCOL_REST)));
+  }
+
+  public String generateAspect(RagConnect ragConnect) {
+    StringBuilder sb = new StringBuilder();
+    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
+      @Override
+      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
+        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
+          @Override
+          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
+            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
+            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
+              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
+            }
+            return wrapper;
+          }
+        };
+      }
+    };
+    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
+    mf.setObjectHandler(roh);
+    com.github.mustachejava.Mustache m = mf.compile("ragconnect.mustache");
+    m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnect);
+    return sb.toString();
+  }
 }
diff --git a/ragconnect.base/src/main/resources/ListAspect.mustache b/ragconnect.base/src/main/resources/ListAspect.mustache
index 764b5441862c96a010f6d07f86e2c1aad2dec81c..31eaf5ac82a5854956077403e92124c9b6160353 100644
--- a/ragconnect.base/src/main/resources/ListAspect.mustache
+++ b/ragconnect.base/src/main/resources/ListAspect.mustache
@@ -1,5 +1,5 @@
 {{#hasTreeListEndpoints}}
-public void {{JastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
+public void {{configJastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
   try {
     g.writeStartArray();
     for (T child : this) {
@@ -7,13 +7,13 @@ public void {{JastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g
     }
     g.writeEndArray();
   } catch (java.io.IOException e) {
-    throw new SerializationException("unable to serialize {{JastAddList}}", e);
+    throw new SerializationException("unable to serialize {{configJastAddList}}", e);
   }
 }
 
 {{#typesForReceivingListEndpoints}}
-public static {{JastAddList}}<{{Name}}> {{Name}}.deserializeList(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException {
-  {{JastAddList}}<{{Name}}> result = new {{JastAddList}}<>();
+public static {{configJastAddList}}<{{Name}}> {{Name}}.deserializeList(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException {
+  {{configJastAddList}}<{{Name}}> result = new {{configJastAddList}}<>();
   for (java.util.Iterator<com.fasterxml.jackson.databind.JsonNode> it = node.elements(); it.hasNext();) {
     com.fasterxml.jackson.databind.JsonNode element = it.next();
     result.add(deserialize(element));
diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd
index d859d38d819c17df941d4b0179fbeec28df6f83f..0651d5fa84940f16ea6ff2ba1c674b477e509fe8 100644
--- a/ragconnect.base/src/main/resources/MqttHandler.jadd
+++ b/ragconnect.base/src/main/resources/MqttHandler.jadd
@@ -316,11 +316,8 @@ public class MqttHandler {
       needSubscribe = pairToAddTo.callbacks.isEmpty();
       pairToAddTo.callbacks.add(callback);
     } else { // normal topic
-      java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.get(topic);
-      if (callbacksForTopic == null) {
-        callbacksForTopic = new java.util.ArrayList<>();
-        normalCallbacks.put(topic, callbacksForTopic);
-      }
+      java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.
+          computeIfAbsent(topic, t -> new java.util.ArrayList<>());
       needSubscribe = callbacksForTopic.isEmpty();
       callbacksForTopic.add(callback);
     }
@@ -346,7 +343,10 @@ public class MqttHandler {
         });
       });
       try {
-        operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        if (!finishedInTime) {
+          return false;
+        }
         return success.get();
       } catch (InterruptedException e) {
         return false;
@@ -428,7 +428,10 @@ public class MqttHandler {
         });
       });
       try {
-        operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        if (!finishedInTime) {
+          return false;
+        }
       } catch (InterruptedException e) {
         logger.catching(e);
         success.set(false);
diff --git a/ragconnect.base/src/main/resources/dependencyDefinition.mustache b/ragconnect.base/src/main/resources/dependencyDefinition.mustache
index 0ab8d55242816a7d3ada0491c8c7db6dcd70ee42..891935029b5c5345df4dd7546705af347a422a5e 100644
--- a/ragconnect.base/src/main/resources/dependencyDefinition.mustache
+++ b/ragconnect.base/src/main/resources/dependencyDefinition.mustache
@@ -1,3 +1,3 @@
-public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) {
+public void {{targetParentTypeName}}.{{dependencyMethodName}}({{sourceParentTypeName}} source) {
   add{{internalRelationPrefix}}Source(source);
 }
diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache
index fe8d9459a0d1b977f7416da85ba3fb1c4d3e6b8f..37923f4419371cea7bbafe2eab9ad7cb745fe425 100644
--- a/ragconnect.base/src/main/resources/handler.mustache
+++ b/ragconnect.base/src/main/resources/handler.mustache
@@ -1,19 +1,28 @@
 aspect RagConnectHandler {
 {{#Handlers}}
   {{#InUse}}
-  private {{ClassName}} {{rootNodeName}}.{{FieldName}} = {{{Construction}}};
+  private {{ClassName}} {{rootNodeName}}.{{fieldName}} = {{{constructionSnippet}}};
+  {{#hasRootTypeComponents}}inh {{ClassName}} ASTNode.{{attributeName}}();{{/hasRootTypeComponents}}
   {{#rootTypeComponents}}
-  {{#first}}inh {{ClassName}} ASTNode.{{AttributeName}}();{{/first}}
-  eq {{rootNodeName}}.get{{name}}().{{AttributeName}}() = {{FieldName}};
+  eq {{rootNodeName}}.get{{Name}}().{{attributeName}}() = {{fieldName}};
   {{/rootTypeComponents}}
-  syn {{ClassName}} {{rootNodeName}}.{{AttributeName}}() = {{FieldName}};
+  syn {{ClassName}} {{rootNodeName}}.{{attributeName}}() = {{fieldName}};
   {{/InUse}}
 {{/Handlers}}
-  public void {{rootNodeName}}.{{closeMethod}}() {
+  public void {{rootNodeName}}.{{closeMethodName}}() {
     {{#Handlers}}
-    {{#InUse}}{{FieldName}}.close();{{/InUse}}
+    {{#InUse}}{{fieldName}}.close();{{/InUse}}
     {{/Handlers}}
   }
+
+{{#mqttHandler}}
+  {{#InUse}}
+  public void {{rootNodeName}}.{{setupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) {
+    {{fieldName}}.setupWaitUntilReady(time, unit);
+  }
+  {{/InUse}}
+{{/mqttHandler}}
+
   class RagConnectToken {
     static java.util.concurrent.atomic.AtomicLong counter = new java.util.concurrent.atomic.AtomicLong(0);
     final long id;
diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache
index bc59e0b318dcd430b5f21fd0a67d157b35a4bb7c..836e8d438397db9fc8e862c828e5b57eeb34d2f1 100644
--- a/ragconnect.base/src/main/resources/mappingApplication.mustache
+++ b/ragconnect.base/src/main/resources/mappingApplication.mustache
@@ -1,8 +1,8 @@
 {{{lastDefinitionToType}}} {{lastResult}};
 try {
-  {{#InnerMappingDefinitions}}
+  {{#innerMappingDefinitions}}
   {{^last}}{{{toType}}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}});
-  {{/InnerMappingDefinitions}}
+  {{/innerMappingDefinitions}}
 } catch (RagConnectRejectMappingException e) {
   // do not print message in case of rejection
   {{preemptiveReturn}}
@@ -10,8 +10,8 @@ try {
   e.printStackTrace();
   {{preemptiveReturn}}
 }
-{{^alwaysApply}}
+{{^AlwaysApply}}
 if ({{{condition}}}) {
   {{preemptiveReturn}}
 }
-{{/alwaysApply}}
+{{/AlwaysApply}}
diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache
index 09f161f37c3742850b09969d4eb85c7798d2d346..5be93e4bdc91ca2eaa54f364eb8e5f7c069da2a5 100644
--- a/ragconnect.base/src/main/resources/mappingDefinition.mustache
+++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache
@@ -1,3 +1,3 @@
-protected static {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{fromVariableName}}) throws Exception {
-  {{{content}}}
+protected static {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception {
+  {{{Content}}}
 }
diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache
deleted file mode 100644
index f12cb8982443f01dd6195e2c86a0ae55f1485811..0000000000000000000000000000000000000000
--- a/ragconnect.base/src/main/resources/mqtt.mustache
+++ /dev/null
@@ -1,5 +0,0 @@
-aspect MQTT {
-  public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) {
-    {{mqttHandlerField}}.setupWaitUntilReady(time, unit);
-  }
-}
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index dd509aa53b34e56f425777c16f5f9f3d34c2fc7a..c3b50710f38fd247aca8ca11456c23279a0bee8c 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,45 +1,37 @@
-{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
 {{> handler}}
 aspect RagConnect {
-  {{#TokenReceiveDefinitions}}
-  {{> receiveDefinition}}
-  {{/TokenReceiveDefinitions}}
-
-  {{#TokenSendDefinitions}}
-  {{> sendDefinition}}
-  {{/TokenSendDefinitions}}
-
-  {{#TypeReceiveDefinitions}}
-  {{> receiveDefinition}}
-  {{/TypeReceiveDefinitions}}
-
-  {{#TypeSendDefinitions}}
-  {{> sendDefinition}}
-  {{/TypeSendDefinitions}}
+  {{#allEndpointDefinitionList}}
+    {{#Send}}
+      {{> sendDefinition}}
+    {{/Send}}
+    {{^Send}}
+      {{> receiveDefinition}}
+    {{/Send}}
+  {{/allEndpointDefinitionList}}
 
   class RagConnectRejectMappingException extends RuntimeException {}
   private static void ASTNode.reject() {
     throw new RagConnectRejectMappingException();
   }
 
-  {{#MappingDefinitions}}
+  {{#allMappingDefinitions}}
   {{#isUsed}}
   {{> mappingDefinition}}
   {{/isUsed}}
-  {{/MappingDefinitions}}
+  {{/allMappingDefinitions}}
 
-  {{#DependencyDefinitions}}
+  {{#allDependencyDefinitionList}}
   {{> dependencyDefinition}}
-  {{/DependencyDefinitions}}
+  {{/allDependencyDefinitionList}}
 
-  {{#TokenComponents}}
+  {{#tokenComponentsThatNeedProxy}}
   {{> tokenComponent}}
-  {{/TokenComponents}}
+  {{/tokenComponentsThatNeedProxy}}
 
   {{> ListAspect}}
 
   public void {{rootNodeName}}.ragconnectCheckIncremental() {
-  {{#incrementalOptionActive}}
+  {{#configIncrementalOptionActive}}
     // check if --tracing is active
     trace().getReceiver();
     // check if tracing of INC_FLUSH_ATTR is possible, i.e., if --tracing=flush
@@ -48,11 +40,11 @@ aspect RagConnect {
     mayHaveRewrite();
     // check if --incremental is active
     Object checkIncremental = inc_throwAway_visited;
-  {{/incrementalOptionActive}}
+  {{/configIncrementalOptionActive}}
   }
 }
 
-{{#incrementalOptionActive}}
+{{#configIncrementalOptionActive}}
 aspect RagConnectObserver {
 
   class RagConnectObserver implements ASTState.Trace.Receiver {
@@ -71,7 +63,7 @@ aspect RagConnectObserver {
       }
     }
 
-{{#experimentalJastAdd329}}
+{{#configExperimentalJastAdd329}}
     class RagConnectObserverStartEntry {
       final ASTNode node;
       final String attributeString;
@@ -82,16 +74,16 @@ aspect RagConnectObserver {
         this.flushIncToken = flushIncToken;
       }
     }
-{{/experimentalJastAdd329}}
+{{/configExperimentalJastAdd329}}
 
     ASTState.Trace.Receiver oldReceiver;
 
     java.util.List<RagConnectObserverEntry> observedNodes = new java.util.ArrayList<>();
 
-{{#experimentalJastAdd329}}
+{{#configExperimentalJastAdd329}}
     java.util.Set<RagConnectObserverEntry> entryQueue = new java.util.HashSet<>();
     RagConnectObserverStartEntry startEntry = null;
-{{/experimentalJastAdd329}}
+{{/configExperimentalJastAdd329}}
 
     RagConnectObserver(ASTNode node) {
       // set the receiver. potentially dangerous because overriding existing receiver!
@@ -100,9 +92,9 @@ aspect RagConnectObserver {
     }
 
     void add(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
-      {{#loggingEnabledForIncremental}}
+      {{#configLoggingEnabledForIncremental}}
       System.out.println("** observer add: " + node + " on " + attributeString);
-      {{/loggingEnabledForIncremental}}
+      {{/configLoggingEnabledForIncremental}}
       observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall));
     }
     void remove(RagConnectToken connectToken) {
@@ -111,12 +103,12 @@ aspect RagConnectObserver {
     @Override
     public void accept(ASTState.Trace.Event event, ASTNode node, String attribute, Object params, Object value) {
       oldReceiver.accept(event, node, attribute, params, value);
-{{#experimentalJastAdd329}}
+{{#configExperimentalJastAdd329}}
       // react to INC_FLUSH_START and remember entry
       if (event == ASTState.Trace.Event.INC_FLUSH_START && startEntry == null) {
-        {{#loggingEnabledForIncremental}}
+        {{#configLoggingEnabledForIncremental}}
         System.out.println("** observer start: " + node + " on " + attribute);
-        {{/loggingEnabledForIncremental}}
+        {{/configLoggingEnabledForIncremental}}
         startEntry = new RagConnectObserverStartEntry(node, attribute, value);
         return;
       }
@@ -130,37 +122,37 @@ aspect RagConnectObserver {
         RagConnectObserverEntry[] entriesToProcess = entryQueue.toArray(new RagConnectObserverEntry[entryQueue.size()]);
         entryQueue.clear();
         startEntry = null;
-        {{#loggingEnabledForIncremental}}
+        {{#configLoggingEnabledForIncremental}}
         System.out.println("** observer process (" + entriesToProcess.length + "): " + node + " on " + attribute);
-        {{/loggingEnabledForIncremental}}
+        {{/configLoggingEnabledForIncremental}}
         for (RagConnectObserverEntry entry : entriesToProcess) {
           entry.attributeCall.run();
         }
         return;
       }
 
-{{/experimentalJastAdd329}}
+{{/configExperimentalJastAdd329}}
       // ignore all other events but INC_FLUSH_ATTR
       if (event != ASTState.Trace.Event.INC_FLUSH_ATTR) {
         return;
       }
 
-      {{#loggingEnabledForIncremental}}
+      {{#configLoggingEnabledForIncremental}}
       System.out.println("** observer check INC_FLUSH_ATTR event: " + node + " on " + attribute);
-      {{/loggingEnabledForIncremental}}
+      {{/configLoggingEnabledForIncremental}}
       // iterate through list, if matching pair. could maybe be more efficient.
       for (RagConnectObserverEntry entry : observedNodes) {
         if (entry.node.equals(node) && entry.attributeString.equals(attribute)) {
           // hit. call the attribute/nta-token
-          {{#loggingEnabledForIncremental}}
+          {{#configLoggingEnabledForIncremental}}
           System.out.println("** observer hit: " + entry.node + " on " + entry.attributeString);
-          {{/loggingEnabledForIncremental}}
-{{#experimentalJastAdd329}}
+          {{/configLoggingEnabledForIncremental}}
+{{#configExperimentalJastAdd329}}
           entryQueue.add(entry);
-{{/experimentalJastAdd329}}
-{{^experimentalJastAdd329}}
+{{/configExperimentalJastAdd329}}
+{{^configExperimentalJastAdd329}}
           entry.attributeCall.run();
-{{/experimentalJastAdd329}}
+{{/configExperimentalJastAdd329}}
         }
       }
     }
@@ -175,4 +167,4 @@ aspect RagConnectObserver {
     return _ragConnectObserverInstance;
   }
 }
-{{/incrementalOptionActive}}
+{{/configIncrementalOptionActive}}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 2fd0ea21f1dc6072941b99d6bfa6238f1c0f4f90..25dd2a484abd288091f4a501f6fe8e06a90c1f95 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -13,67 +13,67 @@ private int {{parentTypeName}}.{{resolveInListMethodName}}(String topic) {
 
 /**
  * Connects the receive endpoint {{entityName}}.
-{{#typeIsList}}{{#isWithAdd}}
+{{#typeIsList}}{{#WithAdd}}
  * New values are appended to the end of the list.
-{{/isWithAdd}}{{/typeIsList}}
+{{/WithAdd}}{{/typeIsList}}
  * @param {{connectParameterName}} string describing protocol and path as an URI
-{{#typeIsList}}{{#IndexBasedListAccess}}{{^isWithAdd}}
+{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}
  * @param index index of node in list to connect (the list is expected to have enough elements)
-{{/isWithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}
+{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}
  * @return true if connect was successful, false otherwise
  * @throws java.io.IOException if connect failed
  */
-public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}{{#typeIsList}}{{#IndexBasedListAccess}}{{^isWithAdd}}, int index{{/isWithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}, int index{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
     {{> mappingApplication}}
-{{#loggingEnabledForReads}}
+{{#configLoggingEnabledForReads}}
     System.out.println("[Receive] " + {{connectParameterName}} + " -> {{entityName}} = " + {{lastResult}});
-{{/loggingEnabledForReads}}
-{{#isTypeEndpointDefinition}}
+{{/configLoggingEnabledForReads}}
+{{#hasTypeEndpointTarget}}
     {{lastResult}}.treeResolveAll();
  {{#typeIsList}}
   {{^IndexBasedListAccess}}
-   {{#isWithAdd}}
-    {{getterMethod}}().addAll({{lastResult}});
-   {{/isWithAdd}}
-   {{^isWithAdd}}
+   {{#WithAdd}}
+    {{getterMethodName}}().addAll({{lastResult}});
+   {{/WithAdd}}
+   {{^WithAdd}}
     set{{entityName}}({{lastResult}});
-   {{/isWithAdd}}
+   {{/WithAdd}}
   {{/IndexBasedListAccess}}
   {{#IndexBasedListAccess}}
     {{lastResult}}.set{{idTokenName}}(topic);
-   {{#isWithAdd}}
-    {{getterMethod}}().add({{lastResult}});
-   {{/isWithAdd}}
-   {{^isWithAdd}}
+   {{#WithAdd}}
+    {{getterMethodName}}().add({{lastResult}});
+   {{/WithAdd}}
+   {{^WithAdd}}
     set{{entityName}}({{lastResult}}, index);
-   {{/isWithAdd}}
+   {{/WithAdd}}
   {{/IndexBasedListAccess}}
  {{/typeIsList}}
  {{^typeIsList}}
     set{{entityName}}({{lastResult}});
  {{/typeIsList}}
-{{/isTypeEndpointDefinition}}
-{{^isTypeEndpointDefinition}}
+{{/hasTypeEndpointTarget}}
+{{^hasTypeEndpointTarget}}
     set{{entityName}}({{lastResult}});
-{{/isTypeEndpointDefinition}}
+{{/hasTypeEndpointTarget}}
   };
-  return {{internalConnectMethod}}({{connectParameterName}}, consumer);
+  return {{internalConnectMethodName}}({{connectParameterName}}, consumer);
 }
 
-{{#typeIsList}}{{#IndexBasedListAccess}}{{^isWithAdd}}
+{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}
 /**
  * Connects the receive endpoint {{entityName}} using a "wildcard" URI (if supported by the chosen protocol).
  * @param {{connectParameterName}} string describing protocol and path as an URI
  * @return true if connect was successful, false otherwise
  * @throws java.io.IOException if connect failed
 */
-public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
     {{> mappingApplication}}
-{{#loggingEnabledForReads}}
+{{#configLoggingEnabledForReads}}
     System.out.println("[Receive] " + {{connectParameterName}} + " (" + topic + ") -> {{entityName}} = " + {{lastResult}});
-{{/loggingEnabledForReads}}
+{{/configLoggingEnabledForReads}}
     {{lastResult}}.set{{idTokenName}}(topic);
     int resolvedIndex = {{resolveInListMethodName}}(topic);
     if (resolvedIndex == -1) {
@@ -82,29 +82,33 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
       set{{entityName}}({{lastResult}}, resolvedIndex);
     }
   };
-  return {{internalConnectMethod}}({{connectParameterName}}, consumer);
+  return {{internalConnectMethodName}}({{connectParameterName}}, consumer);
 }
-{{/isWithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}
+{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}
 
-private boolean {{parentTypeName}}.{{internalConnectMethod}}(String {{connectParameterName}},
+private boolean {{parentTypeName}}.{{internalConnectMethodName}}(String {{connectParameterName}},
     java.util.function.BiConsumer<String, byte[]> consumer) throws java.io.IOException {
   {{>handleUri}}
   RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
   boolean success;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
-      success = {{mqttHandlerAttribute}}().newConnection(connectToken, consumer);
+      success = {{attributeName}}().newConnection(connectToken, consumer);
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      success = {{restHandlerAttribute}}().newPUTConnection(connectToken, input -> {
+      success = {{attributeName}}().newPUTConnection(connectToken, input -> {
         // TODO wildcard-topic not supported yet
         consumer.accept("", input.getBytes());
       });
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
       success = false;
@@ -115,7 +119,7 @@ private boolean {{parentTypeName}}.{{internalConnectMethod}}(String {{connectPar
   return success;
 }
 
-public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   {{>handleUri}}
   java.util.List<RagConnectToken> connectTokens = connectTokenMap.removeAll(this, true, uri, "{{entityName}}");
   if (connectTokens.isEmpty()) {
@@ -124,14 +128,18 @@ public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameter
   }
   RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
-  {{#usesMqtt}}
-    case "mqtt": disconnectingMethod = {{mqttHandlerAttribute}}()::disconnect;
+  {{#mqttHandler}}
+  {{#InUse}}
+    case "mqtt": disconnectingMethod = {{attributeName}}()::disconnect;
     break;
-  {{/usesMqtt}}
-  {{#usesRest}}
-    case "rest": disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
+    case "rest": disconnectingMethod = {{attributeName}}()::disconnect;
     break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 5cee6a483cda29519905918a505337db7779fce6..3c29e922faf0bc1de3838468886cbc905d97b2dc 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -1,74 +1,82 @@
-private RagConnectPublisher {{parentTypeName}}.{{sender}} = new RagConnectPublisher();
+private RagConnectPublisher {{parentTypeName}}.{{senderName}} = new RagConnectPublisher();
 
-public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}, boolean writeCurrentValue) throws java.io.IOException {
   {{>handleUri}}
   RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
   boolean success;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
-      final MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);
-      final String topic = {{mqttHandlerAttribute}}().extractTopic(uri);
-      {{sender}}.add(() -> {
-        {{#loggingEnabledForWrites}}
-        System.out.println("[Send] {{entityName}} = " + {{getterMethod}}() + " -> " + {{connectParameterName}});
-        {{/loggingEnabledForWrites}}
+      final MqttHandler handler = {{attributeName}}().resolveHandler(uri);
+      final String topic = {{attributeName}}().extractTopic(uri);
+      {{senderName}}.add(() -> {
+        {{#configLoggingEnabledForWrites}}
+        System.out.println("[Send] {{entityName}} = " + {{getterMethodName}}() + " -> " + {{connectParameterName}});
+        {{/configLoggingEnabledForWrites}}
         handler.publish(topic, {{lastValue}});
         }, connectToken);
-      {{updateMethod}}();
+      {{updateMethodName}}();
       if (writeCurrentValue) {
-        {{writeMethod}}();
+        {{writeMethodName}}();
       }
       success = true;
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      success = {{restHandlerAttribute}}().newGETConnection(connectToken, () -> {
-        {{updateMethod}}();
+      success = {{attributeName}}().newGETConnection(connectToken, () -> {
+        {{updateMethodName}}();
         return new String({{lastValue}});
       });
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
       success = false;
   }
   if (success) {
     connectTokenMap.add(this, false, connectToken);
-    {{#incrementalOptionActive}}
-    _ragConnectObserver().add(connectToken, this, "{{getterMethod}}", () -> {
-      if (this.{{updateMethod}}()) {
-        this.{{writeMethod}}();
+    {{#configIncrementalOptionActive}}
+    _ragConnectObserver().add(connectToken, this, "{{getterMethodName}}", () -> {
+      if (this.{{updateMethodName}}()) {
+        this.{{writeMethodName}}();
       }
     });
-    {{/incrementalOptionActive}}
+    {{/configIncrementalOptionActive}}
   }
   return success;
 }
 
-public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   {{>handleUri}}
   java.util.List<RagConnectToken> connectTokens = connectTokenMap.removeAll(this, false, uri, "{{entityName}}");
   if (connectTokens.isEmpty()) {
     System.err.println("Disconnect called without connection for sending " + this + ".{{entityName}} to '" + {{connectParameterName}} + "'!");
     return false;
   }
-  {{#incrementalOptionActive}}
+  {{#configIncrementalOptionActive}}
   connectTokens.forEach(token -> _ragConnectObserver().remove(token));
-  {{/incrementalOptionActive}}
+  {{/configIncrementalOptionActive}}
   RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
-      disconnectingMethod = {{sender}}::remove;
+      disconnectingMethod = {{senderName}}::remove;
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
+      disconnectingMethod = {{attributeName}}()::disconnect;
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
@@ -80,16 +88,16 @@ public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameter
   return success;
 }
 
-protected boolean {{parentTypeName}}.{{updateMethod}}() {
+protected boolean {{parentTypeName}}.{{updateMethodName}}() {
   {{^shouldSendValue}}
-  {{tokenResetMethod}}();
+  {{tokenResetMethodName}}();
   {{/shouldSendValue}}
   {{> mappingApplication}}
   {{lastValue}} = {{lastResult}};
-  // normally we would return true here. unless no connect method was called so far to initialize {{sender}} yet
-  return {{sender}} != null;
+  // normally we would return true here. unless no connect method was called so far to initialize {{senderName}} yet
+  return {{senderName}} != null;
 }
 
-protected void {{parentTypeName}}.{{writeMethod}}() {
-  {{sender}}.run();
+protected void {{parentTypeName}}.{{writeMethodName}}() {
+  {{senderName}}.run();
 }
diff --git a/ragconnect.base/src/main/resources/tokenComponent.mustache b/ragconnect.base/src/main/resources/tokenComponent.mustache
index 169f5f64fff01ef2c34abeea34266b5a82deaf3a..3b0ae62080f39f17a329a34caeb410da66365f5d 100644
--- a/ragconnect.base/src/main/resources/tokenComponent.mustache
+++ b/ragconnect.base/src/main/resources/tokenComponent.mustache
@@ -1,22 +1,22 @@
-public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) {
+public {{parentTypeName}} {{parentTypeName}}.set{{Name}}({{javaType}} value) {
   set{{internalName}}(value);
-  {{#DependencyDefinitions}}
+  {{#DependencySourceDefinitions}}
   for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
     {{#targetEndpointDefinition}}
-    if (target.{{updateMethod}}()) {
-      target.{{writeMethod}}();
+    if (target.{{updateMethodName}}()) {
+      target.{{writeMethodName}}();
     }
     {{/targetEndpointDefinition}}
   }
-  {{/DependencyDefinitions}}
+  {{/DependencySourceDefinitions}}
   {{#normalTokenSendDef}}
-  if ({{updateMethod}}()) {
-    {{writeMethod}}();
+  if ({{updateMethodName}}()) {
+    {{writeMethodName}}();
   }
   {{/normalTokenSendDef}}
   return this;
 }
 
-public {{javaType}} {{parentTypeName}}.get{{name}}() {
+public {{javaType}} {{parentTypeName}}.get{{Name}}() {
   return get{{internalName}}();
 }
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index ee6da58c40c1bb690258d3958ac53a6ec44ac362..cfeb6ae4680eca67212c256e3e39debc514609c7 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -9,7 +9,7 @@ buildscript {
     }
     dependencies {
         classpath 'org.jastadd:jastaddgradle:1.13.3'
-        classpath 'org.jastadd.preprocessor:testing:0.2.10'
+        classpath 'org.jastadd.preprocessor:testing:0.2.11'
     }
 }
 
@@ -32,11 +32,14 @@ group = 'de.tudresden.inf.st'
 repositories {
     mavenCentral()
 }
+tasks.compileTestJava {
+    options.release.set(11)
+}
 
 dependencies {
     implementation project(':ragconnect.base')
 
-    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
+    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5-dresden'
 //    runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs')
 
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0'
@@ -61,6 +64,12 @@ dependencies {
     api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0'
 }
 
+java {
+    toolchain {
+        languageVersion.set(JavaLanguageVersion.of(11))
+    }
+}
+
 //task helper {
 //    doLast {
 //        println(defaultOnlyRead.inputs.files.files)
@@ -278,7 +287,6 @@ task compileIncremental(type: RagConnectTest) {
         inputFiles = [file('src/test/01-input/incremental/Test.relast'),
                       file('src/test/01-input/incremental/Test.connect')]
         rootNode = 'A'
-        logWrites = true
     }
     relast {
         useJastAddNames = true
@@ -303,9 +311,6 @@ task compileMapping(type: RagConnectTest) {
         inputFiles = [file('src/test/01-input/mapping/Test.relast'),
                       file('src/test/01-input/mapping/Test.connect')]
         rootNode = 'A'
-        logReads = true
-        logWrites = true
-        verbose = true
     }
     relast {
         useJastAddNames = true
@@ -532,6 +537,7 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc
         inputFiles = [file('src/test/01-input/singleListVariant/Test.relast'),
                       file('src/test/01-input/singleListVariant/Test.connect')]
         rootNode = 'Root'
+        extraOptions = ['--experimental-jastadd-329']
     }
     relast {
         useJastAddNames = true
diff --git a/ragconnect.tests/src/test/01-input/errors/Part.expected b/ragconnect.tests/src/test/01-input/errors/Part.expected
index 70ea4a4baeaffb5b35df672c03e90821eb3cec82..86bb6dd0d9cc23865031bdcf89ba71c441f85a7f 100644
--- a/ragconnect.tests/src/test/01-input/errors/Part.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Part.expected
@@ -1,11 +1,11 @@
-Part1.connect Line 3, column 1: Receive definition already defined for DoubledValue
-Part1.connect Line 4, column 1: Receive definition already defined for DoubledValue
+Part1.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
+Part1.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
 Part1.connect Line 10, column 1: Receiving target token must not be an NTA token!
 Part1.connect Line 13, column 1: No suitable default mapping found for type java.util.List
-Part1.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the Token (String)!
-Part1.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the Token (String)!
-Part1.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the Token (String)!
-Part2.connect Line 5, column 1: Send definition already defined for DoubledValue
-Part2.connect Line 6, column 1: Send definition already defined for DoubledValue
+Part1.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
+Part1.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
+Part1.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
+Part2.connect Line 5, column 1: Endpoint definition already defined for C.DoubledValue
+Part2.connect Line 6, column 1: Endpoint definition already defined for C.DoubledValue
 Part2.connect Line 17, column 1: The name of a dependency definition must not be equal to a list-node on the source
 Part2.connect Line 22, column 1: Dependency definition already defined for D with name DoubledValue
diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.expected b/ragconnect.tests/src/test/01-input/errors/Standard.expected
index 3ee660570525834a63c41f26820966a7504f7789..2e14612cc9fc4d7ffae435745e8b8593a22258d5 100644
--- a/ragconnect.tests/src/test/01-input/errors/Standard.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Standard.expected
@@ -1,11 +1,11 @@
-Standard.connect Line 3, column 1: Receive definition already defined for DoubledValue
-Standard.connect Line 4, column 1: Receive definition already defined for DoubledValue
+Standard.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
+Standard.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
 Standard.connect Line 10, column 1: Receiving target token must not be an NTA token!
 Standard.connect Line 13, column 1: No suitable default mapping found for type java.util.List
-Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the Token (String)!
-Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the Token (String)!
-Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the Token (String)!
-Standard.connect Line 39, column 1: Send definition already defined for DoubledValue
-Standard.connect Line 40, column 1: Send definition already defined for DoubledValue
+Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
+Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
+Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
+Standard.connect Line 39, column 1: Endpoint definition already defined for C.DoubledValue
+Standard.connect Line 40, column 1: Endpoint definition already defined for C.DoubledValue
 Standard.connect Line 51, column 1: The name of a dependency definition must not be equal to a list-node on the source
 Standard.connect Line 56, column 1: Dependency definition already defined for D with name DoubledValue
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
index 174bea2923e2977c93a058dc1408113276e47382..0bd6bcf7218b32f4737716380a5239d178ecdbce 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
@@ -1,6 +1,7 @@
 package org.jastadd.ragconnect.tests.singleListVariant;
 
 import org.jastadd.ragconnect.tests.TestUtils;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Tag;
 import singleListVariantInc.ast.*;
 
@@ -16,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  * @author rschoene - Initial contribution
  */
 @Tag("Incremental")
+@Disabled("Fails indeterministically")
 public class SingleListVariantIncrementalVariantTest extends AbstractSingleListVariantTest {
 
   private Root model;