diff --git a/.gitignore b/.gitignore
index 58eae60023b9f2e2e18e81d6f2a940735a0d68a5..ba2a5fce5df810bbe49706397f5f1d473bcaadc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,8 @@ Makefile
 .version
 maestro-core-*/
 maestro-core-*.tar.gz
+maestro-*/
+maestro-*.tar.gz
 *\#
 TAGS
 tags
@@ -104,3 +106,4 @@ tests/check_subscribe_local
 tests/subscribe-archiver.c
 tests/check_cdo_selectors
 /tests/check_memlock
+MaestroConfig.cmake
diff --git a/INSTALL.md b/INSTALL.md
index 579de21e9a30d2aad1a365f219743c42f8812433..b0bee5d6746b3ea9f848c93742ba33540d75f2e9 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,60 +1,124 @@
 
-Building:
----------
+# Prerequisites
+You will need a `C compiler` capable of understanding `C11`, as well as `posix threads`. 
 
-Prerequisites: You will need a C compiler capable of understanding C11, as well
-as posix threads. 
+Also, you will need `autoreconf` and `libtool` for configuring Maestro build system.
 
-We are including libyaml, libcyaml and libfabric as subtrees in our tree
-and build these versions automatically with our make rules.
 
-By default, the OFI conductor will be built. To enable the experimental other variants 
-`--enable-mpi-conductor` or `--enable-smp-conductor` at configure time (INCOMPLETE).
+deps/c-timestamp
+deps/libcyaml
+deps/libfabric
+deps/libyaml
+deps/mamba
+deps/mio
+deps/protobuf
+deps/protobuf-c
+
+
+
+#### Subtree projects and updates
+
+We are including a number of external dependency libraries as subtrees in our tree under `./deps/`
+and build these versions automatically with our make rules. 
+Maestro uses the libraries it built from the subtrees and not the versions of the libraries on your system.
+
+
+| Subtree      | Remote branch                           |
+| :------------|:----------------------------------------| 
+| c-timestamp  | git@github.com:chansen/c-timestamp.git  |
+| libcyaml     | git@github.com:tlsa/libcyaml.git master |  
+| libfabric    | git@github.com:ofiwg/libfabric.git v1.11.x |  
+| libyaml      | git@github.com:yaml/libyaml.git master  |
+| mamba        | git@gitlab.com:cerl/mamba.git master    |
+| mio          | git@gitlab.version.fz-juelich.de:10022/maestro/mio.git master|
+| protobuf     | git@github.com:protocolbuffers/protobuf.git 3.10.x |
+| protobuf-c   | git@github.com:protobuf-c/protobuf-c.git next |
+
+
+
+To update a dependency project (e.g. mamba) subtree please do 
+
+```
+git subtree pull --prefix=deps/mamba --squash git@gitlab.com:cerl/mamba.git master
+```
+
+
+# Building
+
+## For CRAY XC systems
+Please load the following modules before configuring/building, to ensure `libfabric` will be built properly.
 
-For CRAY XC systems you should do
 	module load rdma-credentials
 	module load gni-headers
-before configuring/building, to ensure libfabric will be built properly.
 
-We are also including Version 0.1.0 of the Mamba library in deps/mamba, and a snapshot of MIO in deps/mio.
+#### jupiter system
+Do not use one of the `craype-network-ofi\*` modules at this time.
+`libfabric` builds nicely with CCE 9.0.
 
-To use MIO you need to add the `--with-mio` flag at configure time, and have MERO installed on the system.
+### sage prototype
+Please load the following modules before configuring/building.
+
+```
+module use $CLIENT_MOD_PATH
+module swap gnu GCC/9.3.0
+module load Autotools git binutils pkg-config libreadline
+```
+
+## Calling autoreconf
 
-Then
 ```
 autoreconf -ivf
-./configure # CPPFLAGS="-I/path/to/ofi/include" LDFLAGS="-L/path/to/ofi/lib" 
-make
 ```
 
-Depending on which branch you build, the default configure argumenst may include options to enable the
-gcc/llvm address sanitizer. To disable, please configure with
+## Configuring
+
 ```
---disable-asan
+./configure
 ```
-If you have the address sanitizer enabled, that may (depending on the compiler) also turn on the leak checker, and since 
-we are not fully memory leak-free at this point may trigger exit-time failures. To disable it, set the environment variable
+
+or specify the install directory, e.g. 
+
 ```
-LSAN_OPTIONS=detect_leaks=0
+./configure  --prefix=$HOME/maestro
 ```
 
+#### For CRAY systems
+Be sure to use `CC=cc` on the configure line.
+
+### Configuration options 
 
-Cray-specific (jupiter)
------------------------
-Do not use one of the craype-network-ofi\* modules at this time.
-Be sure to use CC=cc on the configure line.
-libfabric builds nicely with CCE 9.0.
+By default, the OFI conductor will be built. To enable the experimental other variants 
+`--enable-mpi-pool-manager` or `--enable-smp-pool-manager` at configure time (INCOMPLETE).
+
+We are also including Version 0.1.0 of the Mamba library in deps/mamba, and a snapshot of MIO in deps/mio.
+
+To use MIO you need to add the `--with-mio` flag at configure time, and have MERO installed on the system.
 
-Subtree project updates
------------------------
-To update a dependency project (like mamba) subtree do 
+Depending on which branch you build, the default configure arguments may include options to enable the
+gcc/llvm address sanitizer. To disable, please configure with `--disable-asan`
 
+
+## Build 
+
+Please use 
+
+``` 
+make
 ```
-git subtree pull --prefix=deps/mamba --squash git@gitlab.com:cerl/mamba.git master
+
+If you have the address sanitizer enabled, that may (depending on the compiler) also turn on the leak checker. Since 
+we are not fully memory leak-free at this point, it may trigger exit-time failures. To disable it, set the environment variable
+`LSAN_OPTIONS=detect_leaks=0`
+
+## Install
+
+Please use
+
+```
+make install
 ```
 
-CI
---
+# Continuous integration 
 
 We are running gitlab-CI on the master, devel, and mvp branches.
 If you notice issues you can run the gitlab CI locally using docker and the gitlab-runner tool.
diff --git a/MaestroConfig.cmake.in b/MaestroConfig.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..7e1e7f7e507fc53b8e4dbf909692667dea279b5c
--- /dev/null
+++ b/MaestroConfig.cmake.in
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2021 HPE Computer GmbH
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+set(PREFIX @prefix@)
+
+set(Maestro_INCLUDE_DIRS ${PREFIX}/include/maestro)
+set(Maestro_LIBRARIES ${PREFIX}/lib/libmaestro.a)
diff --git a/Makefile.am b/Makefile.am
index 6362694845fdd2fe933c805726a729822e103c11..5f3bba5ee1f2c1dea634bf460ca1a597f5172617 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,13 +33,13 @@ ACLOCAL_AMFLAGS=-I m4
 
 # automatic version number generation from git version
 # If you want to force updating the version string you need to run
-# 	autoconf -f 
+# 	autoconf -f
 # in the toplevel dir. We don't do this all the time automatically to
 # save development time. In a fresh git checkout the initial autoreconf does
 # the right thing. Of course, building a release should thus be done in a clean
 # checkout.
 BUILT_SOURCES = $(top_srcdir)/.version
-EXTRA_DIST = $(top_srcdir)/.version
+EXTRA_DIST = $(top_srcdir)/.version examples
 $(top_srcdir)/.version:
 	echo $(VERSION) > $@-t && mv $@-t $@
 dist-hook:
@@ -54,7 +54,7 @@ maestro: deps
 tests: maestro
 
 lib_LTLIBRARIES = libmaestro.la
-libmaestro_la_SOURCES = 
+libmaestro_la_SOURCES =
 libmaestro_la_LIBADD = 			 \
 	maestro/libmaestro_core.la    \
 	protocols/libmaestro_proto.la \
@@ -65,6 +65,10 @@ libmaestro_la_LIBADD = 			 \
 # README
 dist_doc_DATA = README.md
 
+# CMake package
+cmakepackagedir = $(libdir)/cmake/maestro/
+cmakepackage_DATA = MaestroConfig.cmake
+
 if WITH_FREQUENT_TAGS
 all-local: TAGS tags
 endif
diff --git a/README.md b/README.md
index 78063c06c2f1e2ae97a7b82b04cd32f88ed179b4..9802421e1b0f041c8e0c7785108487831afcc086 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,61 @@
-Maestro Core
-------------
+# Description
 
-This repository contains the Maestro Core Library, as developed for D3.2.
-It features the Maestro Core API, used by example code and a MVP demonstrator.
+Maestro is a data- and memory-aware middleware framework that addresses the ubiquitous problems of data movement in complex memory hierarchies that exist at multiple levels of the HPC software stack.
 
-Installation
-------------
+<img alt="Maestro architecture overview image" src="docs/maestro-arch-overview.png">
 
-Please refer to INSTALL.md
 
-Examples
---------
+This repository contains the Maestro Core Library, as developed for `D3.2`.
+It features the Maestro Core API, used by example codes and a MVP demonstrator.
+
+# Installation
+
+Please refer to [INSTALL.md](INSTALL.md)
+
+# Usage
+Maestro can be executed on various sizes and types of machines from a simple laptop to large HPC clusters. 
+On Cray systems, please build all binaries on the service nodes (login nodes) and execute on compute nodes.
+
+## Access an installed Maestro version 
+
+Please include the main `Maestro header` file in your code
+```
+#include "maestro.h"
+```
+
+Please add the `include path` and `library path` of Maestro to the compilation/linking command
+```
+-I$(MAESTRO_PATH)/include/maestro -L$(MAESTRO_PATH)/lib -lmaestro
+```
+Please `export` the path to Maestro library before running 
 
-Please use 
+```
+export LD_LIBRARY_PATH=$(MAESTRO_PATH)/lib:$LD_LIBRARY_PATH
+```
+
+where `$(MAESTRO_PATH)` is Maestro install path specified during configuration with `./configure  --prefix=$(MAESTRO_PATH)`
+
+## Unit tests
+
+Build the unit tests only
+```
+make check TESTS=
+```
+
+Run the unit tests only
+```
+make check TESTS
+```
+
+Build and run the unit tests. This may take some time
 ```
 make check
 ```
-to build and run the test examples.
-This may take some time.
 
-Limits
-------
 
-Maestro-core needs quite a few file descriptors and also wants to lock pages
+## Limits
+
+Maestro requires quite a few file descriptors and also locks pages
 into memory for RDMA purposes.  We try to give a diagnostic message if errors
 are triggered that may be due to resource constraints. Still, we recommend
 
@@ -33,72 +66,63 @@ ulimit -l 256
 
 to set at least 1024 file descriptors and 256k of RDMA space.
 
+When using a system that uses `PBSPro` or `ALPS` workload manager, please export 
 
-Local multithreaded demo (MVP1)
--------------------------------
-
-MVP1 consists in a local multithreaded demo application. More reading (d3.2) here :
-`https://bscw.zam.kfa-juelich.de/bscw/bscw.cgi/2995531`
-
-Reference version is tagged d3.2-draft, on master branch.
-
-`make check` also builds the demo executable `demo_mvp_d3_2` in addition to examples, and runs it.
-`./run_demo.sh` permits to run the demo alone.
-
-
-Adaptive Transport demo
------------------------
+```
+export APRUN_XFER_LIMITS=1
+```
+before submiting your job to ensure enough limits for Maestro on the compute nodes. 
 
-Pool manager interlock demo uses a three application setup, comprising one pool
-manager process, and showing GFS and MIO transport. More reading (d5.5 to appear on BSCW) and
-information on how to setup a VM to run Mero here:
-`https://gitlab.version.fz-juelich.de/maestro/maestro-mero-vm`
+## Fabric provider choice/ High-Performance interconnect usage
 
-Reference version is tagged d5.5-review, on master branch.
+Maestro isolates the user from the multitude of network provider choices by using 
+`libfabric`, and transparently choosing 'the best' connectivity between components. Unfortunately this
+functionality is not fully working, due to issues in the upstream `libfabric` code, and in incomplete testing
+of our usage of it.
 
-The pool manager interlock demonstration `./tests/check_pm_interlock.sh` is
-automatically launched with make check.
+Please use
 
+```
+export FI_PROVIDER=provider
+```
+to force a specific fabric provider.
 
-fabric provider choice/ High-Performance Interconnect usage
------------------------------------------------------------
+### List of supported providers
 
-Maestro-core is trying hard to isolate the user from the multitude of network provider choices by using 
-libfabric, and transparently choosing 'the best' connectivity between components. Unfortunately this
-functionality is not fully working, due to issues in the upstream libfabric code, and in incomplete testing
-of our usage of it.
 
-The safest (and lowest performance) connectivity is provided by the `sockets` provider. You can force usage of 
-that by setting
+| Provider      | Support                           |
+| :------------ |:----------------------------------| 
+| sockets       | TCP/IP networking                 |
+| verbs         | Infiniband networks and slingshot |  
+| gni           | Cray Areis networks               |  
+| psm2          | Intel Omni-Path networks          |
 
+ 
+ 
+You can execute
 ```
-FI_PROVIDER=sockets
+./Maestro-source-dir/deps/libfabric/util/fi_info
 ```
+to see all the discovered providers by libfabrics, where `Maestro-source-dir` is the directory containing Maestro source. 
+
+The safest (and lowest performance) connectivity is provided by the `sockets` provider. It should work on almost any network that can support TCP/IP networking, including ethernet, IB, and GNI (Aries).
 
-in your environment. It should work on most any network that can support TCP/IP
-networking, including ethernet, IB, and GNI (Aries).
 
 Usage of the `tcp` and `tcp;ofi_rxm` provider is currently broken, an upstream
 issue is open.
 
 On Cray XC systems the GNI (Aries) provider is supported. If you compile with
-the `rdma-credentials` and `gni-headers` modules loaded the GNI provider should
+the `rdma-credentials` and `gni-headers` modules loaded, the GNI provider should
 be autoselected if a GNI NIC is found at runtime. 
 
 NOTE that GNI NICs on login nodes typically do not work, due to a limitation of
-the libfabric/gni driver, so you will have to run your application exclusively
+the `libfabric/gni` driver, so you will have to run your application exclusively
 on compute nodes, or manually switch the components running on login nodes to
 the sockets provider.
 
-The GNI driver can be forced by setting
 
-```
-FI_PROVIDER=gni
-```
-
-
-If you are using GNI you will implicitly be using Cray libdrc, a mechanism to obtain network
-authentication tokens. Maestro-core is requesting workflow-level tokens that even
+If you are using GNI you will implicitly be using Cray `libdrc`, a mechanism to obtain network
+authentication tokens. Maestro core is requesting workflow-level tokens that even
 support running multiple components of a workflow from different user IDs. In
 some cases the system may run out of tokens, and there is no user-level token
 inquiry tool available. If you see failure of GNI startup, try running your application with
@@ -115,20 +139,86 @@ LIBDRC:CORE:DEBUG        rdmacred.c:658 - finished acquire request, rc=-28
 
 If you see this, contact your system admin to clear cached DRC credentials.
 
+## Examples
+
+Examples can be found in `/examples` directory. It includes currently one simple example, `single_node_pool_op.c`. It is a multi-threaded application (pthread) consists of a producer thread, and two consumer threads. `single_node_pool_op.c` is based on d3.2 of Maestro project, more reading (d3.2) [here](https://bscw.zam.kfa-juelich.de/bscw/bscw.cgi/2995531). 
+
+To build the example, please use
+
+```
+make MAESTRO=$(MAESTRO_PATH)
+```
+
+Example paramters, such as `num_producers`, `num_consumers`, `num_archivers`, `cdo_size`, and `cdo_count`, can be configured using `single_node_pool_op_config.yaml` file.
+
+Before executing the binary, please `export LD_LIBRARY_PATH=$(MAESTRO_PATH)/lib:$LD_LIBRARY_PATH` and then
+
+```
+./single_node_pool_op.o
+```
+## Demos
+
+### Local multithreaded demo (MVP1)
+
+MVP1 consists in a local multithreaded demo application. More reading (d3.2) [here](https://bscw.zam.kfa-juelich.de/bscw/bscw.cgi/2995531) 
+
+Reference version is tagged `d3.2-draft`, on master branch.
+
+`make check` also builds the demo executable `demo_mvp_d3_2` in addition to examples, and runs it.
+`./run_demo.sh` permits to run the demo alone.
+
+
+### Adaptive Transport demo
+
+
+Pool manager interlock demo uses a three application setup, comprising one pool
+manager process, and showing GFS and MIO transport. More reading (`d5.5` to appear on BSCW) and
+information on how to setup a VM to run Mero [here](https://gitlab.version.fz-juelich.de/maestro/maestro-mero-vm)
+
+Reference version is tagged `d5.5-review`, on master branch.
+
+The pool manager interlock demonstration [check_pm_interlock.sh](./tests/check_pm_interlock.sh) is
+automatically launched with make check.
 
-Documentation
--------------
 
-Doxygen documentation is available and compiled in `./docs` folder.
+# Documentation
 
-Common issues/FAQs
-------------------
+Doxygen documentation is available and compiled in [docs](./docs) folder.
+
+# Common issues/FAQs
 
 * If you have many network interfaces/many addresses assigned to an interface
-  (may happen with IPv6 rather suddenly) the libfabric setup of the pool
-  manager may hit 'too many open files/errno=-24' issues. Check `ulimit -n`,
+  (may happen with `IPv6` rather suddenly) the `libfabric` setup of the pool
+  manager may hit `too many open files/errno=-24` issues. Check `ulimit -n`,
   and increase the limit.
 
-* If you see clients stuck at JOIN time while everything else looks good, there
+* If you see clients stuck at `JOIN` time while everything else looks good, there
   is a chance that your firewalling intercepts the packages.
 
+  
+* ##### Is it safe to invalidate the pointer to data wrapped by an offered cdo, given that ownership has passed to maestro core? (We keep hold onto the cdo handle itself of course.) 
+   No, the allocation that was captured in the cdo handle must not be touched until after DISPOSE. Of course you can forget the pointer you have, but you must not re-use the allocation or free it.
+
+* ##### Does a producer have to check whether an offered cdo has been consumed (DEMANDed), and wait until it is, before calling (withdraw followed by) dispose?
+   A producer cannot directly figure that out (unless you do complicated event ops). The idea is: The consumer must submit the REQUIRE before the WITHDRAW occurs. This can be accomplished by
+      1. pre-posting the REQUIRE, or
+      2. by posting it after observing an OFFER:before or OFFER:after event (for safety with a 'require-ack' flag or any earlier event, like DECLARE or SEAL),
+      3. by posting it in a WITHDRAW:before with require-ack set.
+   In all these cases Maestro will ensure that the REQUIRE can be satisfied, by taking a copy (more or less eagerly, this is to be tuned], or by blocking WITHDRAW.
+
+* ##### Should withdraw be needed at all if an offered cdo is consumed by another application?
+  Every OFFER must be followed by WITHDRAW (and DISPOSE); every REQUIRE must be followed by RETRACT or DEMAND (and DISPOSE). Remember that one OFFER can satisfy many REQUIRES for the same CDO; WITHDRAW indicates that you're no longer ready to do so (and maestro needs to ensure outstanding REQUIRES can still be satisfied if their DEMAND comes in) 
+
+* ##### Should dispose block until an offered cdo is consumed by another? Or will it only block if there has already been a require posted?
+   WITHDRAW may block if maestro decides that it cannot or does not want to take a copy and there is at least one outstanding REQUIRE for the CDO, or a DEMAND is still in progress. DISPOSE should never block (but may take some time -- but not related to the pool protocol).
+
+
+# Authors and acknowledgment
+
+[Data Orchestration in High Performance Computing](https://www.maestro-data.eu/) project has received funding from the European Union’s Horizon 2020 research and innovation program through grant agreement  801101.
+
+# License
+
+[BSD 3-clause License](https://gitlab.com/cerl/maestro/maestro-core/-/blob/devel/LICENSE)
+
+
diff --git a/attributes/maestro-schema.c b/attributes/maestro-schema.c
index eb7db7b9ffd700ff7178db840caba1575ab50269..ac87c0d3e79c38504f2614a78575c289afd29493 100644
--- a/attributes/maestro-schema.c
+++ b/attributes/maestro-schema.c
@@ -730,10 +730,12 @@ mstro_attribute_val__compute_size(enum mstro_stp_val_kind kind,
       *val_size = sizeof(double); break;
     case MSTRO_STP_STR:
     case MSTRO_STP_REGEX: 
-      if(string)
+      if(string!=NULL)
         *val_size = strlen(string)+1;
-      else
+      else {
+	assert(val!=NULL);
         *val_size = strlen((char*)val)+1;
+      }
       break;
     case MSTRO_STP_TIMESTAMP:
       *val_size = sizeof(mstro_timestamp);
@@ -2516,7 +2518,8 @@ mstro_attribute_dict_set(mstro_attribute_dict dict, const char *key,
   }
   
   if(copy_value) {
-    status = mstro_attribute_val__compute_size(entry->kind, NULL, val, &entry->valsize);
+    status = mstro_attribute_val__compute_size(entry->kind, NULL,
+                                               val, &entry->valsize);
     if(status!=MSTRO_OK) {
       ERR("Cannot compute value size\n");
       goto BAILOUT;
@@ -2709,7 +2712,18 @@ mstro_attribute_entry_to_mapentry(const struct mstro_attribute_entry_ *entry,
     case MSTRO_STP_STR:
     case MSTRO_STP_REGEX:
       res->val->val_case = MSTRO__POOL__AVAL__VAL_STRING;
-      res->val->string = (char *)entry->val;
+      /* we need to duplicate strings, since the deallocation of
+       * protobuf messages descends into char* members, and so the
+       * entry in the dictionary where the entry is will be invalid
+       * when the message is deallocated (or vice versa) */
+      res->val->string = strdup((char *)entry->val);
+       if(res->val->string==NULL) {
+         ERR("Failed to allocat string value for KV entry\n");
+         free(res->val);
+         free(res);
+         s=MSTRO_NOMEM;
+         goto BAILOUT;
+       }
       break;
     case MSTRO_STP_BLOB:
       res->val->val_case = MSTRO__POOL__AVAL__VAL_BYTES;
diff --git a/build-envs/README b/build-envs/README
index 234f7a826822a05acc1bf23e885adc30771bf6ba..e48c4ffd8d92db5ad508ee37fa278c4cdf285b75 100644
--- a/build-envs/README
+++ b/build-envs/README
@@ -25,6 +25,13 @@ Running things by hand
 
   $ docker pull registry.gitlab.com/cerl/maestro/maestro-core/buildenv/debian
   $ docker run --ulimit memlock=133000:133000 -i -t registry.gitlab.com/cerl/maestro/maestro-core/buildenv/debian /bin/bash 
+
+  # but since you're reading this, you may be interested in debugging. In that
+  # case, to permit gdb to work inside the docker container you might want to
+  # use
+  #  docker run --cap-add=SYS_PTRACE --ulimit memlock=133000:133000 -i -t registry.gitlab.com/cerl/maestro/maestro-core/buildenv/debian /bin/bash
+  # instead
+
   # now you have a shell in the docker container that is currently used on gitlab for CI
   $ mkdir /builds
   $ cd /builds
diff --git a/build/clang/Makefile b/build/clang/Makefile
deleted file mode 100644
index 907900239586445b6d24a684d690bc33a8d3785b..0000000000000000000000000000000000000000
--- a/build/clang/Makefile
+++ /dev/null
@@ -1,51 +0,0 @@
-CC=clang
-CFLAGS_D=-O0 -g2 -Wall
-CFLAGS_R=-O2 -Wall -DNDEBUG
-LDFLAGS_D=
-LDFLAGS_R=
-
-SRCDIR=../../src
-TMPDIR_D=tmp/debug
-TMPDIR_R=tmp/release
-BINDIR_D=bin/debug
-BINDIR_R=bin/release
-
-EXAMPLES:=$(patsubst $(SRCDIR)/examples/%.peg,examples/%,$(wildcard $(SRCDIR)/examples/*.peg))
-
-BINS= \
-  $(BINDIR_D)/packcc \
-  $(BINDIR_R)/packcc \
-  $(patsubst %,$(BINDIR_D)/%,$(EXAMPLES)) \
-  $(patsubst %,$(BINDIR_R)/%,$(EXAMPLES))
-SRCS= \
-  $(patsubst %,$(TMPDIR_D)/%.c,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_D)/%.h,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_R)/%.c,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_R)/%.h,$(EXAMPLES))
-
-.PHONY: all clean
-
-.SECONDARY: $(SRCS)
-
-all: $(BINS)
-
-$(BINDIR_D)/packcc: $(SRCDIR)/packcc.c
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_D) -o $@ $< $(LDFLAGS_D)
-
-$(BINDIR_R)/packcc: $(SRCDIR)/packcc.c
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_R) -o $@ $< $(LDFLAGS_R)
-
-$(BINDIR_D)/examples/%: $(TMPDIR_D)/examples/%.c $(TMPDIR_D)/examples/%.h
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_D) -I. -o $@ $< $(LDFLAGS_D)
-
-$(BINDIR_R)/examples/%: $(TMPDIR_R)/examples/%.c $(TMPDIR_R)/examples/%.h
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_R) -I. -o $@ $< $(LDFLAGS_R)
-
-$(TMPDIR_D)/examples/%.c $(TMPDIR_D)/examples/%.h: $(SRCDIR)/examples/%.peg $(BINDIR_D)/packcc
-	mkdir -p $(dir $@) && $(BINDIR_D)/packcc -o $(basename $@) $<
-
-$(TMPDIR_R)/examples/%.c $(TMPDIR_R)/examples/%.h: $(SRCDIR)/examples/%.peg $(BINDIR_R)/packcc
-	mkdir -p $(dir $@) && $(BINDIR_R)/packcc -o $(basename $@) $<
-
-clean:
-	rm -f $(BINS) $(SRCS)
diff --git a/build/gcc/Makefile b/build/gcc/Makefile
deleted file mode 100644
index eb6e4893328f3e5e5a65d33fc870d205abd73a1c..0000000000000000000000000000000000000000
--- a/build/gcc/Makefile
+++ /dev/null
@@ -1,51 +0,0 @@
-CC=gcc
-CFLAGS_D=-O0 -g2 -Wall
-CFLAGS_R=-O2 -Wall -DNDEBUG
-LDFLAGS_D=
-LDFLAGS_R=
-
-SRCDIR=../../src
-TMPDIR_D=tmp/debug
-TMPDIR_R=tmp/release
-BINDIR_D=bin/debug
-BINDIR_R=bin/release
-
-EXAMPLES:=$(patsubst $(SRCDIR)/examples/%.peg,examples/%,$(wildcard $(SRCDIR)/examples/*.peg))
-
-BINS= \
-  $(BINDIR_D)/packcc \
-  $(BINDIR_R)/packcc \
-  $(patsubst %,$(BINDIR_D)/%,$(EXAMPLES)) \
-  $(patsubst %,$(BINDIR_R)/%,$(EXAMPLES))
-SRCS= \
-  $(patsubst %,$(TMPDIR_D)/%.c,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_D)/%.h,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_R)/%.c,$(EXAMPLES)) \
-  $(patsubst %,$(TMPDIR_R)/%.h,$(EXAMPLES))
-
-.PHONY: all clean
-
-.SECONDARY: $(SRCS)
-
-all: $(BINS)
-
-$(BINDIR_D)/packcc: $(SRCDIR)/packcc.c
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_D) -o $@ $< $(LDFLAGS_D)
-
-$(BINDIR_R)/packcc: $(SRCDIR)/packcc.c
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_R) -o $@ $< $(LDFLAGS_R)
-
-$(BINDIR_D)/examples/%: $(TMPDIR_D)/examples/%.c $(TMPDIR_D)/examples/%.h
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_D) -I. -o $@ $< $(LDFLAGS_D)
-
-$(BINDIR_R)/examples/%: $(TMPDIR_R)/examples/%.c $(TMPDIR_R)/examples/%.h
-	mkdir -p $(dir $@) && $(CC) $(CFLAGS_R) -I. -o $@ $< $(LDFLAGS_R)
-
-$(TMPDIR_D)/examples/%.c $(TMPDIR_D)/examples/%.h: $(SRCDIR)/examples/%.peg $(BINDIR_D)/packcc
-	mkdir -p $(dir $@) && $(BINDIR_D)/packcc -o $(basename $@) $<
-
-$(TMPDIR_R)/examples/%.c $(TMPDIR_R)/examples/%.h: $(SRCDIR)/examples/%.peg $(BINDIR_R)/packcc
-	mkdir -p $(dir $@) && $(BINDIR_R)/packcc -o $(basename $@) $<
-
-clean:
-	rm -f $(BINS) $(SRCS)
diff --git a/build/msvc/examples/calc/calc.vcxproj b/build/msvc/examples/calc/calc.vcxproj
deleted file mode 100644
index a586bbb58eb05f56f7951f3644101f8e3f98ec6c..0000000000000000000000000000000000000000
--- a/build/msvc/examples/calc/calc.vcxproj
+++ /dev/null
@@ -1,210 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <ItemGroup>
-    <CustomBuild Include="..\..\..\..\src\examples\calc.peg">
-      <FileType>Document</FileType>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)tmp\$(PlatformTarget)\$(Configuration)\%(Filename).c;%(Outputs)</Outputs>
-      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkObjects>
-      <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ClCompile</OutputItemType>
-      <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatOutputAsContent>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)tmp\$(PlatformTarget)\$(Configuration)\%(Filename).c;%(Outputs)</Outputs>
-      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkObjects>
-      <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ClCompile</OutputItemType>
-      <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</TreatOutputAsContent>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)tmp\$(PlatformTarget)\$(Configuration)\%(Filename).c;%(Outputs)</Outputs>
-      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkObjects>
-      <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ClCompile</OutputItemType>
-      <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatOutputAsContent>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)tmp\$(PlatformTarget)\$(Configuration)\%(Filename).c;%(Outputs)</Outputs>
-      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkObjects>
-      <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ClCompile</OutputItemType>
-      <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</TreatOutputAsContent>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
-      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</DeploymentContent>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
-      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</DeploymentContent>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
-      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DeploymentContent>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
-      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</DeploymentContent>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(PlatformTarget)\$(Configuration)\packcc -o tmp\$(PlatformTarget)\$(Configuration)\%(Filename) %(FullPath)</Command>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(PlatformTarget)\$(Configuration)\packcc -o tmp\$(PlatformTarget)\$(Configuration)\%(Filename) %(FullPath)</Command>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(PlatformTarget)\$(Configuration)\packcc -o tmp\$(PlatformTarget)\$(Configuration)\%(Filename) %(FullPath)</Command>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(PlatformTarget)\$(Configuration)\packcc -o tmp\$(PlatformTarget)\$(Configuration)\%(Filename) %(FullPath)</Command>
-    </CustomBuild>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <VCProjectVersion>16.0</VCProjectVersion>
-    <ProjectGuid>{5EB5881F-0182-4E32-A3E8-6940E5016770}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>calc</RootNamespace>
-    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
-    <ProjectName>calc</ProjectName>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="Shared">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <LinkIncremental>true</LinkIncremental>
-    <OutDir>$(ProjectDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-    <TargetName>$(ProjectName)</TargetName>
-    <IncludePath>.\;$(IncludePath)</IncludePath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <LinkIncremental>true</LinkIncremental>
-    <OutDir>$(ProjectDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-    <TargetName>$(ProjectName)</TargetName>
-    <IncludePath>.\;$(IncludePath)</IncludePath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(ProjectDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-    <TargetName>$(ProjectName)</TargetName>
-    <IncludePath>.\;$(IncludePath)</IncludePath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(ProjectDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-    <TargetName>$(ProjectName)</TargetName>
-    <IncludePath>.\;$(IncludePath)</IncludePath>
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-    <PreBuildEvent>
-      <Command>
-      </Command>
-    </PreBuildEvent>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-</Project>
\ No newline at end of file
diff --git a/build/msvc/examples/calc/calc.vcxproj.filters b/build/msvc/examples/calc/calc.vcxproj.filters
deleted file mode 100644
index 7c4e44f391f148b650560ac64f9fbfc5bb1614ae..0000000000000000000000000000000000000000
--- a/build/msvc/examples/calc/calc.vcxproj.filters
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="Source Files">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
-    </Filter>
-    <Filter Include="Header Files">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
-    </Filter>
-    <Filter Include="Resource Files">
-      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
-      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <CustomBuild Include="..\..\..\..\src\examples\calc.peg">
-      <Filter>Source Files</Filter>
-    </CustomBuild>
-  </ItemGroup>
-</Project>
\ No newline at end of file
diff --git a/build/msvc/examples/calc/calc.vcxproj.user b/build/msvc/examples/calc/calc.vcxproj.user
deleted file mode 100644
index 0f14913f3c72094bb7b1e695e153ade04b17d5b0..0000000000000000000000000000000000000000
--- a/build/msvc/examples/calc/calc.vcxproj.user
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup />
-</Project>
\ No newline at end of file
diff --git a/build/msvc/msvc.sln b/build/msvc/msvc.sln
deleted file mode 100644
index 97f234da09bc5df82b98dc459f0efd972f141f90..0000000000000000000000000000000000000000
--- a/build/msvc/msvc.sln
+++ /dev/null
@@ -1,49 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29209.62
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "packcc", "packcc.vcxproj", "{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "calc", "examples\calc\calc.vcxproj", "{5EB5881F-0182-4E32-A3E8-6940E5016770}"
-	ProjectSection(ProjectDependencies) = postProject
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C} = {9A1A5538-7127-4B2F-9C82-3282A69F3C9C}
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{16C15CF5-EA71-4DEB-A758-CC52E0CA2558}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|x64 = Debug|x64
-		Debug|x86 = Debug|x86
-		Release|x64 = Release|x64
-		Release|x86 = Release|x86
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Debug|x64.ActiveCfg = Debug|x64
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Debug|x64.Build.0 = Debug|x64
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Debug|x86.ActiveCfg = Debug|Win32
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Debug|x86.Build.0 = Debug|Win32
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Release|x64.ActiveCfg = Release|x64
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Release|x64.Build.0 = Release|x64
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Release|x86.ActiveCfg = Release|Win32
-		{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}.Release|x86.Build.0 = Release|Win32
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Debug|x64.ActiveCfg = Debug|x64
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Debug|x64.Build.0 = Debug|x64
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Debug|x86.ActiveCfg = Debug|Win32
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Debug|x86.Build.0 = Debug|Win32
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Release|x64.ActiveCfg = Release|x64
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Release|x64.Build.0 = Release|x64
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Release|x86.ActiveCfg = Release|Win32
-		{5EB5881F-0182-4E32-A3E8-6940E5016770}.Release|x86.Build.0 = Release|Win32
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{5EB5881F-0182-4E32-A3E8-6940E5016770} = {16C15CF5-EA71-4DEB-A758-CC52E0CA2558}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {BD6C24C6-47A2-40C5-87E8-F72C92F9D5FF}
-	EndGlobalSection
-EndGlobal
diff --git a/build/msvc/packcc.vcxproj b/build/msvc/packcc.vcxproj
deleted file mode 100644
index ee0441d064d1145ba42059281a852fdbbcc73859..0000000000000000000000000000000000000000
--- a/build/msvc/packcc.vcxproj
+++ /dev/null
@@ -1,175 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <VCProjectVersion>16.0</VCProjectVersion>
-    <ProjectGuid>{9A1A5538-7127-4B2F-9C82-3282A69F3C9C}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>packcc</RootNamespace>
-    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="Shared">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <LinkIncremental>true</LinkIncremental>
-    <OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <LinkIncremental>true</LinkIncremental>
-    <OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\</OutDir>
-    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-      <DisableLanguageExtensions>true</DisableLanguageExtensions>
-      <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-      <DisableLanguageExtensions>true</DisableLanguageExtensions>
-      <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-      <DisableLanguageExtensions>true</DisableLanguageExtensions>
-      <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <ConformanceMode>true</ConformanceMode>
-      <DisableLanguageExtensions>true</DisableLanguageExtensions>
-      <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\src\packcc.c" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-</Project>
\ No newline at end of file
diff --git a/build/msvc/packcc.vcxproj.filters b/build/msvc/packcc.vcxproj.filters
deleted file mode 100644
index 3de6784b22e987f2fe74ba443358ce53648d5688..0000000000000000000000000000000000000000
--- a/build/msvc/packcc.vcxproj.filters
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="Source Files">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
-    </Filter>
-    <Filter Include="Header Files">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
-    </Filter>
-    <Filter Include="Resource Files">
-      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
-      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\src\packcc.c">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-  </ItemGroup>
-</Project>
\ No newline at end of file
diff --git a/build/msvc/packcc.vcxproj.user b/build/msvc/packcc.vcxproj.user
deleted file mode 100644
index 0f14913f3c72094bb7b1e695e153ade04b17d5b0..0000000000000000000000000000000000000000
--- a/build/msvc/packcc.vcxproj.user
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup />
-</Project>
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
index 8a029a626991cf1710feab7a83dd65da2cbffd03..5e9274d497216637c1f50b96a494a4ff08fef909 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,9 +58,18 @@ dnl silent automake requires version 1.11
 m4_ifdef([AM_SILENT_RULES],
 	 [AM_SILENT_RULES([yes])])
 
-AC_ARG_ENABLE([libnuma],
-              AS_HELP_STRING([--disable-libnuma],
-	                   [Disable the Linux libnuma]))
+AC_ARG_ENABLE([numa],
+              [AS_HELP_STRING([--enable-numa],[Disable libnuma usage (default: use if detected)])],
+	      [enable_numa=$enableval],[enable_numa=yes])
+AS_IF([test "x$enable_numa" = xyes],
+      [AC_CHECK_HEADERS([numa.h numaif.h],
+			[AC_CHECK_LIB([numa],[numa_available],
+				      AC_DEFINE([HAVE_NUMA])
+				      LIBS="-lnuma $LIBS")
+				      have_numa=yes],
+			[have_numa=no])])
+AM_CONDITIONAL([NUMA_ENABLED], [test "x$have_numa" = "xyes"])
+AC_SUBST([NUMA_ENABLED], [$have_numa])
 
 AC_CONFIG_SRCDIR([maestro/version.c])
 AX_CHECK_ENABLE_DEBUG()
@@ -115,7 +124,6 @@ AC_COMPILE_IFELSE(
  [AC_MSG_NOTICE([C compiler understands C11 well])],
  [AC_MSG_ERROR([C compiler does not handle constant initializers to structures, not fully C11 compatible])])
 
-
 dnl check for code-coverage option. This needs to stay at the start of checks
 AC_ARG_ENABLE([code-coverage],
 	  AS_HELP_STRING([--enable-code-coverage],
@@ -167,7 +175,8 @@ AC_ARG_ENABLE([tsan],
 if test "x$enable_tsan" = "xyes"; then
         BUILD_TSAN=yes
         AX_CHECK_COMPILE_FLAG([-fsanitize=thread -fno-omit-frame-pointer],
-       		[CFLAGS="${CFLAGS} -O1 -g -fsanitize=thread -fno-omit-frame-pointer"],
+       		[CFLAGS="${CFLAGS} -O1 -g -fsanitize=thread -fno-omit-frame-pointer"
+				LDFLAGS="${LDFLAGS} -fsanitize=thread"],
 		AC_MSG_ERROR([compiler does not support -fsanitize=thread flag]))
 else
         BUILD_TSAN=no
@@ -415,7 +424,9 @@ AS_IF([test "x$enable_ofi_pool_manager" = "xyes"], [
    AS_IF([test ! -f $srcdir/deps/libfabric/configure],
          [(cd $srcdir/deps/libfabric; ./autogen.sh)])
    #AX_SUBDIRS_CONFIGURE([deps/libfabric],[[--enable-embedded],[--disable-rxm],[--disable-rxd],[--disable-psm]],[],[],[])
-   AX_SUBDIRS_CONFIGURE([deps/libfabric],[[--enable-embedded],[--disable-rxd],[--disable-shm],[--disable-tcp],[--enable-debug]],[],[],[])
+   dnl Configuring without kdreg to fix issue https://gitlab.com/cerl/maestro/maestro-core/-/issues/117 which relates to https://github.com/ofiwg/libfabric/issues/5313
+   dnl Whether doing so creates a performance problem or not is still to be determined
+   AX_SUBDIRS_CONFIGURE([deps/libfabric],[[--enable-embedded],[--disable-rxd],[--disable-shm],[--disable-tcp],[--with-kdreg=no],[--enable-debug]],[],[],[])
    AC_MSG_NOTICE([================== done preconfiguring private libfabric library build])
   ])
 
@@ -536,6 +547,7 @@ AC_CONFIG_FILES([
   deps/libyaml/src/Makefile
   deps/libcyaml/Makefile
   deps/c-timestamp/Makefile
+  MaestroConfig.cmake
 ])
 
 dnl AC_SUBST does not preserve executable bit on scripts, so do
diff --git a/docs/maestro-arch-overview.png b/docs/maestro-arch-overview.png
new file mode 100644
index 0000000000000000000000000000000000000000..99e98dbd94ae2f85692a33ed2a198ad8c085cbc3
Binary files /dev/null and b/docs/maestro-arch-overview.png differ
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..5094eed71ecd2d4c4e4c740137de410bf0abb504
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,30 @@
+MAESTRO ?=/usr/local/
+
+CC ?= gcc
+MPICC ?= mpicc
+
+INC=-I$(MAESTRO)/include/maestro
+LDFLAGS=-L$(MAESTRO)/lib
+
+LIBS=-lmaestro
+LIBS_OMP=-fopenmp -lmaestro
+
+CFLAGS=-O3
+
+OBJs = core_bench local_pool_op
+
+
+all: local_pool_op core_bench
+
+%: %.c
+	$(CC) $(CFLAGS) $(INC) $< $(LDFLAGS) $(LIBS) -o $@
+
+%.o: %.c
+	$(MPICC) -c $(CFLAGS) $(INC) $< -o $@
+
+core_bench: omp_injector.o omp_consumer.o core_benchmark.o
+	$(MPICC)  $(LDFLAGS) $^ $(LIBS_OMP) -o $@
+
+
+clean:
+	rm -f $(OBJs) *.o *CDO*
diff --git a/examples/bechmark_attributes.yaml b/examples/bechmark_attributes.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c2be382888ed3106e575fea7286edd4bf63cabfe
--- /dev/null
+++ b/examples/bechmark_attributes.yaml
@@ -0,0 +1,220 @@
+# A user-defined schema. The minimum is name and version
+schema-name: Benchmark Attributes
+schema-version: 0
+
+schema-namespace: ".maestro.benchmark."
+
+# attributes section is optional; if given it needs to have a sequence value
+maestro-attributes:
+
+# Top-level attributes
+
+  - key: "attrib_1"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_2"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_3"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_4"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_5"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_6"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_7"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_8"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_9"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_10"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_11"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_12"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_13"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_14"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_15"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_16"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_17"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_18"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_19"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_20"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_21"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_22"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_23"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_24"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_25"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_26"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_27"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_28"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_29"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_30"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_31"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_32"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_33"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_34"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "attrib_35"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
diff --git a/examples/core_benchmark.c b/examples/core_benchmark.c
new file mode 100644
index 0000000000000000000000000000000000000000..dafbf70135772023c464be41cc30c427917bf8c3
--- /dev/null
+++ b/examples/core_benchmark.c
@@ -0,0 +1,356 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief benchmark core CDO operations, i.e., declare, offer, require, demand, withdraw, and dispose. Using multiple producers and consumers. This benchmark demonstrates also how to write an MPI+OpenMP application with Maestro.
+ **/
+
+/*
+ * Copyright (C) 2021 Hewlett Packard Enterprise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "maestro.h"
+#include <assert.h>
+#include "maestro/logging.h"
+#include <string.h>
+#include "omp.h"
+#include "omp_injector.h"
+#include "omp_consumer.h"
+#include "mpi.h"
+
+
+#define BENCHMARK_ATTRIBUTES_YAML "./bechmark_attributes.yaml"
+#define MAX_NUM_ATTRIBUTES 35
+
+
+
+/* simplify logging */
+#define DEBUG(...) LOG_DEBUG(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define INFO(...)  LOG_INFO(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define WARN(...)  LOG_WARN(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define ERR(...)   LOG_ERR(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+
+int main(int argc, char *argv[])
+{
+
+  int injector_id = 1; // could come from MPI_COMM_rank
+  size_t num_attributes = 0;
+  size_t size_attributes = 0;
+  size_t CDOs_per_thread = 10000; // 10k CDOs/thread by default
+  size_t nConsumers = 0; // number of consumers
+  size_t size_CDO = 0; // size of CDOs
+  mstro_status status = MSTRO_OK; // global status
+
+
+  int rank, size;
+
+  // read arguments
+  if (argc > 1)
+  {
+    num_attributes =atoi(argv[1]);
+    if (num_attributes > MAX_NUM_ATTRIBUTES)
+    {
+      printf("Maximum number of supported attributes in this benchmark is %d \n", MAX_NUM_ATTRIBUTES);
+      num_attributes = MAX_NUM_ATTRIBUTES;
+    }
+  }
+  if (argc > 2)
+  {
+    size_attributes =atoi(argv[2]);
+  }
+  if (argc > 3)
+  {
+    CDOs_per_thread =atoi(argv[3]);
+  }
+  if (argc > 4)
+  {
+    nConsumers =atoi(argv[4]);
+  }
+  if (argc > 5)
+  {
+    size_CDO =atoi(argv[5]);
+  }
+
+  /* save print time: */
+  setenv("MSTRO_LOG_LEVEL","0",1);
+
+
+  // export the path to the custom scehma
+  setenv("MSTRO_SCHEMA_LIST",BENCHMARK_ATTRIBUTES_YAML,1);
+
+  // read num of threads
+  size_t num_threads = 1;
+  // Read environment variables
+  if (getenv("OMP_NUM_THREADS") != NULL)
+  {
+    num_threads = atoi(getenv("OMP_NUM_THREADS"));
+  }
+
+  /*start MPI ... if number of ranks is 1, then start only injector component
+  that will work with local pool manager*/
+  MPI_Init(NULL,NULL);
+  MPI_Comm_rank (MPI_COMM_WORLD, &rank);
+  MPI_Comm_size (MPI_COMM_WORLD, &size);
+
+  if (size == 1)
+  {
+    // start producer
+    status = start_injector(rank, num_attributes, size_attributes, CDOs_per_thread);
+  }
+  else
+  {
+    /*If number of ranks is greater than 1,
+    1. start a pool manager component
+    2. broadcast PM info
+    3.look for the num of producers and consumers and start them
+    */
+    if (rank == 0)
+    {
+      // start pool manager ---    workflow, component,    id
+      status = mstro_init("Tests", "Pool_Manager", 0);
+      if(status!=MSTRO_OK) {
+         ERR("Maestro Pool Manager: Failed to initialize Maestro: %d (%s)\n",
+            status, mstro_status_description(status));
+          // panic and fail
+          MPI_Abort(MPI_COMM_WORLD, -1);
+      }
+      status = mstro_pm_start();
+      if(status!=MSTRO_OK) {
+        ERR("Simple Maestro Pool Manager: Failed to start pool: %d (%s)\n",
+              status, mstro_status_description(status));
+
+        // panic and fail
+        MPI_Abort(MPI_COMM_WORLD, -1);
+      }
+
+      char *info = NULL;
+      status = mstro_pm_getinfo(&info);
+
+      if(status!=MSTRO_OK) {
+        ERR("Simple Maestro Pool Manager: Failed to obtain pool contact info: %d (%s)\n",
+              status, mstro_status_description(status));
+
+        // panic and fail
+        MPI_Abort(MPI_COMM_WORLD, -1);
+        }
+
+        assert(status == MSTRO_OK);
+
+        // get size of PM info and broadcast it
+        int info_size = strlen(info);
+        MPI_Bcast(&info_size,1,MPI_INT, 0, MPI_COMM_WORLD);
+        //broadcast the PM info
+        MPI_Bcast(info,info_size,MPI_CHAR, 0, MPI_COMM_WORLD);
+
+        //sync producers and conumers --otherwise producers may widthraw CDOs before consumed
+        MPI_Barrier(MPI_COMM_WORLD);
+
+    }
+    else if ((rank > 0) && (rank <= nConsumers))
+    {
+      double require_time, demand_time, before, after;
+      // get PM info
+      int info_size;
+      MPI_Bcast(&info_size,1,MPI_INT, 0, MPI_COMM_WORLD);
+      //get the PM info
+      char *info = (char *) malloc(info_size*sizeof(char));
+      MPI_Bcast(info,info_size,MPI_CHAR, 0, MPI_COMM_WORLD);
+
+      // put it in the environment
+      setenv("MSTRO_POOL_MANAGER_INFO",info,1);
+
+
+      //get consumer mode
+      int consumer_mode = get_consumer_mode();
+      printf("[Consumer %d] consumer mode = %d \n",rank, consumer_mode);
+      // get the number of producers assigned to me
+      int num_producers = get_num_producers(size, nConsumers, consumer_mode);
+      printf("[Consumer %d] num_producers = %d \n",rank, num_producers );
+      // allocate producers list
+      int *producers_ids = (int *) malloc(sizeof(int)*num_producers);
+      // calculate my assigned producers
+      get_producers(rank, size, nConsumers, producers_ids, num_producers, consumer_mode);
+
+
+      for (size_t i = 0; i < num_producers; i++) {
+        printf("producer: %d \n", producers_ids[i]);
+      }
+
+      // create CDOs array
+      size_t num_CDOs = CDOs_per_thread * num_threads*num_producers; // CDOs per producer * (num_thread) * num_producers
+      printf("[Consumer %d] num_CDOs = %zu \n",rank, num_CDOs );
+
+      mstro_cdo cdos[num_CDOs];
+
+
+
+      // start consumers
+      status = mstro_init("Tests","Consumer",rank);
+      assert(MSTRO_OK == status);
+
+
+      // Require CDOs
+      before = omp_get_wtime();
+      // declare CDOs loop
+
+      status = require_CDOs(cdos, num_CDOs, producers_ids, num_producers, num_attributes, size_attributes);
+      assert(MSTRO_OK == status);
+      after = omp_get_wtime();
+      require_time = (after - before) * 1000.0*1000.0; //time in us seconds
+
+      fprintf(stdout, "Throughput (declare/require): %.5f us\n", require_time/(double) num_CDOs);
+
+
+      //sync producers and consumers -- avoid CDOs been withdrawn before require
+      MPI_Barrier(MPI_COMM_WORLD);
+
+
+      // Demand CDOs
+      before = omp_get_wtime(); // restart timer here
+      status = demand_CDOs(cdos, num_CDOs);
+      assert(MSTRO_OK == status);
+      after = omp_get_wtime();
+      demand_time = (after - before) * 1000.0*1000.0; //time in us seconds
+      fprintf(stdout, "Throughput (demand/dispose): %.5f us\n", demand_time/(double) num_CDOs);
+
+
+      // finalize Maestro
+      status = mstro_finalize();
+      assert(status == MSTRO_OK);
+
+      //clean up
+      if (producers_ids != NULL)
+      {free(producers_ids);}
+
+
+
+    }
+    else
+    {
+      double declare_time,withdraw_time, before, after;
+
+      // get PM info
+      int info_size;
+      MPI_Bcast(&info_size,1,MPI_INT, 0, MPI_COMM_WORLD);
+      //get the PM info
+      char *info = (char *) malloc(info_size*sizeof(char));
+      MPI_Bcast(info,info_size,MPI_CHAR, 0, MPI_COMM_WORLD);
+
+      // put it in the environment
+      setenv("MSTRO_POOL_MANAGER_INFO",info,1);
+
+      size_t num_CDOs = CDOs_per_thread * num_threads; // 1K per producer * (num_thread)
+
+      status = mstro_init("Tests","Injector",rank);
+
+      assert(MSTRO_OK == status);
+
+      // create CDOs array
+      mstro_cdo cdos[num_CDOs];
+
+      // create CDOs DATA
+      char *CDO_data[num_CDOs];
+      if (size_CDO != 0)
+      {
+        for (size_t i = 0; i < num_CDOs; i++) {
+          //allocate data
+          posix_memalign((void**) &CDO_data[i], (size_t) sysconf(_SC_PAGESIZE), sizeof(char)*size_CDO);
+          //fill data
+          fill_char_array(CDO_data[i], size_CDO);
+        }
+
+      }
+
+      before = omp_get_wtime();
+      // declare CDOs loop
+      status = inject_CDOs(rank, cdos, num_CDOs, num_attributes, size_attributes, size_CDO, CDO_data);
+
+      assert(MSTRO_OK == status);
+
+      after = omp_get_wtime();
+
+      declare_time = (after - before) * 1000.0*1000.0; //time in us seconds
+
+      fprintf(stdout, "#CDOs: %zu, #Threads: %zu, #Attributes: %zu, Size of attributes: %zu \n", num_CDOs, num_threads, num_attributes, size_attributes);
+      fprintf(stdout, "Throughput (declare/offer): %.5f us\n", declare_time/(double) num_CDOs);
+
+      //sync producers and consumers -- avoid CDOs been withdrawn before require
+      MPI_Barrier(MPI_COMM_WORLD);
+
+      before = omp_get_wtime(); // restart timer here
+      // withdraw CDOs loop
+      status = withdraw_CDOs(cdos, num_CDOs);
+
+      assert(MSTRO_OK == status);
+
+      after = omp_get_wtime();
+
+      withdraw_time = (after - before) * 1000.0*1000.0; //time in us seconds
+      fprintf(stdout, "Throughput (withdraw/dispose): %.5f us\n", withdraw_time/(double) num_CDOs);
+
+      status = mstro_finalize();
+
+      assert(status == MSTRO_OK);
+
+    }
+
+
+
+  }
+
+
+  // wait for termination otherwise PM will exist before producers and consumers
+  MPI_Barrier(MPI_COMM_WORLD);
+
+  // terminate pool manager
+  if ((rank == 0) && (size > 1))
+  {
+    status = mstro_pm_terminate();
+    if(status!=MSTRO_OK) {
+      ERR("Simple Maestro Pool Manager: Failed to shut down pool: %d (%s)\n",
+        status, mstro_status_description(status));
+
+      // panic and fail
+      MPI_Abort(MPI_COMM_WORLD, -1);
+    }
+
+    status = mstro_finalize();
+    if(status!=MSTRO_OK) {
+      ERR("Simple Maestro Pool Maestro: failed to terminate: %d (%s)\n",
+        status, mstro_status_description(status));
+
+      // panic and fail
+      MPI_Abort(MPI_COMM_WORLD, -1);
+    }
+  }
+
+  MPI_Finalize();
+
+  return 0;
+}
diff --git a/examples/local_pool_op.c b/examples/local_pool_op.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb3410620fcb81c51d7fb0e90ceb38826d1ceaaa
--- /dev/null
+++ b/examples/local_pool_op.c
@@ -0,0 +1,1060 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief   Single node pool operations example
+ **/
+
+/*
+   Driver
+
+   - A multi-threaded application (pthread) consisting of
+
+   - A proxy for the workflow component (WP4) (work description)
+
+   - A producer application
+
+   - two consumer applications C1 and C2
+
+   - The producer creates CDOs (type -,1,2)
+
+   - The consumer C1 (consumer) retrieves the CDOs and performs computations on it, then drops them
+
+   - The consumer C2 (archiver) writes out the CDOs to permanent storage
+
+
+   Maestro Pool MVP
+
+    - Single node pool operation
+
+    - CDO type 0, 1 and 2 declarations
+
+    - Basic Offer-Demand functionality
+
+    - Task coupling / CDO resolution through the pool
+
+    - DEMAND operations with user-allocated buffers
+
+    - Basic interface to Mamba for type 1,2 CDOs
+ */
+
+/** Implementation:
+ * main thread:
+ *    - constructs work description
+ *    - posts (announces) a work description CDO for all to see.
+ *    - starts threads for consumer, archiver tasks
+ *    - posts CONSUMER_READY REQUIRE
+ *      -- if multiple consumers, multiple such CDOs
+ *    - posts ARCHIVER_READY REQUIRE
+ *      -- if multiple archivers, multiple such CDOs
+ *    - starts producer
+ *    - waits for threads to finish
+ *    - withdraws work description CDO
+ *    - quits
+ *
+ * archiver:
+ *    - waits for work description
+ *    - posts CDO REQUIRES for all CDOs in the list
+ *    - demands them one by one
+ *    - writes them to disk (file name = CDO name) using Mamba tiling interface
+ *    - disposes them
+ *      -- when multiple archive threads run, archiver i
+ *         handles CDOs with index%num_archivers==i
+ *
+ * consumer:
+ *    - waits for work description
+ *    - posts CDO REQUIRES for all CDOS in the list
+ *    - demands them one by one
+ *    - computes a checksum on the data, prints it
+ *    - disposes them
+ *      -- when multiple consumer threads run, consumer i
+ *         handles CDOs with index%num_consumers==i
+ *
+ * producer:
+ *    - waits for work description
+ *    - posts CDO OFFER for all CDOs
+ *    - posts CDO WITHDRAW for all CDOs
+ *    - disposes them
+ *      -- when multiple producer threads run, producer i
+ *         handles CDOs with index%num_consumers==i
+ *
+ */
+/*
+ * Copyright (C) 2019 Cray Computer GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maestro.h"
+#include "maestro/logging.h"
+
+#include "mamba.h"
+#include "mmb_tile_iterator.h"
+
+#include <cyaml.h>
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+
+/* simplify logging */
+#define DEBUG(...) LOG_DEBUG(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define INFO(...)  LOG_INFO(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define WARN(...)  LOG_WARN(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define ERR(...)   LOG_ERR(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+
+
+
+/** name template of data CDOs sent around */
+#define CDO_NAME_TEMPLATE "CDO %zu"
+/** UB on length of CDO_NAME_TEMPLATE */
+#define CDO_NAME_MAX (strlen(CDO_NAME_TEMPLATE)+10)
+
+/** maximum number of CDOs in one run */
+#define CDO_COUNT_MAX 100000
+
+/** A structure holding the workflow configuration */
+struct cdo_announcement {
+  size_t num_producers;      /** number of producer threads */
+  size_t num_consumers;      /** number of consumer threads */
+  size_t num_archivers;      /** number of archiver threads */
+  int64_t cdo_size;           /** size of each CDO */
+  size_t num_entries;        /** number of CDOs */
+  char *cdo_names[CDO_COUNT_MAX];   /** string names of the CDOs */
+};
+
+/** A suitable scope/layout/size string for the announcement */
+char *announcement_scope_string;
+
+/** A CDO name for the work announcement */
+#define ANNOUNCEMENT_TOKEN_NAME "CDO Announcement"
+
+/** A CDO name template for 'Consumer ready' */
+#define CONSUMER_READY_NAME_TEMPLATE "Consumer Ready %zu"
+
+/** A CDO name template for 'Archiver ready' */
+#define ARCHIVER_READY_NAME_TEMPLATE "Archiver Ready %zu"
+
+#define READY_NAME_MAX 32
+
+/** A rigid version of the workflow configuration for cyaml to fill */
+struct mvp_config_
+{
+  int num_producers;
+  int num_consumers;
+  int num_archivers;
+  int cdo_size;
+  int cdo_count;
+};
+
+typedef struct mvp_config_ * mvp_config;
+
+/** cyaml schema */
+static const cyaml_schema_field_t top_mapping_schema[] = {
+	CYAML_FIELD_INT("num_producers", CYAML_FLAG_DEFAULT | CYAML_FLAG_OPTIONAL,
+			struct mvp_config_, num_producers),
+	CYAML_FIELD_INT("num_consumers", CYAML_FLAG_DEFAULT | CYAML_FLAG_OPTIONAL,
+			struct mvp_config_, num_consumers),
+	CYAML_FIELD_INT("num_archivers", CYAML_FLAG_DEFAULT | CYAML_FLAG_OPTIONAL,
+			struct mvp_config_, num_archivers),
+	CYAML_FIELD_INT("cdo_size", CYAML_FLAG_DEFAULT | CYAML_FLAG_OPTIONAL,
+			struct mvp_config_, cdo_size),
+	CYAML_FIELD_INT("cdo_count", CYAML_FLAG_DEFAULT | CYAML_FLAG_OPTIONAL,
+			struct mvp_config_, cdo_count),
+       	CYAML_FIELD_END
+};
+
+/* cyaml value schema for the top level mapping. */
+static const cyaml_schema_value_t top_schema = {
+	CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
+			struct mvp_config_, top_mapping_schema),
+};
+
+/** Basic cyaml config */
+static const cyaml_config_t config = {
+	.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
+	.log_fn = cyaml_log,            /* Use the default logging function. */
+	.mem_fn = cyaml_mem,            /* Use the default memory allocator. */
+};
+
+/** Call cyaml to parse config file */
+int
+mvp_config_parse(mvp_config* handle, const char* filename)
+{
+  if (handle == NULL) return MSTRO_INVARG;
+
+  cyaml_err_t err;
+  err = cyaml_load_file(filename, &config,
+  		&top_schema, (cyaml_data_t **)handle, NULL);
+  if (err != CYAML_OK) {
+    fprintf(stderr, "ERROR: %s (file %s)\n", cyaml_strerror(err), filename);
+    return -1;
+  }
+
+  return 0;
+}
+
+/** announcement message receive: */
+static void
+wait_for_announcement(struct cdo_announcement *announcement)
+{
+  mstro_status s;
+  mstro_cdo announcement_cdo;
+  /* start by waiting for the announcement */
+  s = mstro_cdo_declare(ANNOUNCEMENT_TOKEN_NAME,
+                        MSTRO_ATTR_DEFAULT,
+                        &announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to declare announcement CDO\n");
+    abort();
+  }
+  s = mstro_cdo_attribute_set(announcement_cdo,
+                              MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                              announcement);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to set raw-ptr attribute on announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_attribute_set_yaml(announcement_cdo,
+                                   announcement_scope_string);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to set raw-ptr size attribute on announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_require(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to require announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_demand(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to withdraw announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_dispose(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to dispose announcement CDO\n");
+    abort();
+  }
+
+  return;
+}
+
+static void
+do_announce(struct cdo_announcement *announcement, mstro_cdo *result)
+{
+  mstro_status s;
+  mstro_cdo announcement_cdo;
+  /* start by waiting for the announcement */
+  s = mstro_cdo_declare(ANNOUNCEMENT_TOKEN_NAME,
+                        MSTRO_ATTR_DEFAULT,
+                        &announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to declare announcement CDO\n");
+    abort();
+  }
+  s = mstro_cdo_attribute_set(announcement_cdo,
+                              MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                              announcement);
+
+  if(s!=MSTRO_OK) {
+    ERR("Failed to set raw-ptr attribute on  announcement CDO\n");
+    abort();
+  }
+  s = mstro_cdo_attribute_set_yaml(announcement_cdo,
+                                   announcement_scope_string);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to set raw-ptr size attribute on announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_offer(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to offer announcement CDO\n");
+    abort();
+  }
+
+  *result = announcement_cdo;
+}
+
+static void
+withdraw_announcement(mstro_cdo announcement_cdo)
+{
+  mstro_status s;
+
+  s = mstro_cdo_withdraw(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to withdraw announcement CDO\n");
+    abort();
+  }
+
+  s = mstro_cdo_dispose(announcement_cdo);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to dispose announcement CDO\n");
+    abort();
+  }
+
+  return;
+}
+
+/* do the equivalent of a sem_post using the given CDO name */
+static void
+cdo_sem_post(const char *name, mstro_cdo *sem)
+{
+  mstro_cdo cdo;
+  mstro_status s;
+  /* use type 0 CDO */
+  s = mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT,
+                        &cdo);
+  s |= mstro_cdo_offer(cdo);
+  if(s!=MSTRO_OK) {
+    ERR("CDO sem post failed\n");
+    abort();
+  }
+  *sem = cdo;
+}
+
+static void
+cdo_sem_post_cleanup(mstro_cdo sem)
+{
+  mstro_status s = mstro_cdo_withdraw(sem);
+  s |= mstro_cdo_dispose(sem);
+
+  if(s!=MSTRO_OK) {
+    ERR("CDO sem post cleanup failed\n");
+    abort();
+  }
+}
+
+/* do the equivalent of a sem_wait using the given CDO name */
+static void
+cdo_sem_wait(const char *name)
+{
+  mstro_cdo cdo;
+  mstro_status s;
+  /* use type 0 CDO */
+  s = mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT,
+                        &cdo);
+  s |= mstro_cdo_require(cdo);
+  s |= mstro_cdo_demand(cdo);
+  s |= mstro_cdo_dispose(cdo);
+
+  if(s!=MSTRO_OK) {
+    ERR("CDO sem wait failed\n");
+    abort();
+  }
+
+}
+/* Quickly flash the "Hello" token */
+void
+declare_archiver_ready(size_t index, mstro_cdo *cdo)
+{
+  char name[READY_NAME_MAX];
+  size_t s = snprintf(name, READY_NAME_MAX,
+                      ARCHIVER_READY_NAME_TEMPLATE, index);
+  if(s>=READY_NAME_MAX) {
+    ERR("Failed to construct name");
+    abort();
+  }
+  cdo_sem_post(name, cdo);
+}
+
+/* Block on a "Hello" token */
+void
+wait_archiver_ready(size_t index)
+{
+  char name[READY_NAME_MAX];
+  size_t s = snprintf(name, READY_NAME_MAX,
+                      ARCHIVER_READY_NAME_TEMPLATE, index);
+  if(s>=READY_NAME_MAX) {
+    ERR("Failed to construct name");
+    abort();
+  }
+  cdo_sem_wait(name);
+}
+
+void
+declare_consumer_ready(size_t index, mstro_cdo *cdo)
+{
+  char name[READY_NAME_MAX];
+  size_t s = snprintf(name, READY_NAME_MAX,
+                      CONSUMER_READY_NAME_TEMPLATE, index);
+  if(s>=READY_NAME_MAX) {
+    ERR("Failed to construct name");
+    abort();
+  }
+  cdo_sem_post(name,cdo);
+}
+
+void
+wait_consumer_ready(size_t index)
+{
+  char name[READY_NAME_MAX];
+  size_t s = snprintf(name, READY_NAME_MAX,
+                      CONSUMER_READY_NAME_TEMPLATE, index);
+  if(s>=READY_NAME_MAX) {
+    ERR("Failed to construct name");
+    abort();
+  }
+  cdo_sem_wait(name);
+}
+
+
+static void
+archiver_flush_to_disk(const char *name, mmbArray *a)
+{
+  size_t chunkdims = 4096;
+  mmbDimensions chunks = {1, &chunkdims};
+  mmbError stat = mmb_array_tile(a, &chunks);
+  if(stat != MMB_OK) {
+    ERR("Failed to tile mamba array (chunked tiles)\n");
+    abort();
+  }
+
+  // Loop over tiles
+  mmbTileIterator* it;
+  stat = mmb_tile_iterator_create(a, &it);
+  if(stat != MMB_OK) {
+    ERR("Failed to get tile iterator\n");
+    abort();
+  }
+
+  size_t nt;
+  stat = mmb_tile_iterator_count(it, &nt);
+  if(stat != MMB_OK) {
+    ERR("Failed to get tile iterator count\n");
+    abort();
+  }
+  stat = mmb_tile_iterator_first(it);
+  if(stat != MMB_OK) {
+    ERR("Failed to move tile iterator to first\n");
+    abort();
+  }
+  FILE *dst = fopen((const char*)name, "w");
+  if(dst==NULL) {
+    ERR("Failed to open %s for writing\n", name);
+    abort();
+  }
+  for(size_t i = 0; i < nt; i++){
+    mmbArrayTile* tile = it->tile;
+    size_t count = fwrite(&MMB_IDX_1D(tile, tile->lower[0], char),
+                          1,
+                          tile->upper[0]-tile->lower[0],
+                          dst);
+    if(count!= tile->upper[0]-tile->lower[0]) {
+      ERR("Incomplete write on tile %d of %s\n", i, name);
+      abort();
+    }
+    stat = mmb_tile_iterator_next(it);
+    if(stat != MMB_OK) {
+      ERR("Failed to increment tile iterator\n");
+      abort();
+    }
+  }
+  if(0!=fclose(dst)) {
+    ERR("Failed to close %s after writing\n", name);
+    abort();
+  }
+  stat = mmb_tile_iterator_destroy(it);
+  if(stat != MMB_OK) {
+    ERR("Failed to free tile iterator\n");
+    abort();
+  }
+
+  // Remove tiling
+  stat = mmb_array_untile(a);
+  if(stat != MMB_OK) {
+    ERR("Failed to untile mamba array\n");
+    abort();
+  }
+
+}
+
+/*
+ * archiver:
+ *    - waits for work description
+ *    - posts CDO REQUIRES for all CDOs in the list
+ *    - demands them one by one
+ *    - writes them to disk (file name = CDO name) using Mamba tiling interface
+ *    - disposes them
+ *      -- when multiple archive threads run, archiver i
+ *         handles CDOs with index%num_archivers==i
+ */
+void*
+archiver_thread_fun(void *closure)
+{
+  mstro_status s;
+  size_t my_idx = * (size_t*)closure;
+  INFO("Archiver %zu starting\n", my_idx);
+
+  struct cdo_announcement *announcement
+      = malloc(sizeof(struct cdo_announcement));
+  if(announcement==NULL) {
+    ERR("Failed to allocate for incoming announcement\n");
+    abort();
+  }
+
+  wait_for_announcement(announcement);
+
+  mstro_cdo incoming[announcement->num_entries];
+  void *incoming_buffers[announcement->num_entries];
+
+  /* declare and require all that we are responsible for */
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_archivers==my_idx) {
+      /* declare it */
+      s = mstro_cdo_declare(announcement->cdo_names[i],
+                            MSTRO_ATTR_DEFAULT,
+                            &(incoming[i]));
+      if(s!=MSTRO_OK) {
+        ERR("Failed to declare CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* add allocation */
+      incoming_buffers[i] = malloc(announcement->cdo_size);
+      if(incoming_buffers[i]==NULL) {
+        ERR("Cannot allocate CDO buffer for archiving\n");
+        abort();
+      }
+      s = mstro_cdo_attribute_set(incoming[i],
+                                  MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                                  incoming_buffers[i]);
+      s |= mstro_cdo_attribute_set(incoming[i],
+                                   MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                   &announcement->cdo_size);
+      //      INFO("archiver cdo %d incoming buffer %p\n", i, incoming_buffers[i]);
+
+      if(s!=MSTRO_OK) {
+        ERR("Failed to add buffer to CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* require it */
+      s = mstro_cdo_require(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to require CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+    }
+  }
+  /* send ACK that we're ready */
+  INFO("Declaring Archiver %zu ready\n", my_idx);
+  mstro_cdo ready_cdo;
+  declare_archiver_ready(my_idx,&ready_cdo);
+
+  /* process all in some order */
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_archivers==my_idx) {
+      s = mstro_cdo_demand(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to demand CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+
+      /* extract mamba pointer */
+      mmbArray *mamba_array;
+      s = mstro_cdo_access_mamba_array(incoming[i], &mamba_array);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to extract mamba array from CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+
+      /* do write-out */
+      archiver_flush_to_disk(announcement->cdo_names[i], mamba_array);
+
+      s = mstro_cdo_dispose(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to dispose CDO %s after archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      free(incoming_buffers[i]);
+    }
+  }
+
+  cdo_sem_post_cleanup(ready_cdo);
+  free(announcement);
+  return NULL;
+}
+
+void buf_fill_rand(const char* name, char* b, int size)
+{
+  size_t i;
+  for (i=0; i<size/sizeof(char); i++)
+    b[i] = (unsigned char)rand()%256;
+}
+
+/*
+ * producer:
+ *    - waits for work description
+ *    - posts CDO OFFER for all CDOs
+ *    - posts CDO WITHDRAW for all CDOs
+ *    - disposes them
+ *      -- when multiple producer threads run, producer i
+ *         handles CDOs with index%num_consumers==i
+ */
+void*
+producer_thread_fun(void *closure)
+{
+  mstro_status s;
+  size_t my_idx = * (size_t*)closure;
+  INFO("Producer %zu starting\n", my_idx);
+
+  struct cdo_announcement *announcement
+      = malloc(sizeof(struct cdo_announcement));
+  if(announcement==NULL) {
+    ERR("Failed to allocate for incoming announcement\n");
+    abort();
+  }
+
+  wait_for_announcement(announcement);
+
+  /* produce */
+  mstro_cdo outgoing[announcement->num_entries];
+  void *outgoing_buffers[announcement->num_entries];
+
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_producers==my_idx) {
+      /* declare it */
+      s = mstro_cdo_declare(announcement->cdo_names[i],
+                            MSTRO_ATTR_DEFAULT,
+                            &(outgoing[i]));
+      if(s!=MSTRO_OK) {
+        ERR("Failed to declare outgoing CDO %s\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* add allocation */
+      outgoing_buffers[i] = malloc(announcement->cdo_size);
+      if(outgoing_buffers[i]==NULL) {
+        ERR("Cannot allocate outgoing CDO buffer\n");
+        abort();
+      }
+      /* add some data */
+      buf_fill_rand(announcement->cdo_names[i],
+                    outgoing_buffers[i], announcement->cdo_size);
+
+      s = mstro_cdo_attribute_set(outgoing[i],
+                                  MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                                  outgoing_buffers[i]);
+      s |= mstro_cdo_attribute_set(outgoing[i],
+                                   MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                   &announcement->cdo_size);
+
+      if(s!=MSTRO_OK) {
+        ERR("Failed to add outgoing buffer to CDO %s\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* OFFER it */
+      s = mstro_cdo_offer(outgoing[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to offer CDO %s\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+    }
+  }
+  /* now claim them back */
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_producers==my_idx) {
+      s = mstro_cdo_withdraw(outgoing[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to demand outgoing CDO %s\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+
+      s = mstro_cdo_dispose(outgoing[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to dipose outgoing CDO %s\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      free(outgoing_buffers[i]);
+    }
+  }
+
+  free(announcement);
+  return NULL;
+}
+
+
+void
+mvp_checksum(const char* name, void* rawptr, uint64_t size)
+{
+  unsigned char x;
+  uint64_t i;
+
+  for (i=0,x=0;i<size; i++)
+    x ^= ((unsigned char*)rawptr)[i];
+
+  INFO("Checksum for cdo \"%s\": %d\n", name, x);
+}
+
+void
+consumer_flush_to_disk(const char *name, void *a, uint64_t size)
+{
+  char file_name [256];
+  sprintf(file_name, "consumer_%s", name);
+
+  FILE *dst = fopen((const char*)file_name, "w");
+  if(dst==NULL) {
+    ERR("Failed to open %s for writing\n", name);
+    abort();
+  }
+
+  size_t count = fwrite(a,
+                        sizeof(char),
+                        size/sizeof(char),
+                        dst);
+  if(count != size) {
+    ERR("Incomplete write of %s (%zu of  %"PRIu64")\n", file_name, count, size);
+    abort();
+  }
+  if(0!=fclose(dst)) {
+    ERR("Failed to close %s after writing\n", file_name);
+    abort();
+  }
+}
+
+/*
+ * consumer:
+ *    - waits for work description
+ *    - posts CDO REQUIRES for all CDOS in the list
+ *    - demands them one by one
+ *    - computes a checksum on the data, prints it
+ *    - disposes them
+ *      -- when multiple consumer threads run, consumer i
+ *         handles CDOs with index%num_consumers==i
+ */
+void *
+consumer_thread_fun(void *closure)
+{
+  mstro_status s;
+  size_t my_idx = * (size_t*)closure;
+  INFO("Consumer %zu starting\n", my_idx);
+
+  struct cdo_announcement *announcement
+      = malloc(sizeof(struct cdo_announcement));
+  if(announcement==NULL) {
+    ERR("Failed to allocate for incoming announcement\n");
+    abort();
+  }
+
+  wait_for_announcement(announcement);
+
+  /* post requests */
+  mstro_cdo incoming[announcement->num_entries];
+  void *incoming_buffers[announcement->num_entries];
+
+  /* declare and require all that we are responsible for */
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_consumers==my_idx) {
+      /* declare it */
+      s = mstro_cdo_declare(announcement->cdo_names[i],
+                            MSTRO_ATTR_DEFAULT,
+                            &incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to declare CDO %s for consumption\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* add allocation */
+      incoming_buffers[i] = malloc(announcement->cdo_size);
+      if(incoming_buffers[i]==NULL) {
+        ERR("Cannot allocate CDO buffer for comsumption\n");
+        abort();
+      }
+      s = mstro_cdo_attribute_set(incoming[i],
+                                  MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                                  incoming_buffers[i]);
+      s |= mstro_cdo_attribute_set(incoming[i],
+                                   MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                   &announcement->cdo_size);
+      //      INFO("consumer cdo %d incoming buffer %p\n", i, incoming_buffers[i]);
+
+      if(s!=MSTRO_OK) {
+        ERR("Failed to add buffer to CDO %s for consumption\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* require it */
+      s = mstro_cdo_require(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to require CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+    }
+  }
+
+  /* send ACK that we're ready */
+  mstro_cdo ready_cdo;
+
+  INFO("Declaring Consumer %zu ready\n", my_idx);
+  declare_consumer_ready(my_idx,&ready_cdo);
+
+  /* process all in some order */
+  for(size_t i=0; i<announcement->num_entries; i++) {
+    if(i%announcement->num_consumers==my_idx) {
+      s = mstro_cdo_demand(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to demand CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+
+      /* extract raw ptr */
+      void* rawptr;
+      enum mstro_cdo_attr_value_type type;
+      const int64_t* size;
+      s = mstro_cdo_access_ptr(incoming[i], &rawptr, NULL);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to extract raw pointer from CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      /* query the size */
+      s = mstro_cdo_attribute_get(incoming[i],
+                                  MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                  &type, (const void**)&size);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to extract local size from CDO %s for archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+
+      /* do some work */
+      mvp_checksum(announcement->cdo_names[i], rawptr, *size);
+
+      /* write-out */
+      consumer_flush_to_disk(announcement->cdo_names[i],
+                             rawptr, *size);
+
+      s = mstro_cdo_dispose(incoming[i]);
+      if(s!=MSTRO_OK) {
+        ERR("Failed to dispose CDO %s after archiving\n",
+            announcement->cdo_names[i]);
+        abort();
+      }
+      free(incoming_buffers[i]);
+    }
+  }
+
+  /* demand data and process */
+
+
+  cdo_sem_post_cleanup(ready_cdo);
+  free(announcement);
+  return NULL;
+}
+
+
+
+#define DEFAULT_NUM_PRODUCERS 1
+#define DEFAULT_NUM_CONSUMERS 1
+#define DEFAULT_NUM_ARCHIVERS 1
+#define DEFAULT_CDO_SIZE      1024
+#define DEFAULT_CDO_COUNT     42
+
+#define CONFIG_FILE_PATH "./local_pool_op_config.yaml"
+
+/*
+ * main thread:
+ *    - constructs work description
+ *    - posts (announces) a work description CDO for all to see.
+ *    - starts threads for consumer, archiver tasks
+ *    - posts CONSUMER_READY REQUIRE
+ *      -- if multiple consumers, multiple such CDOs
+ *    - posts ARCHIVER_READY REQUIRE
+ *      -- if multiple archivers, multiple such CDOs
+ *    - starts producer
+ *    - waits for threads to finish
+ *    - withdraws work description CDO
+ *    - quits
+*/
+int main(int argc, char **argv)
+{
+ /* verbosity */
+  putenv("MSTRO_LOG_LEVEL=1");
+  putenv("MSTRO_LOG_COLOR_ERRORS=1");
+  putenv("MMB_LOG_LEVEL=0");
+
+  mstro_status s = mstro_init("Single Node Pool Operations",
+                              "Main", 0);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to initialize Maestro\n");
+    return s;
+  }
+
+  struct cdo_announcement announcement;
+  mvp_config config_handle=NULL;
+  int err;
+
+ /* constructs work description */
+  switch(argc) {
+    default:
+      WARN("Command line arguments ignored\n");
+      /* fallthrough: */
+    case 1:
+
+      err = mvp_config_parse(&config_handle, CONFIG_FILE_PATH);
+      if (err != 0) {
+        WARN("Failed to parse a config file. Using default config.\n");
+        announcement.num_producers = DEFAULT_NUM_PRODUCERS;
+        announcement.num_consumers = DEFAULT_NUM_CONSUMERS;
+        announcement.num_archivers = DEFAULT_NUM_ARCHIVERS;
+        announcement.cdo_size = DEFAULT_CDO_SIZE;
+        announcement.num_entries = DEFAULT_CDO_COUNT;
+      }
+      else {
+        announcement.num_producers = config_handle->num_producers;
+        announcement.num_consumers = config_handle->num_consumers;
+        announcement.num_archivers = config_handle->num_archivers;
+        announcement.cdo_size = config_handle->cdo_size;
+        announcement.num_entries = config_handle->cdo_count;
+      }
+
+      break;
+  }
+  assert(announcement.num_entries<=CDO_COUNT_MAX);
+
+  pthread_t producers[announcement.num_producers];
+  pthread_t consumers[announcement.num_consumers];
+  pthread_t archivers[announcement.num_archivers];
+  size_t producer_idxs[announcement.num_producers];
+  size_t consumer_idxs[announcement.num_consumers];
+  size_t archiver_idxs[announcement.num_archivers];
+
+  for(size_t i=0; i<announcement.num_entries; i++) {
+    announcement.cdo_names[i] = malloc(CDO_NAME_MAX);
+    if(announcement.cdo_names[i]==NULL) {
+      ERR("Failed to allocate CDO name\n");
+      goto BAILOUT;
+    }
+    size_t l =
+        snprintf(announcement.cdo_names[i], CDO_NAME_MAX,
+                 CDO_NAME_TEMPLATE, i);
+    if(l>=CDO_NAME_MAX) {
+      ERR("CDO name does not fit:" CDO_NAME_TEMPLATE, i);
+      goto BAILOUT;
+    }
+  }
+
+  announcement_scope_string = malloc(128);
+  if(announcement_scope_string==NULL)
+    abort();
+  snprintf(announcement_scope_string, 128,
+           "scope:\n"
+           "  local-size: %zu", sizeof(struct cdo_announcement));
+
+ /* posts (announces) a work description CDO for all to see */
+  mstro_cdo announcement_cdo;
+  do_announce(&announcement, &announcement_cdo);
+
+
+  /* start consumers */
+  INFO("Starting %zu consumers\n", announcement.num_consumers);
+  for(size_t i=0; i<announcement.num_consumers; i++) {
+    consumer_idxs[i] = i;
+    pthread_create(&consumers[i], NULL,
+                   consumer_thread_fun,
+                   &(consumer_idxs[i]));
+  }
+  /* start archivers */
+  INFO("Starting %zu archivers\n", announcement.num_archivers);
+  for(size_t i=0; i<announcement.num_archivers; i++) {
+    archiver_idxs[i] = i;
+    pthread_create(&archivers[i], NULL,
+                   archiver_thread_fun,
+                   &(archiver_idxs[i]));
+  }
+
+  for(size_t i=0; i<announcement.num_consumers; i++) {
+    INFO("Waiting for Consumer %zu to be ready\n");
+    wait_consumer_ready(i);
+  }
+  for(size_t i=0; i<announcement.num_archivers; i++) {
+    INFO("Waiting for Archiver %zu to be ready\n");
+    wait_archiver_ready(i);
+  }
+
+  /* start producers */
+  INFO("Starting %zu producers\n", announcement.num_producers);
+  for(size_t i=0; i<announcement.num_producers; i++) {
+    producer_idxs[i]=i;
+    pthread_create(&producers[i], NULL,
+                   producer_thread_fun,
+                   &(producer_idxs[i]));
+  }
+
+
+  /* wait for threads to complete */
+  for(size_t i=0; i<announcement.num_producers; i++) {
+    pthread_join(producers[i], NULL);
+  }
+  for(size_t i=0; i<announcement.num_consumers; i++) {
+    pthread_join(consumers[i], NULL);
+  }
+  for(size_t i=0; i<announcement.num_archivers; i++) {
+    pthread_join(archivers[i], NULL);
+  }
+
+  /* withdraw announcement */
+  withdraw_announcement(announcement_cdo);
+
+
+  for(size_t i=0; i<announcement.num_entries; i++) {
+    free(announcement.cdo_names[i]);
+  }
+
+  fprintf(stdout, "Sent %zu CDOs of %" PRIi64 " bytes each from %zu produce to %zu compute and %zu archiving threads\n",
+          announcement.num_entries,
+          announcement.cdo_size,
+          announcement.num_producers,
+          announcement.num_consumers,
+          announcement.num_archivers);
+
+
+
+BAILOUT:
+  if(config_handle) free(config_handle);
+
+  return s;
+}
diff --git a/examples/local_pool_op_config.yaml b/examples/local_pool_op_config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5b801519b7a027f98541937641468975c077bb06
--- /dev/null
+++ b/examples/local_pool_op_config.yaml
@@ -0,0 +1,5 @@
+num_producers: 1
+num_consumers: 1
+num_archivers: 1
+cdo_size: 1024
+cdo_count: 42
diff --git a/examples/omp_consumer.c b/examples/omp_consumer.c
new file mode 100644
index 0000000000000000000000000000000000000000..8150eaa4f047fb9cd01dd0317642985f0377a5f0
--- /dev/null
+++ b/examples/omp_consumer.c
@@ -0,0 +1,217 @@
+/* Connect to pool and subscribe to many events, archiving all CDOs */
+/*
+ * Copyright (C) 2021 Hewlett Packard Enterprise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "omp_consumer.h"
+#include "omp_injector.h"
+
+int convert_consumer_mode(const char * consumer_mode_env)
+{
+  int result = -1;
+
+  if (strcmp(consumer_mode_env, "MSTRO_CONSUMER_SINK_ALL") == 0)
+  {
+    result = 0;
+  }
+  else if (strcmp(consumer_mode_env, "MSTRO_CONSUMER_ONE2ONE") == 0)
+  {
+    result = 1;
+  }
+  else if (strcmp(consumer_mode_env, "MSTRO_CONSUMER_ONE2TEN") == 0)
+  {
+    result = 2;
+  }
+  else if (strcmp(consumer_mode_env, "MSTRO_CONSUMER_ALL2ALL") == 0)
+  {
+    result = 3;
+  }
+  return result;
+}
+
+int get_consumer_mode()
+{
+  char * consumer_mode_env;
+  int consumer_mode = 0;
+  //get current consumer mode
+  if (getenv("MSTRO_CONSUMER_MODE") != NULL)
+  {
+    consumer_mode_env = getenv("MSTRO_CONSUMER_MODE");
+    consumer_mode = convert_consumer_mode(consumer_mode_env);
+  }
+
+  return consumer_mode;
+}
+
+int get_num_producers(int size, int nConsumers, int consumer_mode)
+{
+  int num_producers = 0;
+
+  //calculate number of producers
+  switch(consumer_mode)
+  {
+    case MSTRO_CONSUMER_SINK_ALL:
+    case MSTRO_CONSUMER_ALL2ALL:
+      //sink all data from all producer ranks, i.e. all ranks - PM and nconsumers
+      num_producers = size - 1 - nConsumers;
+      break;
+    case MSTRO_CONSUMER_ONE2ONE:
+      //sink all data from only one producer
+      num_producers = 1;
+      break;
+
+    case MSTRO_CONSUMER_ONE2TEN:
+      //sink all data from 10 producer ranks
+      num_producers = 10;
+      break;
+
+    default:
+      ERR("Incorrect MSTRO_CONSUMER_MODE \n");
+      break;
+  }
+
+  return num_producers;
+
+}
+
+
+// producer_ids and num_producers are outputs
+void get_producers(int rank, int size, int nConsumers, int *producers_ids, int num_producers, int consumer_mode)
+{
+
+
+  //calculate number of producers
+  switch(consumer_mode)
+  {
+    case MSTRO_CONSUMER_SINK_ALL:
+    case MSTRO_CONSUMER_ALL2ALL:
+      //sink all data from all producer ranks, i.e. all ranks - PM and nconsumers
+      //calculate producers
+      for(int i = 0; i< num_producers; i++)
+      {
+        producers_ids[i] = nConsumers + 1 + i;
+      }
+      break;
+    case MSTRO_CONSUMER_ONE2ONE:
+      //sink all data from only one producer
+      //asumming nProducers == nConsumers, my assignment is shifted by my rank
+      producers_ids[0] = nConsumers + rank;
+      break;
+
+    case MSTRO_CONSUMER_ONE2TEN:
+      //sink all data from 10 producer ranks
+      //calculate producers
+      for(int i = 0; i< num_producers; i++)
+      {
+        producers_ids[i] = ((rank-1) * num_producers) + 1 + i;
+      }
+      break;
+
+    default:
+      ERR("Incorrect MSTRO_CONSUMER_MODE \n");
+      break;
+  }
+
+}
+
+
+mstro_status require_CDOs(mstro_cdo *cdos, int num_CDOs, int *injector_ids, int num_injectors, size_t num_attributes, size_t size_attributes)
+{
+
+  mstro_status s = MSTRO_OK; // global status
+  int CDOs_per_inj = num_CDOs / num_injectors ;
+  size_t cdoidx, cdo_gid;
+
+
+  // declare CDOs loop
+  #pragma omp parallel for private(cdoidx, cdo_gid) reduction(| :s)
+  for(size_t i=0; i < num_injectors; i++)
+  {
+
+    DEBUG("consuming injector %d \n", injector_ids[i]);
+
+
+    for(size_t j=0; j < CDOs_per_inj; j++)
+    {
+      mstro_status s1,s2, s3;
+      cdoidx = injector_ids[i] *CDOs_per_inj + j; // CDO id = injector_id * thread_id * num_CDOs
+      cdo_gid = i*CDOs_per_inj + j;
+      char name[CDO_NAME_MAX];
+      create_name(name, cdoidx);
+
+      s3 = MSTRO_OK; // initialize its value
+      s1 = mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, & (cdos[cdo_gid]));
+
+
+      //FIXME We do not want extra attributes now --need to merge with different branch
+      // create attributes ...numbers and sizes
+      for(size_t j=0; j < num_attributes ; j++)
+      {
+        char attrib_name[ATTRIB_NAME_MAX];
+        void * data = malloc(size_attributes*sizeof(char));
+        snprintf(attrib_name, ATTRIB_NAME_MAX, ".maestro.benchmark.attrib_%lu", j+1);
+        s3 |= mstro_cdo_attribute_set(cdos[i], attrib_name, data);
+        DEBUG("%d: %s \n", s3, mstro_status_description(s3));
+      }
+
+
+      s3 |= mstro_cdo_declaration_seal(cdos[cdo_gid]);
+
+      s2= mstro_cdo_require(cdos[cdo_gid]);
+      DEBUG("[consumer] requiring %s \n", mstro_cdo_name(cdos[cdo_gid]));
+
+      s = s | s1 | s2 | s3;
+    }
+
+  }
+
+  return s;
+}
+
+
+mstro_status demand_CDOs(mstro_cdo *cdos, int num_CDOs)
+{
+
+  mstro_status s = MSTRO_OK; // global status
+
+  #pragma omp parallel for reduction(| :s)
+  for(size_t i=0; i < num_CDOs; i++)
+  {
+    mstro_status s3,s4;
+
+    s3= mstro_cdo_demand(cdos[i]);
+    DEBUG("Hey, I recieved %s \n", mstro_cdo_name(cdos[i]));
+    s4= mstro_cdo_dispose(cdos[i]);
+    s = s | s3 | s4 ;
+  }
+  return s;
+
+}
diff --git a/examples/omp_consumer.h b/examples/omp_consumer.h
new file mode 100644
index 0000000000000000000000000000000000000000..ffe6a4348bf2397622dcb819f53039c3c4a756fc
--- /dev/null
+++ b/examples/omp_consumer.h
@@ -0,0 +1,68 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief openMP injector component
+ **/
+
+/*
+ * Copyright (C) 2021 Hewlett Packard Enterprise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+ #ifndef mstro_consumer
+ #define mstro_consumer 1
+
+
+ #define MSTRO_CONSUMER_SINK_ALL 0
+ #define MSTRO_CONSUMER_ONE2ONE  1
+ #define MSTRO_CONSUMER_ONE2TEN  2
+ #define MSTRO_CONSUMER_ALL2ALL  3
+
+ #include "maestro.h"
+ #include "maestro/logging.h"
+ #include <string.h>
+ #include <unistd.h>
+ #include <inttypes.h>
+
+ /* simplify logging */
+ #define DEBUG(...) LOG_DEBUG(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+ #define INFO(...)  LOG_INFO(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+ #define WARN(...)  LOG_WARN(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+ #define ERR(...)   LOG_ERR(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+
+
+
+mstro_status require_CDOs(mstro_cdo *cdos, int num_CDOs, int *injector_ids, int num_injectors, size_t num_attributes, size_t size_attributes);
+mstro_status demand_CDOs(mstro_cdo *cdos, int num_CDOs);
+void get_producers(int rank, int size, int nConsumers, int *producers_ids, int num_producers, int consumer_mode);
+int convert_consumer_mode(const char * consumer_mode_env);
+int get_num_producers(int size, int nConsumers, int consumer_mode);
+int get_consumer_mode();
+
+#endif
diff --git a/examples/omp_injector.c b/examples/omp_injector.c
new file mode 100644
index 0000000000000000000000000000000000000000..c35d2323b8596ba398f4e255412cd5152889142a
--- /dev/null
+++ b/examples/omp_injector.c
@@ -0,0 +1,179 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief openMP injector component
+ **/
+
+/*
+ * Copyright (C) 2021 Hewlett Packard Enterprise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include "omp_injector.h"
+
+
+
+void create_name(char *dst, size_t idx)
+{
+      snprintf(dst, CDO_NAME_MAX, "Test CDO %zu\n", idx);
+}
+
+
+mstro_status inject_CDOs(int injector_id, mstro_cdo *cdos, int num_CDOs, size_t num_attributes, size_t size_attributes, size_t size_CDO, char **CDO_data)
+{
+
+  mstro_status s = MSTRO_OK; // global status
+
+  size_t cdoidx;
+
+  // declare CDOs loop
+  #pragma omp parallel for private(cdoidx) reduction(| :s)
+  for(size_t i=0; i < num_CDOs; i++)
+  {
+    mstro_status s1,s2, s3;
+    cdoidx = injector_id *num_CDOs + i; // CDO id = injector_id * thread_id * num_CDOs
+    char name[CDO_NAME_MAX];
+    create_name(name, cdoidx);
+
+    s3 = MSTRO_OK; // initialize its value
+    s1 = mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, & (cdos[i]));
+
+
+    // create attributes ...numbers and sizes
+    for(size_t j=0; j < num_attributes ; j++)
+    {
+      char attrib_name[ATTRIB_NAME_MAX];
+      void * data = malloc(size_attributes*sizeof(char));
+      snprintf(attrib_name, ATTRIB_NAME_MAX, ".maestro.benchmark.attrib_%lu", j+1);
+      s3 |= mstro_cdo_attribute_set(cdos[i], attrib_name, data);
+      DEBUG("%d: %s \n", s3, mstro_status_description(s3));
+    }
+
+
+    // add data
+    if (size_CDO != 0)
+    {
+      s3 |= mstro_cdo_attribute_set(cdos[i],
+                                  MSTRO_ATTR_CORE_CDO_RAW_PTR,
+                                  CDO_data[i]);
+      s3 |= mstro_cdo_attribute_set(cdos[i],
+                                   MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                   &size_CDO);
+    }
+
+
+    s3 |= mstro_cdo_declaration_seal(cdos[i]);
+
+    s2= mstro_cdo_offer(cdos[i]);
+    DEBUG("[injector] offering %s \n", mstro_cdo_name(cdos[i]));
+
+    s = s | s1 | s2 | s3;
+  }
+
+  return s;
+
+}
+
+void fill_char_array(char *array, size_t size)
+{
+  for (size_t i = 0; i < size; i++) {
+    array[i] = 'x';
+  }
+}
+
+mstro_status withdraw_CDOs(mstro_cdo *cdos, int num_CDOs)
+{
+
+  mstro_status s = MSTRO_OK; // global status
+
+  #pragma omp parallel for reduction(| :s)
+  for(size_t i=0; i < num_CDOs; i++) {
+    mstro_status s3,s4;
+
+    s3= mstro_cdo_withdraw(cdos[i]);
+    s4= mstro_cdo_dispose(cdos[i]);
+    s = s | s3 | s4 ;
+  }
+  return s;
+
+}
+
+
+mstro_status start_injector(int injector_id, size_t num_attributes, size_t size_attributes, size_t CDOs_per_thread)
+{
+
+  double declare_time,withdraw_time, before, after;
+  size_t num_threads = 1;
+
+  // Read environment variables
+  if (getenv("OMP_NUM_THREADS") != NULL)
+  {
+    num_threads = atoi(getenv("OMP_NUM_THREADS"));
+  }
+
+  mstro_status s = MSTRO_OK; // global status
+
+
+  size_t num_CDOs = CDOs_per_thread * num_threads; // 1K per producer * (num_thread)
+
+  s = mstro_init("Tests","Injector",injector_id);
+
+  assert(MSTRO_OK == s);
+
+
+
+  mstro_cdo cdos[num_CDOs];
+
+  before = omp_get_wtime();
+  // declare CDOs loop
+  s = inject_CDOs(injector_id, cdos, num_CDOs, num_attributes, size_attributes, 0, NULL);
+
+  assert(MSTRO_OK == s);
+
+  after = omp_get_wtime();
+  declare_time = (after - before) * 1000.0*1000.0; //time in us seconds
+
+  before = after; // restart timer here
+  // withdraw CDOs loop
+  s = withdraw_CDOs(cdos, num_CDOs);
+
+  assert(MSTRO_OK == s);
+
+  after = omp_get_wtime();
+
+  withdraw_time = (after - before) * 1000.0*1000.0; //time in us seconds
+  fprintf(stdout, "#CDOs: %zu, #Threads: %zu, #Attributes: %zu, Size of attributes: %zu \n", num_CDOs, num_threads, num_attributes, size_attributes);
+  fprintf(stdout, "Throughput (declare/offer): %.5f us\n", declare_time/(double) num_CDOs);
+  fprintf(stdout, "Throughput (withdraw/dispose): %.5f us\n", withdraw_time/(double) num_CDOs);
+
+  s |= mstro_finalize();
+
+  return s;
+}
diff --git a/examples/omp_injector.h b/examples/omp_injector.h
new file mode 100644
index 0000000000000000000000000000000000000000..23104f276dd88381bd00f7ab16061d7be8010d8f
--- /dev/null
+++ b/examples/omp_injector.h
@@ -0,0 +1,66 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief openMP injector component
+ **/
+
+/*
+ * Copyright (C) 2021 Hewlett Packard Enterprise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+ #ifndef omp_injector
+ #define omp_injector 1
+
+#include "maestro.h"
+#include <assert.h>
+#include "maestro/logging.h"
+#include <string.h>
+#include "omp.h"
+
+
+#define CDO_NAME_MAX 32
+#define ATTRIB_NAME_MAX 128
+
+/* simplify logging */
+#define DEBUG(...) LOG_DEBUG(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define INFO(...)  LOG_INFO(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define WARN(...)  LOG_WARN(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+#define ERR(...)   LOG_ERR(MSTRO_LOG_MODULE_USER,__VA_ARGS__)
+
+
+
+void create_name(char *dst, size_t idx);
+
+mstro_status start_injector(int injector_id, size_t num_attributes, size_t size_attributes, size_t CDOs_per_thread);
+mstro_status inject_CDOs(int injector_id, mstro_cdo *cdos, int num_CDOs, size_t num_attributes, size_t size_attributes, size_t size_CDO, char **CDO_data);
+mstro_status withdraw_CDOs(mstro_cdo *cdos, int num_CDOs);
+void fill_char_array(char *array, size_t size);
+
+#endif
diff --git a/include/maestro/attributes.h b/include/maestro/attributes.h
index ecd390a637ccf41a8f81b082e0304f85cc8a6d21..5a66bafe6027b168b4c24c97da95f6e3327df601 100644
--- a/include/maestro/attributes.h
+++ b/include/maestro/attributes.h
@@ -68,6 +68,15 @@ typedef struct mstro_attribute_dict_* mstro_cdo_attributes;
    Predefined symbolic key strings exist for the well-known attributes
 **/
 
+/**@brief maestro.core.cdo.name
+ **
+ ** The name of the CDO
+ **
+ ** C-side data type: `char *'
+ ** default value: false (set automatically by mstro_cdo_declare())
+ **/
+extern const char *MSTRO_ATTR_CORE_CDO_NAME;
+
 /**@brief maestro.core.cdo.allocate-now
  **
  ** Indicate that Maestro should perform allocation before DECLARE completes
@@ -305,18 +314,20 @@ typedef struct mstro_cdo_ *mstro_cdo;
 /**
  ** @brief Add (*key*, *val*) pair to attribute set of *cdo*
  **
- ** @param[in] 	cdo	A CDO handle
- ** @param[in]	key	Attribute key string
- ** @param[in]  val	Pointer to the value to be set
+ ** @param[in] 	cdo			A CDO handle
+ ** @param[in]	key			Attribute key string
+ ** @param[in]  val			Pointer to the value to be set
+ ** @param[in]	copy_value  Create an internal allocation for the value and
+ **							copy @arg val into it 
  **
- ** BEWARE: The memory pointed to by val must remain valid for the
- ** entire lifetime of the CDO. Stack-allocated variables passed in by
- ** address are a source of ugly to trace bugs.
+ ** BEWARE: If copy_value is set to false, the memory pointed to by val must
+ ** remain valid for the entire lifetime of the CDO. Stack-allocated variables
+ ** passed in by address are a source of ugly to trace bugs.
  **
  ** @returns A status code, ::MSTRO_OK on success.
  **/
 mstro_status
-mstro_cdo_attribute_set(mstro_cdo cdo, const char* key, void* val);
+mstro_cdo_attribute_set(mstro_cdo cdo, const char* key, void* val, bool copy_value);
 
 /**
  ** @brief Retrieve value into *val_p* associated with *key* of *cdo*.
diff --git a/include/maestro/core.h b/include/maestro/core.h
index 4954c18343e471514f1471f8cf1de7bbc5ad4453..5a64354e19c7edc39c868fedbf19579596589bc8 100644
--- a/include/maestro/core.h
+++ b/include/maestro/core.h
@@ -44,12 +44,12 @@ extern "C" {
   #include "maestro/status.h"
   #include <stdint.h>
   #include <inttypes.h>
-  
+
   /**@defgroup MSTRO_Core Maestro Core API
    **@{
    ** This is the core API, as developed for D3.2
    **/
-  
+
   /**
    ** @brief Initialize the maestro core
    **
@@ -74,7 +74,7 @@ extern "C" {
    ** @param[in] workflow_name     The workflow ID.
    **
    ** @param[in] component_name    The component ID.
-   ** 
+   **
    ** @param[in] component_index   The unique index among all processes
    **                          using the same *component_name*.
    **
@@ -84,7 +84,7 @@ extern "C" {
   mstro_core_init(const char *workflow_name,
                   const char *component_name,
                   uint64_t component_index);
-  
+
   /**
    ** @brief De-initialize the maestro core
    **
@@ -103,8 +103,9 @@ extern "C" {
    **/
   #define PRIappid PRIu64
 
+
   /**@} (end of group MSTRO_Core) */
-  
+
   /* include the remaining public API */
 #include "maestro/cdo.h"
 #include "maestro/pool.h"
@@ -112,10 +113,9 @@ extern "C" {
 #include "maestro/attributes.h"
 #include "maestro/groups.h"
 #include "maestro/env.h"
-  
+
 
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif
 #endif /* MAESTRO_CORE_H_ */
-
diff --git a/include/maestro/env.h b/include/maestro/env.h
index 0d95b73a21a19b4fbae924559b4183bcd904d61d..8129cba1f79033118804603a93a4708ec8065e14 100644
--- a/include/maestro/env.h
+++ b/include/maestro/env.h
@@ -69,7 +69,7 @@
  * all modules are permitted to log. This variable can be set to
  * selectively disable or enable certain modules.
  *
- * The syntax is 
+ * The syntax is
  *   component1,component2,^component3,...
  *
  * where 'component1' indicates that it should be included, and
@@ -96,6 +96,20 @@
  */
 #define MSTRO_ENV_LOG_COLOR "MSTRO_LOG_COLOR"
 
+/**@brief lists the set of Schemata (in order from left to right) that should be loaded after the maestro-core schema is loaded.
+ * If set,mstro_init will merge these onto the built-ins. Default: core + ecmwf
+ * example "export MSTRO_SCHEMA_LIST=ecmwf.yaml;benchmark.yaml"
+*/
+#define MSTRO_ENV_SCHEMA_LIST "MSTRO_SCHEMA_LIST"
+
+
+/**@brief path to search for schemata (in order from left to right).
+ *  Default: current directory "."
+ * example export MSTRO_SCHEMA_PATH=".:/usr/share/maestro/schemata"
+*/
+#define MSTRO_ENV_SCHEMA_PATH "MSTRO_SCHEMA_PATH"
+
+
 /**@brief enable coloring of error log messages
  *
  * If set, enable coloring of error and warning messages. May not be a good idea if logging to syslog (see @ref MSTRO_LOG_DST).
@@ -191,12 +205,23 @@
 #define MSTRO_ENV_TRANSPORT_DEFAULT "MSTRO_TRANSPORT_DEFAULT"
 
 
+/**
+ ** @brief Directory for GFS transport
+ **
+ ** Must be visible on all workflow components that want to use GFS
+ ** transport. Should be a high-performance global file system location.
+ **
+ ** Default is "./", the directory where the application is started
+ ** (which will often work, but not always).
+ **/
+#define MSTRO_ENV_TRANSPORT_GFS_DIR "MSTRO_TRANSPORT_GFS_DIR"
+
 /**
  ** @brief Path to the MIO transport config file 
  **
- ** This is needed to initialize MIO 
+ ** This is needed to initialize MIO
  **
- **/ 
+ **/
 #define MSTRO_ENV_MIO_CONFIG "MSTRO_MIO_CONFIG"
 
 /**@} (end of group MSTRO_ENV) */
diff --git a/include/maestro/i_cdo.h b/include/maestro/i_cdo.h
index d4e2fbd4203dee4eda01434746b401bca5126270..8200877e463b4c04c228329eded3f8d793f8949e 100644
--- a/include/maestro/i_cdo.h
+++ b/include/maestro/i_cdo.h
@@ -156,7 +156,14 @@ typedef uint32_t mstro_cdo_state;
 /** The NIL UUID */
 #define MSTRO_CDO_ID_INVALID { .id = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 } }
 
-/** return a string describing the CDO state @arg s */
+/** return a string describing the CDO state @arg s
+ **
+ ** The string is owned by this function, but this function can safely
+ ** be called from multiple threads (the buffer is thread-local). The
+ ** buffer will be re-used by subsequent calls in the same thread
+ ** after a small number of invocations (currently: 3, see @ref
+ ** MAX_STATE_DESCRIPTION_BUFFERS).
+ **/
 const char *
 mstro_cdo_state_describe(mstro_cdo_state s);
 
@@ -321,6 +328,10 @@ struct mstro_cdo_ {
 
   char *name;                    /**< user-provided name */
 
+  int preferred_numa_node;       /**< the preferred NUMA node for this CDO. -1
+				   for don't care. Typically set from raw-ptr
+				   or mamba-array information */
+
   /* Cached copies of the data attributes. Caching occirs at SEAL
    * time, after which the attribute setters will refuse chaning the
    * dictionary entries, so caching is safe */
diff --git a/include/maestro/i_globals.h b/include/maestro/i_globals.h
index 60c432eb04abf1e235611fad00ba8ad2776796d0..75fab2a90c9821cb72f43e5a8601fc4584a1845d 100644
--- a/include/maestro/i_globals.h
+++ b/include/maestro/i_globals.h
@@ -95,7 +95,7 @@ extern mmbMemSpace *g_default_cdo_memspace;
 
 /** the default (DRAM) memory interface */
 extern mmbMemInterface *g_default_cdo_interface;
-   
+
 /** MIO can be compiled in, but run time config may be missing, or MIO may fail
  * to initialize. This flag is TRUE if MIO is properly initialized and usable
  * */
@@ -105,12 +105,14 @@ extern bool g_mio_available;
 /** the app token used in communicating to the pool manager. Set after successfully JOINing, cleared after LEAVEing */
 extern Mstro__Pool__Apptoken g_pool_apptoken;
 
-/** the app ID (packed version of @ref g_pool_app_id) used in communicating with the pool manager. */ 
+/** the app ID (packed version of @ref g_pool_app_id) used in communicating with the pool manager. */
 extern Mstro__Pool__Appid    g_pool_appid;
 
 /** the fundamental built-in attribute schema. Filled early in mstro_core_init(), then constant */
 extern mstro_schema g_mstro_core_schema_instance;
 
+/** flag set if numa is working */
+extern bool g_have_numa;
 
 /** flag set if we are connected to a pool manager service */
 extern bool g_have_pool_manager;
@@ -143,6 +145,8 @@ union mstro_component_descriptor {
     char workflow_name[MSTRO_WORKFLOW_NAME_MAX];
     /** the component name */
     char component_name[MSTRO_WORKFLOW_NAME_MAX];
+    /** the list of user schemas */
+    char schema_list[MSTRO_WORKFLOW_NAME_MAX];
     /** the maestro core version */
     char version[128];
   }; /*<** descriptor data */
@@ -159,4 +163,14 @@ extern union mstro_component_descriptor g_component_descriptor;
          | (MSTRO_POOL_PROTOCOL_VERSION_MINOR << 8)    \
          | (MSTRO_POOL_PROTOCOL_VERSION_PATCH << 0)))
 
+/** separators for user defined schema lists and paths enviroment variables */
+#define SCHEMA_LIST_SEP ";"
+#define SCHEMA_PATH_SEP ":"
+
+/** the precomputed default path for GFS transport (incl. trailing '/') */
+extern char *g_mstro_transport_gfs_dir;
+/** string length of @ref g_mstro_transport_gfs_dir */
+extern size_t g_mstro_transport_gfs_dir_len;
+
+
 #endif /* MAESTRO_I_GLOBALS_H_ */
diff --git a/maestro/Makefile.am b/maestro/Makefile.am
index 26b9feedcccab128d44ca5b7f9347932cd1197dd..e85e2723928c4a64a01f6db4e5aec1f2bef776ff 100644
--- a/maestro/Makefile.am
+++ b/maestro/Makefile.am
@@ -68,6 +68,7 @@ libmaestro_core_la_SOURCES = \
 	i_event.h event.c \
 	cdo_sel_parse.c cdo_sel_parse.h \
 	i_groups.h groups.c \
+	i_maestro_numa.h \
 	memlock.c \
         cdo-attributes-default.txt 
 
@@ -78,6 +79,10 @@ cdo_sel_parse.c cdo_sel_parse.h: cdo_sel_parse.peg
 
 libmaestro_core_la_LIBADD=$(top_builddir)/deps/libcyaml/libcyaml.la $(top_builddir)/deps/mamba/libmmb.la $(top_builddir)/deps/c-timestamp/libtimestamp.la
 
+if NUMA_ENABLED
+libmaestro_core_la_LIBADD+=-lnuma
+endif
+
 # Conductor implementation varies depending on configuration
 if WITH_OFI_POOL_MANAGER
 libmaestro_core_la_SOURCES+=pool_manager_ofi.c ofi.c drc.c
diff --git a/maestro/attributes.c b/maestro/attributes.c
index e9105e845c3564fbe1abd80ec21a919ae77a4b7b..501170a41d27247172d53cbff399ab8567562a55 100644
--- a/maestro/attributes.c
+++ b/maestro/attributes.c
@@ -50,6 +50,9 @@
 
 /* FIXME: we should have a table of name/type/... and make the const
  * char* refer to the entries in that */
+const char *MSTRO_ATTR_CORE_CDO_NAME =
+    ".maestro.core.cdo.name";
+
 const char *MSTRO_ATTR_CORE_CDO_ALLOCATE_NOW =
     ".maestro.core.cdo.allocate-now";
 
@@ -124,7 +127,7 @@ mstro_cdo_attr_table__destroy(mstro_cdo_attr_table tab)
 }
 
 mstro_status
-mstro_cdo_attribute_set(mstro_cdo cdo, const char* key, void* val)
+mstro_cdo_attribute_set(mstro_cdo cdo, const char* key, void* val, bool copy_value)
 {
   if(cdo==NULL || key==NULL)
     return MSTRO_INVARG;
@@ -160,7 +163,7 @@ mstro_cdo_attribute_set(mstro_cdo cdo, const char* key, void* val)
   mstro_status status
       = mstro_attribute_dict_set(cdo->attributes, fqkey,
                                  MSTRO_CDO_ATTR_VALUE_INVALID, /* we dont help in checking */
-                                 val, false);
+                                 val, copy_value);
   if(tmpfqkey)
     free(tmpfqkey);
   return status;
diff --git a/maestro/cdo.c b/maestro/cdo.c
index 37f25ccaacc2b9e92ac52866c76956aded77c3ec..4a340c8a7349e6658470ccf3194d4479ca9435a8 100644
--- a/maestro/cdo.c
+++ b/maestro/cdo.c
@@ -46,6 +46,7 @@
 
 #include "maestro/status.h"
 #include "maestro/i_statistics.h"
+#include "i_maestro_numa.h"
 
 #include "transformation/transformation.h"
 #include <mamba.h>
@@ -144,6 +145,9 @@ mstro_cdo__alloc(void)
   }
   /* we need to zero out the hash-table relevant parts. For simplicity we zero everything */
   memset(res, 0, sizeof(struct mstro_cdo_));
+
+  /* default: same region as handle is in */
+  res->preferred_numa_node = mstro_numa__node_for_addr(res);
   
   /* initialize parts that need special handling */
   atomic_init(&(res->state), MSTRO_CDO_STATE_INVALID);
@@ -448,10 +452,10 @@ mstro_cdo__create_mamba_array(mstro_cdo cdo)
   int64_t* ndims, *patt, *localsz, *elsz;
   size_t* dimsz;
 
-  DEBUG("Sync'ing mamba_array and attributes (CDO `%s`)\n", cdo->name);
-
   s = mstro_cdo_attribute_get(cdo, MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE, NULL, (const void**)&localsz);
 
+  DEBUG("Sync'ing mamba_array (size: %zu) and attributes (CDO `%s`)\n", (size_t)*localsz, cdo->name);
+
   /* fetching minimal set of attributes for custom layout */
   s |= mstro_cdo_attribute_get(cdo, MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE,
                                NULL, (const void**)&elsz);
@@ -524,6 +528,7 @@ mstro_cdo__create_mamba_array(mstro_cdo cdo)
     }
     
     if(me != MMB_OK) {
+      status = MSTRO_FAIL;
       ERR("Failed to create mamba array\n");
       goto BAILOUT;
     }
@@ -639,6 +644,47 @@ mstro_cdo_declaration_seal(mstro_cdo cdo)
       goto BAILOUT;
     }
   }
+
+  /* Ensure a sane name is filled in. The attribute is, in fact, only
+   * needed for subscription handling (so users can match on names the
+   * same way that they can for other attributes).
+   *
+   * The user does not typically set the name attribute explicitly,
+   * but if they do we need to ensure it's not in disagreement with
+   * the name specified at mstro_cdo_declare() time. */
+  const void *cdo_name_attrval=NULL;
+  enum mstro_cdo_attr_value_type valtype;
+  
+  mstro_status s_cdoname = mstro_attribute_dict_get(
+      cdo->attributes, MSTRO_ATTR_CORE_CDO_NAME,
+      &valtype, &cdo_name_attrval, NULL, false);
+  if(s_cdoname==MSTRO_OK || s_cdoname==MSTRO_NOENT) {
+    const char *n_cdo = mstro_cdo_name(cdo);
+    if(s_cdoname==MSTRO_OK) {
+      /* ok, already have a name, check it matches */
+      const char *n_attr = (const char *)cdo_name_attrval;
+      
+      if(0!=strcmp(n_cdo,n_attr)) {
+        ERR("CDO |%s| has name attribute set to |%s|, overriding\n",
+            cdo->name, cdo_name_attrval);
+      }
+    }
+    
+    s_cdoname = mstro_attribute_dict_set(cdo->attributes,
+                                         MSTRO_ATTR_CORE_CDO_NAME,
+                                         MSTRO_CDO_ATTR_VALUE_cstring,
+                                         &n_cdo, true);
+    if(s_cdoname!=MSTRO_OK) {
+      ERR("Failed to set CDO name attribute: %d (%s)\n", s_cdoname,
+          mstro_status_description(s_cdoname));
+      status = s_cdoname;
+      goto BAILOUT;
+    }
+  } else {
+    ERR("Failed to inquire about CDO name attribute\n");
+    status = s_cdoname;
+    goto BAILOUT;
+  }
   
   /* we're done, but we need to ensure we've seen the DECLARE_ACK with
    * the global ID by now */
@@ -701,8 +747,8 @@ mstro_cdo_declaration_seal(mstro_cdo cdo)
       idstr, &cdo->id,
       DEBUG("CDO %s (id %s) now sealed\n",
            cdo->name, idstr););
-  
-  status = MSTRO_OK;
+
+   status = MSTRO_OK;
 
 BAILOUT:
   return status;
@@ -1592,29 +1638,43 @@ struct {
 /** a reasonably safe way to count the number of entries in an array of structures */
 #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
 
+
+/** we support this many concurrent state buffers in flight per
+ * thread. Hideous, but callers typically will use this function in a
+ * PRINT statement, with up to two states (to compare them) at most
+ */
+
+#define MAX_STATE_DESCRIPTION_BUFFERS 3
 const char *
 mstro_cdo_state_describe(mstro_cdo_state s)
 {
-  static _Thread_local char buf[sizeof(states_and_names)];
+  static _Thread_local char buf[MAX_STATE_DESCRIPTION_BUFFERS][sizeof(states_and_names)];
+  static _Thread_local size_t bufid = 0;
 
-  buf[0]='\0';
+  // switch to new buffer
+  bufid = (bufid+1) % MAX_STATE_DESCRIPTION_BUFFERS;
+  
+  buf[bufid][0]='\0';
 
   if(s==MSTRO_CDO_STATE_INVALID)
     return "INVALID";
   else {
     for(size_t i=0; i<COUNT_OF(states_and_names); i++) {
       if(s&states_and_names[i].state) {
-        strcat(buf, states_and_names[i].name);
+        strcat(buf[bufid], states_and_names[i].name);
       }
     }
-    return buf+1; /* cut off leading '|' we put there for simplicity
+    
+    return buf[bufid]+1; /* cut off leading '|' we put there for simplicity
                    * of the loop above */
   }
 }
 
 mstro_status
-mstro_cdo_attributes_update_incoming(mstro_cdo cdo, const Mstro__Pool__Attributes *attr,
-                                     size_t num_precious_attr, const char **precious_attributes)
+mstro_cdo_attributes_update_incoming(mstro_cdo cdo,
+                                     const Mstro__Pool__Attributes *attr,
+                                     size_t num_precious_attr,
+                                     const char **precious_attributes)
 {
   if(cdo==NULL || attr==NULL) 
     return MSTRO_INVARG;
@@ -1791,7 +1851,7 @@ mstro_cdo_allocate_data(mstro_cdo cdo)
                                   MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE,
                                   MSTRO_CDO_ATTR_VALUE_pointer, dimsz, true);
     if (s != MSTRO_OK) {
-      ERR("Can't sync mamba array because attribute_set failed\n");
+      ERR("Can't sync mamba array because dict_set failed\n");
       return MSTRO_FAIL;
     }
   } else {
@@ -1815,6 +1875,7 @@ mstro_cdo_allocate_data(mstro_cdo cdo)
     }
     /* now we know a size */
     int64_t size = *(const int64_t*)valp;
+	DEBUG("size (%" PRIi64 ")\n", size);
  
     if(size<0 && cdo->raw_ptr!=NULL) {
       ERR("CDO |%s| has negative local-size but non-NULL raw-ptr, unsupported\n",
@@ -1827,6 +1888,8 @@ mstro_cdo_allocate_data(mstro_cdo cdo)
       ERR("Failed to create mmbArray wrapper for raw-ptr\n");
       return s;
     }
+	DEBUG("size (%" PRIi64 ")\n", size);
+
   }
   
   return MSTRO_OK;
diff --git a/maestro/cdo_sel_parse.c b/maestro/cdo_sel_parse.c
index a8612ff82ebce2272e71cd7ef1ecf188618e4fdb..c6f1aa9215996e53817aa83f3e60c16cfca2cb2b 100644
--- a/maestro/cdo_sel_parse.c
+++ b/maestro/cdo_sel_parse.c
@@ -1429,7 +1429,23 @@ static void pcc_action_value_1(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pc
 static void pcc_action_value_2(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pcc_in, pcc_value_t *__pcc_out) {
 #define auxil (__pcc_ctx->auxil)
 #define __ (*__pcc_out)
-#define i (*__pcc_in->data.leaf.values.buf[2])
+#define n (*__pcc_in->data.leaf.values.buf[2])
+#define _0 pcc_get_capture_string(__pcc_ctx, &__pcc_in->data.leaf.capt0)
+#define _0s ((const)__pcc_in->data.leaf.capt0.range.start)
+#define _0e ((const)__pcc_in->data.leaf.capt0.range.end)
+    __=n; DEBUG("numeric value\n");
+#undef _0e
+#undef _0s
+#undef _0
+#undef n
+#undef __
+#undef auxil
+}
+
+static void pcc_action_value_3(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pcc_in, pcc_value_t *__pcc_out) {
+#define auxil (__pcc_ctx->auxil)
+#define __ (*__pcc_out)
+#define i (*__pcc_in->data.leaf.values.buf[3])
 #define _0 pcc_get_capture_string(__pcc_ctx, &__pcc_in->data.leaf.capt0)
 #define _0s ((const)__pcc_in->data.leaf.capt0.range.start)
 #define _0e ((const)__pcc_in->data.leaf.capt0.range.end)
@@ -1593,7 +1609,7 @@ static void pcc_action_string_0(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__p
     __=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
     __->strval = strdup(_1);
     if(__->strval==NULL) {
-      ERR("Failed to allocate string value (for regex) parser node\n");
+      ERR("Failed to allocate string value (for string) parser node\n");
       abort();
     }
 #undef _1e
@@ -1618,7 +1634,7 @@ static void pcc_action_string_1(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__p
     __=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
     __->strval = strdup(_2);
     if(__->strval==NULL) {
-      ERR("Failed to allocate string value (for regex) parser node\n");
+      ERR("Failed to allocate string value (for string) parser node\n");
       abort();
     }
 #undef _2e
@@ -1631,6 +1647,58 @@ static void pcc_action_string_1(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__p
 #undef auxil
 }
 
+static void pcc_action_numeric_val_0(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pcc_in, pcc_value_t *__pcc_out) {
+#define auxil (__pcc_ctx->auxil)
+#define __ (*__pcc_out)
+#define _0 pcc_get_capture_string(__pcc_ctx, &__pcc_in->data.leaf.capt0)
+#define _0s ((const)__pcc_in->data.leaf.capt0.range.start)
+#define _0e ((const)__pcc_in->data.leaf.capt0.range.end)
+#define _1 pcc_get_capture_string(__pcc_ctx, __pcc_in->data.leaf.capts.buf[0])
+#define _1s __pcc_in->data.leaf.capts.buf[0]->range.start
+#define _1e __pcc_in->data.leaf.capts.buf[0]->range.end
+    // signed integer, stored as string 
+    __=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
+    __->strval = strdup(_1);
+    if(__->strval==NULL) {
+        ERR("Failed to allocate string value (for integral) parser node\n");
+        abort();
+    }
+#undef _1e
+#undef _1s
+#undef _1
+#undef _0e
+#undef _0s
+#undef _0
+#undef __
+#undef auxil
+}
+
+static void pcc_action_numeric_val_1(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pcc_in, pcc_value_t *__pcc_out) {
+#define auxil (__pcc_ctx->auxil)
+#define __ (*__pcc_out)
+#define _0 pcc_get_capture_string(__pcc_ctx, &__pcc_in->data.leaf.capt0)
+#define _0s ((const)__pcc_in->data.leaf.capt0.range.start)
+#define _0e ((const)__pcc_in->data.leaf.capt0.range.end)
+#define _2 pcc_get_capture_string(__pcc_ctx, __pcc_in->data.leaf.capts.buf[1])
+#define _2s __pcc_in->data.leaf.capts.buf[1]->range.start
+#define _2e __pcc_in->data.leaf.capts.buf[1]->range.end
+    // exponential notation
+    __=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
+    __->strval = strdup(_2);
+    if(__->strval==NULL) {
+        ERR("Failed to allocate string value (for exponential numeric) parser node\n");
+        abort();
+    }
+#undef _2e
+#undef _2s
+#undef _2
+#undef _0e
+#undef _0s
+#undef _0
+#undef __
+#undef auxil
+}
+
 static void pcc_action_space_0(mstro_csq_context_t *__pcc_ctx, pcc_thunk_t *__pcc_in, pcc_value_t *__pcc_out) {
 #define auxil (__pcc_ctx->auxil)
 #define __ (*__pcc_out)
@@ -1658,6 +1726,7 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_regex_i(mstro_csq_context_t *ctx);
 static pcc_thunk_chunk_t *pcc_evaluate_rule_identifier(mstro_csq_context_t *ctx);
 static pcc_thunk_chunk_t *pcc_evaluate_rule_simple_regex(mstro_csq_context_t *ctx);
 static pcc_thunk_chunk_t *pcc_evaluate_rule_string(mstro_csq_context_t *ctx);
+static pcc_thunk_chunk_t *pcc_evaluate_rule_numeric_val(mstro_csq_context_t *ctx);
 static pcc_thunk_chunk_t *pcc_evaluate_rule_space(mstro_csq_context_t *ctx);
 static pcc_thunk_chunk_t *pcc_evaluate_rule_EOF(mstro_csq_context_t *ctx);
 
@@ -2488,7 +2557,7 @@ L0000:;
 static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
     pcc_thunk_chunk_t *const chunk = pcc_thunk_chunk__create(ctx->auxil);
     chunk->pos = ctx->pos;
-    pcc_value_table__resize(ctx->auxil, &chunk->values, 3);
+    pcc_value_table__resize(ctx->auxil, &chunk->values, 4);
     pcc_capture_table__resize(ctx->auxil, &chunk->capts, 0);
     {
         const int p = ctx->pos;
@@ -2509,7 +2578,7 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
         L0004:;
         }
         {
-            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_0, 3, 0);
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_0, 4, 0);
             thunk->data.leaf.values.buf[0] = &(chunk->values.buf[0]);
             thunk->data.leaf.capt0.range.start = chunk->pos;
             thunk->data.leaf.capt0.range.end = ctx->pos;
@@ -2535,7 +2604,7 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
         L0007:;
         }
         {
-            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_1, 3, 0);
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_1, 4, 0);
             thunk->data.leaf.values.buf[1] = &(chunk->values.buf[1]);
             thunk->data.leaf.capt0.range.start = chunk->pos;
             thunk->data.leaf.capt0.range.end = ctx->pos;
@@ -2552,7 +2621,7 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
             }
         L0009:;
         }
-        if (!pcc_apply_rule(ctx, pcc_evaluate_rule_identifier, &chunk->thunks, &(chunk->values.buf[2]))) goto L0008;
+        if (!pcc_apply_rule(ctx, pcc_evaluate_rule_numeric_val, &chunk->thunks, &(chunk->values.buf[2]))) goto L0008;
         {
             int i;
             for (i = 0;; i++) {
@@ -2561,7 +2630,7 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
         L0010:;
         }
         {
-            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_2, 3, 0);
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_2, 4, 0);
             thunk->data.leaf.values.buf[2] = &(chunk->values.buf[2]);
             thunk->data.leaf.capt0.range.start = chunk->pos;
             thunk->data.leaf.capt0.range.end = ctx->pos;
@@ -2569,6 +2638,32 @@ static pcc_thunk_chunk_t *pcc_evaluate_rule_value(mstro_csq_context_t *ctx) {
         }
         goto L0001;
     L0008:;
+        ctx->pos = p;
+        pcc_thunk_array__revert(ctx->auxil, &chunk->thunks, n);
+        {
+            int i;
+            for (i = 0;; i++) {
+                if (!pcc_apply_rule(ctx, pcc_evaluate_rule_space, &chunk->thunks, NULL)) goto L0012;
+            }
+        L0012:;
+        }
+        if (!pcc_apply_rule(ctx, pcc_evaluate_rule_identifier, &chunk->thunks, &(chunk->values.buf[3]))) goto L0011;
+        {
+            int i;
+            for (i = 0;; i++) {
+                if (!pcc_apply_rule(ctx, pcc_evaluate_rule_space, &chunk->thunks, NULL)) goto L0013;
+            }
+        L0013:;
+        }
+        {
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_value_3, 4, 0);
+            thunk->data.leaf.values.buf[3] = &(chunk->values.buf[3]);
+            thunk->data.leaf.capt0.range.start = chunk->pos;
+            thunk->data.leaf.capt0.range.end = ctx->pos;
+            pcc_thunk_array__add(ctx->auxil, &chunk->thunks, thunk);
+        }
+        goto L0001;
+    L0011:;
         ctx->pos = p;
         pcc_thunk_array__revert(ctx->auxil, &chunk->thunks, n);
         goto L0000;
@@ -2919,6 +3014,149 @@ L0000:;
     return NULL;
 }
 
+static pcc_thunk_chunk_t *pcc_evaluate_rule_numeric_val(mstro_csq_context_t *ctx) {
+    pcc_thunk_chunk_t *const chunk = pcc_thunk_chunk__create(ctx->auxil);
+    chunk->pos = ctx->pos;
+    pcc_value_table__resize(ctx->auxil, &chunk->values, 0);
+    pcc_capture_table__resize(ctx->auxil, &chunk->capts, 2);
+    {
+        const int p = ctx->pos;
+        const int n = chunk->thunks.len;
+        {
+            const int p = ctx->pos;
+            int q;
+            {
+                char c;
+                if (pcc_refill_buffer(ctx, 1) < 1) goto L0003;
+                c = ctx->buffer.buf[ctx->pos];
+                if (!(
+                    c == '+' ||
+                    c == '-'
+                )) goto L0003;
+                ctx->pos++;
+            }
+        L0003:;
+            {
+                const int p = ctx->pos;
+                int i;
+                for (i = 0;; i++) {
+                    char c;
+                    if (pcc_refill_buffer(ctx, 1) < 1) goto L0004;
+                    c = ctx->buffer.buf[ctx->pos];
+                    if (!(c >= '0' && c <= '9')) goto L0004;
+                    ctx->pos++;
+                }
+            L0004:;
+                if (i < 1) {
+                    ctx->pos = p;
+                    goto L0002;
+                }
+            }
+            q = ctx->pos;
+            chunk->capts.buf[0].range.start = p;
+            chunk->capts.buf[0].range.end = q;
+        }
+        {
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_numeric_val_0, 0, 2);
+            thunk->data.leaf.capts.buf[0] = &(chunk->capts.buf[0]);
+            thunk->data.leaf.capt0.range.start = chunk->pos;
+            thunk->data.leaf.capt0.range.end = ctx->pos;
+            pcc_thunk_array__add(ctx->auxil, &chunk->thunks, thunk);
+        }
+        goto L0001;
+    L0002:;
+        ctx->pos = p;
+        pcc_thunk_array__revert(ctx->auxil, &chunk->thunks, n);
+        {
+            const int p = ctx->pos;
+            int q;
+            {
+                char c;
+                if (pcc_refill_buffer(ctx, 1) < 1) goto L0005;
+                c = ctx->buffer.buf[ctx->pos];
+                if (!(
+                    c == '+' ||
+                    c == '-'
+                )) goto L0005;
+                ctx->pos++;
+            }
+            {
+                int i;
+                for (i = 0;; i++) {
+                    char c;
+                    if (pcc_refill_buffer(ctx, 1) < 1) goto L0006;
+                    c = ctx->buffer.buf[ctx->pos];
+                    if (!(c >= '0' && c <= '9')) goto L0006;
+                    ctx->pos++;
+                }
+            L0006:;
+            }
+            if (
+                pcc_refill_buffer(ctx, 1) < 1 ||
+                ctx->buffer.buf[ctx->pos] != '.'
+            ) goto L0005;
+            ctx->pos++;
+            {
+                char c;
+                if (pcc_refill_buffer(ctx, 1) < 1) goto L0005;
+                c = ctx->buffer.buf[ctx->pos];
+                if (!(
+                    c == 'E' ||
+                    c == 'e'
+                )) goto L0005;
+                ctx->pos++;
+            }
+            {
+                char c;
+                if (pcc_refill_buffer(ctx, 1) < 1) goto L0007;
+                c = ctx->buffer.buf[ctx->pos];
+                if (!(
+                    c == '+' ||
+                    c == '-'
+                )) goto L0007;
+                ctx->pos++;
+            }
+        L0007:;
+            {
+                const int p = ctx->pos;
+                int i;
+                for (i = 0;; i++) {
+                    char c;
+                    if (pcc_refill_buffer(ctx, 1) < 1) goto L0008;
+                    c = ctx->buffer.buf[ctx->pos];
+                    if (!(c >= '0' && c <= '9')) goto L0008;
+                    ctx->pos++;
+                }
+            L0008:;
+                if (i < 1) {
+                    ctx->pos = p;
+                    goto L0005;
+                }
+            }
+            q = ctx->pos;
+            chunk->capts.buf[1].range.start = p;
+            chunk->capts.buf[1].range.end = q;
+        }
+        {
+            pcc_thunk_t *const thunk = pcc_thunk__create_leaf(ctx->auxil, pcc_action_numeric_val_1, 0, 2);
+            thunk->data.leaf.capts.buf[1] = &(chunk->capts.buf[1]);
+            thunk->data.leaf.capt0.range.start = chunk->pos;
+            thunk->data.leaf.capt0.range.end = ctx->pos;
+            pcc_thunk_array__add(ctx->auxil, &chunk->thunks, thunk);
+        }
+        goto L0001;
+    L0005:;
+        ctx->pos = p;
+        pcc_thunk_array__revert(ctx->auxil, &chunk->thunks, n);
+        goto L0000;
+    L0001:;
+    }
+    return chunk;
+L0000:;
+    pcc_thunk_chunk__destroy(ctx->auxil, chunk);
+    return NULL;
+}
+
 static pcc_thunk_chunk_t *pcc_evaluate_rule_space(mstro_csq_context_t *ctx) {
     pcc_thunk_chunk_t *const chunk = pcc_thunk_chunk__create(ctx->auxil);
     chunk->pos = ctx->pos;
@@ -2994,7 +3232,7 @@ void mstro_csq_destroy(mstro_csq_context_t *ctx) {
 
 #define MIN(x,y) ((x)<(y) ? (x) : (y))
 
-#line 358 "cdo_sel_parse.peg"
+#line 370 "cdo_sel_parse.peg"
 void
 mstro_csq_val__describe(const struct mstro_csq_val *v, size_t indent)
 {
diff --git a/maestro/cdo_sel_parse.peg b/maestro/cdo_sel_parse.peg
index 8918fb0486af94ea7957b1f2027ab0e35d421146..8360d84e731726e0f8172c28d1cacf0d1b4d08b1 100644
--- a/maestro/cdo_sel_parse.peg
+++ b/maestro/cdo_sel_parse.peg
@@ -294,9 +294,10 @@ attribute_key <- ide:identifier { $$=ide; DEBUG("ide\n"); }
 
 
 # values will be stored as strings and parsed with type info later
-value <- space* r:regex      space*        { $$=r; DEBUG("regex value\n");}
-       / space* s:string     space*        { $$=s; DEBUG("string value\n");}
-       / space* i:identifier space*        { $$=i; DEBUG("ide value\n"); }
+value <- space* r:regex       space*        { $$=r; DEBUG("regex value\n");}
+       / space* s:string      space*        { $$=s; DEBUG("string value\n");}
+       / space* n:numeric_val space*        { $$=n; DEBUG("numeric value\n");}
+       / space* i:identifier  space*        { $$=i; DEBUG("ide value\n"); }
 
 regex <- r:regex_i            { $$ = r; DEBUG("CI regex\n"); }
        / r:simple_regex       { $$ = r; DEBUG("simple regex\n"); }
@@ -337,7 +338,7 @@ string <- ["] < [^"]* > ["] {
              $$=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
 	     $$->strval = strdup($1);
 	     if($$->strval==NULL) {
-	       ERR("Failed to allocate string value (for regex) parser node\n");
+	       ERR("Failed to allocate string value (for string) parser node\n");
 	       abort();
 	     }
            }
@@ -345,12 +346,29 @@ string <- ["] < [^"]* > ["] {
              $$=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
 	     $$->strval = strdup($2);
 	     if($$->strval==NULL) {
-	       ERR("Failed to allocate string value (for regex) parser node\n");
+	       ERR("Failed to allocate string value (for string) parser node\n");
 	       abort();
 	     }
         } 		       
 		     
-
+numeric_val <- < [+-]?[0-9]+ > { 
+	         // signed integer, stored as string 
+	         $$=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
+	    	 $$->strval = strdup($1);
+	     	 if($$->strval==NULL) {
+	  	     ERR("Failed to allocate string value (for integral) parser node\n");
+	             abort();
+	     	 }
+	       }
+             / < [+-][0-9]*[.][Ee][+-]?[0-9]+ > {
+	         // exponential notation
+	         $$=mstro_csq_val__alloc(MSTRO_CSQ_STRING);
+	    	 $$->strval = strdup($2);
+	     	 if($$->strval==NULL) {
+	  	     ERR("Failed to allocate string value (for exponential numeric) parser node\n");
+	             abort();
+	     	 }
+	     }	    
 
 ## isspace()
 space <- [ \t\n\v\f\r] { ; }
@@ -366,7 +384,7 @@ EOF  <- !.
 
 #define MIN(x,y) ((x)<(y) ? (x) : (y))
 
-#line 358 "cdo_sel_parse.peg"
+#line 370 "cdo_sel_parse.peg"
 void
 mstro_csq_val__describe(const struct mstro_csq_val *v, size_t indent)
 {
diff --git a/maestro/core.c b/maestro/core.c
index 19247f2f232df77b115c4025da65e06b378cb433..8837e2aee3f52432201207879553e54466a62fae 100644
--- a/maestro/core.c
+++ b/maestro/core.c
@@ -53,6 +53,10 @@
 #include "maestro/i_state.h"
 #include "transport/transport.h"
 
+#ifdef HAVE_NUMA_H
+#include <numa.h>
+#endif
+
 
 /* simplify logging */
 #define DEBUG(...) LOG_DEBUG(MSTRO_LOG_MODULE_CORE,__VA_ARGS__)
@@ -80,6 +84,26 @@ mstro_i_destroy_initdata(void* initdata)
   return;
 }
 
+static inline
+mstro_status
+mstro_core__numa_init(void)
+{
+#ifdef HAVE_NUMA
+  g_have_numa = (numa_available()==-1 ? false : true);
+  if(g_have_numa) {
+    DEBUG("NUMA: available with %d/%d nodes, %d/%d cpus\n",
+          numa_num_task_nodes(), numa_num_configured_nodes(),
+          numa_num_task_cpus(), numa_num_configured_cpus());
+    /* FIXME: consider initializing some global data structures with this info */
+  } else {
+    DEBUG("NUMA: not available on system\n");
+  }
+#else
+  g_have_numa = false;
+  DEBUG("NUMA: not supported\n");
+#endif
+  return MSTRO_OK;
+}
 
 static inline
 mstro_status
@@ -145,6 +169,132 @@ BAILOUT:
 /** minimum mlock() limit */
 #define MSTRO_MIN_MEMLOCK (4*sizeof(g_component_descriptor))
 
+
+mstro_status mstro_core_init__setup_schemata(void)
+{
+
+  mstro_status status = MSTRO_OK;
+
+  char * env_schema_list = getenv(MSTRO_ENV_SCHEMA_LIST);
+  char * env_schema_path = getenv(MSTRO_ENV_SCHEMA_PATH);
+  //check that neither are null ...if null make them empty strings
+  if(env_schema_list == NULL)
+  {
+    env_schema_list = "";
+  }
+  else
+  {
+    INFO("List of user attributes schemata %s \n", env_schema_list);
+    strncpy(g_component_descriptor.schema_list, env_schema_list, MSTRO_WORKFLOW_NAME_MAX-1);
+    g_component_descriptor.schema_list[MSTRO_WORKFLOW_NAME_MAX-1] = '\0';
+  }
+
+  if(env_schema_path == NULL)
+  {
+    env_schema_path = "";
+  }
+
+  char *schema_list_token;
+  char *schema_path_token;
+
+
+  // parse and merge the builtin schemas first
+  DEBUG("Parsing Maestro core schema\n");
+  status=mstro_schema_parse(MSTRO_SCHEMA_BUILTIN_YAML_CORE,
+                            MSTRO_SCHEMA_BUILTIN_YAML_CORE_LEN,
+                            &g_mstro_core_schema_instance);
+  if(status!=MSTRO_OK) {
+    ERR("Failed to parse built-in core schema\n");
+    return  status;
+  }
+
+  DEBUG("Parsing ECMWF schema\nFIXME for 0.3.0 ... ECMWF schema shoud not be loaded by default\n");
+  mstro_schema ecmwf;
+  status=mstro_schema_parse(MSTRO_SCHEMA_BUILTIN_YAML_ECMWF,
+                            MSTRO_SCHEMA_BUILTIN_YAML_ECMWF_LEN,
+                            &ecmwf);
+  if(status!=MSTRO_OK) {
+    ERR("Failed to parse built-in ecmwf schema\n");
+    return  status;
+  }
+  status=mstro_schema_merge(g_mstro_core_schema_instance, ecmwf);
+  if(status!=MSTRO_OK) {
+    ERR("Failed to merge core and ECMWF schema\n");
+    return  status;
+  }
+
+   // start reading user-defined schemas and merge them.
+   char *end_list_token;
+   char *end_path_token;
+   char *schema_full_path;
+   char *env_schema_path_plus_default;
+
+   // the lenght of all paths =  length of paths exported by user + separator + default path "."
+   int path_len = strlen(env_schema_path) + 2 + 2;
+   env_schema_path_plus_default = (char *) malloc(path_len*sizeof(char));
+
+   /* get the first schema */
+   schema_list_token = strtok_r(env_schema_list, SCHEMA_LIST_SEP, &end_list_token);
+   DEBUG("first schema_list_token: %s \n", schema_list_token);
+
+   /* walk through other tokens */
+   while( schema_list_token != NULL )
+   {
+      // parse a schema from the user
+      mstro_schema user_schema;
+      DEBUG("looking for schema_list_token: %s \n", schema_list_token);
+
+      // creating list of paths to visit. i.e. current directory "." + user defined paths
+      snprintf(env_schema_path_plus_default, path_len, "%s%s%s", ".", SCHEMA_PATH_SEP, env_schema_path);
+      DEBUG("list of paths for schemas: %s\n", env_schema_path_plus_default);
+      /* get the first path */
+      schema_path_token = strtok_r(env_schema_path_plus_default, SCHEMA_PATH_SEP, &end_path_token);
+      DEBUG("first schema_path_token: %s\n", schema_path_token);
+
+      /* walk through other paths */
+      while( schema_path_token != NULL )
+      {
+        // forming the full path:          path token + / + list name + '\0'
+        int schema_full_path_len = strlen(schema_path_token) + 2 + strlen(schema_list_token) + 2;
+        schema_full_path = (char *) malloc(sizeof(char)*schema_full_path_len);
+        snprintf(schema_full_path, schema_full_path_len, "%s%s%s", schema_path_token, "/", schema_list_token);
+        DEBUG("Parsing user-defined schema from %s\n", schema_full_path);
+        status=mstro_schema_parse_from_file(schema_full_path, &user_schema);
+
+        if(status==MSTRO_OK) {
+          DEBUG("user-defined schema is read from %s\n", schema_full_path);
+          free(schema_full_path);
+          schema_full_path = NULL;
+          break; // no need to try other paths
+        }
+
+        free(schema_full_path);
+        schema_full_path = NULL;
+        // read the next path
+          schema_path_token = strtok_r(NULL, SCHEMA_PATH_SEP, &end_path_token);
+
+      }
+
+      if(status!=MSTRO_OK) {
+        ERR("Failed to parse user_schema from file: %s \n", schema_list_token);
+        return  status;
+      }
+      // merge the schema
+      DEBUG("Merging user schema: %s\n", schema_list_token);
+      status=mstro_schema_merge(g_mstro_core_schema_instance, user_schema);
+
+
+      if(status!=MSTRO_OK) {
+        ERR("Failed to merge core and user schema from file %s\n", schema_list_token);
+        return  status;
+      }
+      // read the next schema name
+      schema_list_token = strtok_r(NULL, SCHEMA_LIST_SEP, &end_list_token);
+   }
+
+   return  status;
+}
+
 mstro_status
 mstro_core_init(const char *workflow_name,
                 const char *component_name,
@@ -189,26 +339,55 @@ mstro_core_init(const char *workflow_name,
   g_component_descriptor.component_name[MSTRO_WORKFLOW_NAME_MAX-1] = '\0';
   strcpy(g_component_descriptor.version, mstro_version());
 
-  status=mstro_schema_parse(MSTRO_SCHEMA_BUILTIN_YAML_CORE,
-                            MSTRO_SCHEMA_BUILTIN_YAML_CORE_LEN,
-                            &g_mstro_core_schema_instance);
-  if(status!=MSTRO_OK) {
-    ERR("Failed to parse built-in core schema\n");
-    goto BAILOUT;
-  }
-  /* FIXME: this should not be here */
-  DEBUG("Including ECMWF schema\n");
-  mstro_schema ecmwf;
-  status=mstro_schema_parse(MSTRO_SCHEMA_BUILTIN_YAML_ECMWF,
-                            MSTRO_SCHEMA_BUILTIN_YAML_ECMWF_LEN,
-                            &ecmwf);
-  if(status!=MSTRO_OK) {
-    ERR("Failed to parse built-in ecmwf schema\n");
-    goto BAILOUT;
+  /* check GFS path setting */
+  {
+    char *gfs_dir = getenv(MSTRO_ENV_TRANSPORT_GFS_DIR);
+    if(gfs_dir!=NULL) {
+      bool need_sep = false;
+      size_t l1 = strlen(gfs_dir);
+      assert(l1>0);
+      if(gfs_dir[l1-1]!='/') {
+        need_sep=true;
+      }
+      size_t l2 = strlen(data->workflow_name);
+      size_t l3 = strlen(data->component_name);
+
+      g_mstro_transport_gfs_dir = malloc(l1 + (need_sep? 1 : 0)
+                                         + l2 + 1
+                                         + l3 + 1 +1);
+      if(g_mstro_transport_gfs_dir==NULL) {
+        ERR("Failed to allocate GFS transport dir pathname\n");
+        return MSTRO_NOMEM;
+      }
+      size_t pos = 0;
+      strcpy(g_mstro_transport_gfs_dir, gfs_dir);
+      pos += l1;
+      if(need_sep) {
+        g_mstro_transport_gfs_dir[pos] = '/';
+        pos++;
+      }
+      strcpy(g_mstro_transport_gfs_dir+pos, data->workflow_name);
+      pos += l2;
+
+      g_mstro_transport_gfs_dir[pos] = '/';
+      pos++;
+
+      strcpy(g_mstro_transport_gfs_dir+pos, data->component_name);
+      pos += l3;
+      g_mstro_transport_gfs_dir[pos] = '/';
+      pos++;
+      g_mstro_transport_gfs_dir[pos] = '\0';
+      g_mstro_transport_gfs_dir_len = pos;
+      assert(pos==strlen(g_mstro_transport_gfs_dir));
+
+      DEBUG("Set GFS transport directory to %s\n",
+            g_mstro_transport_gfs_dir);
+    }
   }
-  status=mstro_schema_merge(g_mstro_core_schema_instance, ecmwf);
+
+  /*Reading and integrating attribute schemas*/
+  status = mstro_core_init__setup_schemata();
   if(status!=MSTRO_OK) {
-    ERR("Failed to merge core and ECMWF schema\n");
     goto BAILOUT;
   }
 
@@ -230,6 +409,12 @@ mstro_core_init(const char *workflow_name,
   }
   pthread_mutex_unlock(&g_initdata_mtx);
 
+  status = mstro_core__numa_init();
+  if(status!=MSTRO_OK) {
+    ERR("Failed to initialize NUMA support infrastructure\n");
+    goto BAILOUT;
+  }
+
   status = mstro_core__mamba_init();
   if(status!=MSTRO_OK) {
     goto BAILOUT;
diff --git a/maestro/globals.c b/maestro/globals.c
index 6bb1045630c41ba5f26cc102a81dcc186e80fa33..780928d3abbed2864bb55d7e7f01aa507d1fe4bd 100644
--- a/maestro/globals.c
+++ b/maestro/globals.c
@@ -85,3 +85,15 @@ mstro_schema g_mstro_core_schema_instance = NULL;
 bool g_have_pool_manager=false;
 
 union mstro_component_descriptor g_component_descriptor;
+
+
+/** the precomputed default path for GFS transport (incl. trailing '/') */
+char *g_mstro_transport_gfs_dir = "./"; /* possibly overwritten at init
+                                         * time from env*/
+
+/** string length of @ref g_mstro_transport_gfs_dir */
+size_t g_mstro_transport_gfs_dir_len = 2; /* possibly overwritten at
+                                           * init time */
+
+/** NUMA supported and working? */
+bool g_have_numa = false;
diff --git a/maestro/groups.c b/maestro/groups.c
index 16d4862aa5903f7652d2d8d555690fa7142267fa..6a7f7cdd65437d85217eec66d871351138520aa1 100644
--- a/maestro/groups.c
+++ b/maestro/groups.c
@@ -204,7 +204,7 @@ mstro_group_declare(const char *name, mstro_group* group)
     if(s==MSTRO_OK) {
       bool trueval = true;
       s = mstro_cdo_attribute_set((*group)->group_cdo,
-                                  MSTRO_ATTR_CORE_CDO_ISGROUP, &trueval);
+                                  MSTRO_ATTR_CORE_CDO_ISGROUP, &trueval, true);
       if(s==MSTRO_OK) {
         DEBUG("Declared CDO group |%s|\n", name);
         (*group)->state = MSTRO_GROUP_STATE_DECLARED;
@@ -312,20 +312,21 @@ mstro_group_seal(mstro_group g)
     HASH_ITER(hh, g->members, el, tmp) {
       m->declared_members[i] = malloc(sizeof(Mstro__Pool__UUID));
       /* need to ensure the GID is ready */
-      mstro_status s = mstro_cdo_seal(el->entry);
-      if(s!=MSTRO_OK) {
-        ERR("Failed to seal declared group member: %d\n", s);
-      }
-      if(s!=MSTRO_OK || m->declared_members[i]==NULL) {
-        for(size_t j=i; j>0; j--) {
-          free(m->declared_members[j-1]);
-        };
-        free(m->declared_members);
-        m->declared_members=NULL;
-        goto BAILOUT_FREE;
-        
+      if(!mstro_cdo_state_check(el->entry, MSTRO_CDO_STATE_SEALED)) {
+        mstro_status s = mstro_cdo_seal(el->entry);
+        if(s!=MSTRO_OK) {
+          ERR("Failed to seal declared group member: %d\n", s);
+        }
+        if(s!=MSTRO_OK || m->declared_members[i]==NULL) {
+          for(size_t j=i; j>0; j--) {
+            free(m->declared_members[j-1]);
+          };
+          free(m->declared_members);
+          m->declared_members=NULL;
+          goto BAILOUT_FREE;
+          
+        }
       }
-
       mstro__pool__uuid__init(m->declared_members[i]);
       m->declared_members[i]->qw0 = el->entry->gid.qw[0];
       m->declared_members[i]->qw1 = el->entry->gid.qw[1];
@@ -374,7 +375,7 @@ mstro_group_seal(mstro_group g)
   
   s = mstro_cdo_attribute_set(g->group_cdo,
                               MSTRO_ATTR_CORE_CDO_GROUP_MEMBERS,
-                              blob);
+                              blob, false);
   if(s!=MSTRO_OK) 
     goto BAILOUT_FREE;
 
diff --git a/maestro/i_maestro_numa.h b/maestro/i_maestro_numa.h
new file mode 100644
index 0000000000000000000000000000000000000000..001029f09d8b6c78e09cb0053be64f84ee9ca219
--- /dev/null
+++ b/maestro/i_maestro_numa.h
@@ -0,0 +1,88 @@
+/* -*- mode:c -*- */
+/** @file
+ ** @brief Maestro NUMA abstraction
+ **/
+/*
+ * Copyright (C) 2021 HPE Switzerland GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAESTRO_I_NUMA_H_
+#define MAESTRO_I_NUMA_H_ 1
+
+/**@addtogroup MSTRO_Internal
+ **@{
+ **/
+
+/**@defgroup MSTRO_I_NUMA Maestro NUMA abstraction
+ **@{
+ **
+ **/
+
+#ifdef HAVE_NUMAIF_H
+#include <numaif.h>
+#endif
+
+#include "maestro/logging.h"
+
+
+/** obtain node ID for a given address.
+ *
+ * Will always succeed, yielding '0' if NUMA is not supported or ID cannot be obtained */
+static inline
+int
+mstro_numa__node_for_addr(void * addr)
+{
+#ifdef HAVE_NUMA
+  if(g_have_numa) {
+    int node = -1;
+    long s = get_mempolicy(&node,
+		    NULL /* node mask */,
+		    0 /* max nodes in mask */,
+		    addr, MPOL_F_NODE | MPOL_F_ADDR);
+    if(s<0) {
+      LOG_WARN(MSTRO_LOG_MODULE_CORE,
+	       "Failed to obtain NUMA info for addr %p: %ld (%s)\n",
+	       addr, s, strerror(-s));
+      return 0;
+    } else {
+      return node;
+    }
+  } else {
+    return 0;
+  }
+#else
+  return 0;
+#endif
+}
+/**@} (end of group MSTRO_I_NUMA) */
+/**@} (end of group MSTRO_Internal) */
+
+#endif /* MAESTRO_I_NUMA_H_ */
+
diff --git a/maestro/ofi.c b/maestro/ofi.c
index 72a1572df8ded128f7f66375d169c9eb9b56f951..06f942cdfd4b5021ee4b5e27611272ecb0bf5992 100644
--- a/maestro/ofi.c
+++ b/maestro/ofi.c
@@ -5,21 +5,21 @@
 
 /*
  * Copyright (C) 2019 Cray Computer GmbH
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  * list of conditions and the following disclaimer.
- * 
+ *
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  * this list of conditions and the following disclaimer in the documentation
  * and/or other materials provided with the distribution.
- * 
+ *
  * 3. Neither the name of the copyright holder nor the names of its contributors
  * may be used to endorse or promote products derived from this software without
  * specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -147,9 +147,9 @@ mstro_ep_desc_create_ofi(mstro_endpoint_descriptor *result_p,
     return MSTRO_INVARG;
 
   enum mstro_endpoint_type ept = MSTRO_EP_INVALID;
-  
+
   switch(fi->addr_format) {
-    /** This is the list documented for libfabric 1.5 */ 
+    /** This is the list documented for libfabric 1.5 */
     case FI_SOCKADDR_IN:
       ept = MSTRO_EP_OFI_IN4;        break;
     case FI_SOCKADDR_IN6:
@@ -268,13 +268,13 @@ mstro_ep_desc_serialize__in4(char *dst,
                              const struct sockaddr_in *sin)
 {
   mstro_status stat = MSTRO_UNIMPL;
-  
+
   /* struct sockaddr_in is defined platform-independent and
    * already using network byte order as needed, so we can handle
    * it as a struct */
   tpl_node *tns;
   tpl_bin tb;
-  
+
   tb.sz = sizeof(struct sockaddr_in);
   tb.addr = sin;
   tns = tpl_map("B", &tb);
@@ -318,7 +318,7 @@ mstro_ep_desc_deserialize__in4(struct sockaddr_in *sin,
                                char *b64_strval)
 {
   mstro_status stat=MSTRO_UNIMPL;
-  
+
   tpl_node *tn;
   tpl_bin tb;
   tn = tpl_map( "B", &tb );
@@ -333,7 +333,7 @@ mstro_ep_desc_deserialize__in4(struct sockaddr_in *sin,
     goto BAILOUT;
   }
   /* DEBUG("b64decode: buf of length %zu\n", buflen); */
-  
+
   tpl_load( tn, TPL_MEM, buf, buflen);
   tpl_unpack( tn, 0 );
   tpl_free(tn);
@@ -361,7 +361,7 @@ mstro_ep_desc_serialize__in6(char *dst,
   tpl_bin tb;
 
   INFO("sockaddr_in6 with port %d\n", ntohs(sin->sin6_port));
-  
+
   tb.sz = sizeof(struct sockaddr_in6);
   tb.addr = sin;
   tns = tpl_map("B", &tb);
@@ -388,7 +388,7 @@ mstro_ep_desc_serialize__in6(char *dst,
     stat=MSTRO_FAIL;
     goto BAILOUT;
   }
-    
+
   stat=MSTRO_OK;
 BAILOUT:
   /* clean up */
@@ -422,7 +422,7 @@ mstro_ep_desc_deserialize__in6(struct sockaddr_in6 *sin,
   tpl_load( tn, TPL_MEM, buf, buflen);
   tpl_unpack( tn, 0 );
   tpl_free(tn);
-  
+
   assert(tb.sz==sizeof(struct sockaddr_in6));
 
   memcpy(sin, tb.addr, tb.sz);
@@ -455,13 +455,13 @@ mstro_ep_desc_serialize(char **result_p,
   struct serialized_endpoint_element serialized_element;
 
   assert(MSTRO_EP__MAX<=INT_MAX);
-  
+
   tn = tpl_map("A(S(IssUUc#))", &serialized_element, MSTRO_OFI_KEY_LEN_MAX);
   char *strval = alloca(MSTRO_EP_STRING_MAX*sizeof(char));
   if(strval==NULL)
     return MSTRO_NOMEM;
 
-  
+
   LL_FOREACH(epd,elt) {
     /* each one should serialize into STRVAL buffer, base64-encoded,
      * NULL-terminated */
@@ -495,7 +495,7 @@ mstro_ep_desc_serialize(char **result_p,
         return MSTRO_FAIL;
       }
       /* b64 encode */
-      
+
       size_t needed;
       encoded = base64_encode(buf, buflen, &needed);
       if(needed>MSTRO_EP_STRING_MAX) {
@@ -513,7 +513,7 @@ mstro_ep_desc_serialize(char **result_p,
       stat=MSTRO_OK;
    BAILOUT_1:
       /* clean up */
-      NFREE(encoded);      
+      NFREE(encoded);
       tpl_free(tns);
       free(buf);
     } else if(elt->type == MSTRO_EP_OFI_STR) {
@@ -549,7 +549,7 @@ mstro_ep_desc_serialize(char **result_p,
         stat=MSTRO_NOMEM;
         goto BAILOUT_2;
       }
-      
+
       stat=MSTRO_OK;
    BAILOUT_2:
       NFREE(encoded);
@@ -558,7 +558,7 @@ mstro_ep_desc_serialize(char **result_p,
           elt->type, mstro_ep_descriptor_names[elt->type]);
       return MSTRO_FAIL;
     }
-    
+
     serialized_element.type = elt->type;
     serialized_element.strval=strdup(strval); /* shrink wrap size */
     if(serialized_element.strval==NULL)
@@ -566,7 +566,7 @@ mstro_ep_desc_serialize(char **result_p,
 
     mstro_drc_get_oob_string(&serialized_element.oob_cookie, g_drc_info);
     DEBUG("Added OOB info %s\n", serialized_element.oob_cookie);
-    
+
     /* keys */
     serialized_element.info_addr = ep->component_info_addr;
     serialized_element.info_keysize = ep->component_info_keysize;
@@ -582,7 +582,7 @@ mstro_ep_desc_serialize(char **result_p,
     free(serialized_element.strval);
     free(serialized_element.oob_cookie);
   }
-  
+
   /* now write all to a string */
   size_t len;
   void *buf=NULL;
@@ -619,7 +619,7 @@ mstro_ep_desc_serialize(char **result_p,
 BAILOUT:
   if(buf)
     free(buf);
-        
+
   return stat;
 }
 
@@ -629,7 +629,7 @@ mstro_ofi_pm_info(char **result_p)
   if(result_p==NULL)
     return MSTRO_INVOUT;
   mstro_status status = MSTRO_OK;
-  
+
   unsigned char *encoded=NULL;
   if(g_endpoints==NULL) {
     ERR("No endpoints configured -- did you call mstro_pm_start()?\n");
@@ -645,14 +645,14 @@ mstro_ofi_pm_info(char **result_p)
   struct serialized_endpoint_element serialized_element;
 
   assert(MSTRO_EP__MAX<=INT_MAX);
-  
+
   tn = tpl_map("A(S(IssUUc#))", &serialized_element, MSTRO_OFI_KEY_LEN_MAX);
   char *strval = alloca(MSTRO_EP_STRING_MAX*sizeof(char));
   if(strval==NULL)
     return MSTRO_NOMEM;
 
   mstro_status stat;
-  
+
   LL_FOREACH(&(g_endpoints->eps[0]),elt) {
     mstro_endpoint_descriptor d = elt->descr;
     /* each one should serialize into STRVAL buffer, base64-encoded,
@@ -686,7 +686,7 @@ mstro_ofi_pm_info(char **result_p)
         return MSTRO_FAIL;
       }
       /* b64 encode */
-      
+
       size_t needed;
       encoded = base64_encode(buf, buflen, &needed);
       if(needed>MSTRO_EP_STRING_MAX) {
@@ -704,7 +704,7 @@ mstro_ofi_pm_info(char **result_p)
       stat=MSTRO_OK;
    BAILOUT_1:
       /* clean up */
-      NFREE(encoded);      
+      NFREE(encoded);
       tpl_free(tns);
       free(buf);
     } else if(d->type == MSTRO_EP_OFI_STR) {
@@ -740,7 +740,7 @@ mstro_ofi_pm_info(char **result_p)
         stat=MSTRO_NOMEM;
         goto BAILOUT_2;
       }
-      
+
       stat=MSTRO_OK;
    BAILOUT_2:
       NFREE(encoded);
@@ -749,7 +749,7 @@ mstro_ofi_pm_info(char **result_p)
           d->type, mstro_ep_descriptor_names[d->type]);
       return MSTRO_FAIL;
     }
-    
+
     serialized_element.type = d->type;
     serialized_element.strval=strdup(strval); /* shrink wrap size */
     if(serialized_element.strval==NULL)
@@ -804,7 +804,7 @@ mstro_ofi_pm_info(char **result_p)
 BAILOUT:
   free(buf);
   return status;
-        
+
 }
 
 
@@ -821,7 +821,7 @@ mstro_ep_desc_deserialize__str(char **dst,
 
   mstro_status stat=MSTRO_UNIMPL;
 
-  
+
   size_t buflen;
   unsigned char *buf = base64_decode((unsigned char*)b64_strval,
                                      strlen(b64_strval),
@@ -858,7 +858,7 @@ mstro_ep_desc_deserialize__uint64(uint64_t (*dst)[6],
   }
   mstro_status stat=MSTRO_UNIMPL;
 
-  
+
   size_t buflen;
   unsigned char *buf = base64_decode((unsigned char*)b64_strval,
                                      strlen(b64_strval),
@@ -880,7 +880,7 @@ mstro_ep_desc_deserialize__uint64(uint64_t (*dst)[6],
 BAILOUT:
   NFREE(buf);
 
-  return stat;        
+  return stat;
 }
 
 
@@ -893,7 +893,7 @@ mstro_ep_desc_deserialize(mstro_endpoint_descriptor *result_p,
   tpl_node *tn=NULL;
   mstro_endpoint_descriptor *next = NULL;
 
-  if(result_p==NULL) 
+  if(result_p==NULL)
     return MSTRO_INVOUT;
   if(serialized_eps==NULL)
     return MSTRO_INVARG;
@@ -903,7 +903,7 @@ mstro_ep_desc_deserialize(mstro_endpoint_descriptor *result_p,
   unsigned char *buf = base64_decode((unsigned char*)serialized_eps,
                                      strlen(serialized_eps),
                                      &buflen);
-  
+
   if(buf==NULL) {
     stat=MSTRO_NOMEM;
     goto BAILOUT;
@@ -937,9 +937,9 @@ mstro_ep_desc_deserialize(mstro_endpoint_descriptor *result_p,
       target->oob_cookie=NULL;
       DEBUG("no cookie\n");
     }
-    
+
     target->name=NULL;
-    
+
     if(eptype==MSTRO_EP_OFI_IN4) {
       stat = mstro_ep_desc_deserialize__in4(&(target->in4),
                                             serialized_element.strval);
@@ -956,11 +956,11 @@ mstro_ep_desc_deserialize(mstro_endpoint_descriptor *result_p,
               || eptype==MSTRO_EP_OFI_MLX) {
       /** transparently handles one .. six entries */
       stat = mstro_ep_desc_deserialize__uint64(&target->gni,
-                                               serialized_element.strval);      
+                                               serialized_element.strval);
     } else if(eptype==MSTRO_EP_OFI_STR) {
       target->str=NULL;
       stat = mstro_ep_desc_deserialize__str(&(target->str),
-                                            serialized_element.strval);      
+                                            serialized_element.strval);
     } else {
       ERR("Unsupported EP type: %d\n", eptype);
       stat = MSTRO_UNIMPL;
@@ -994,7 +994,7 @@ mstro_ep_desc_deserialize(mstro_endpoint_descriptor *result_p,
   }
 BAILOUT:
   free(buf);
-  
+
   return stat;
 }
 
@@ -1090,7 +1090,7 @@ mstro_ep_desc_describe(mstro_endpoint_descriptor desc)
 	      s, errno, strerror(errno));
 	      abort();
       }
-      
+
       desc->name = buf;
     }
   }
@@ -1107,7 +1107,7 @@ mstro_ep_desc_to_ofi_addr(uint32_t *addr_format,
     return MSTRO_INVOUT;
   if(epd==NULL)
     return MSTRO_INVARG;
-  
+
   switch(epd->type) {
     case MSTRO_EP_OFI_IN4:
       *addr_format = FI_SOCKADDR_IN;
@@ -1167,7 +1167,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
     return MSTRO_INVOUT;
   if(fi==NULL)
     return MSTRO_INVARG;
-  
+
   struct fid_fabric *fabric = NULL;
   struct fi_eq_attr eq_attr;
   struct fid_eq *eq = NULL;
@@ -1178,7 +1178,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
   struct fi_cq_attr cq_attr;
   struct fid_cq *cq = NULL;
   uint8_t *mr_key = NULL;
-  
+
   /* create fabric object */
   stat = fi_fabric(fi->fabric_attr, &fabric, NULL);
   if(stat!=0) {
@@ -1195,15 +1195,15 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
      * allocated early on in ofi_init (on pool manager) or obtained
      * from PM_INFO (on clients) */
     assert(g_drc_info!=NULL);
-    
+
     stat = mstro_drc_insert_ofi(fi, g_drc_info);
     if(stat!=MSTRO_OK) {
       ERR("Failed to insert DRC credential into fabric info\n");
       retstat=MSTRO_FAIL;
       goto BAILOUT_FAIL;
-    }      
+    }
   }
-  
+
   /* create domain */
   stat = fi_domain(fabric, fi, &domain, NULL);
   if(stat!=0) {
@@ -1232,7 +1232,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
     ERR("fi_cq_open failed: %d (%s)\n", stat, fi_strerror(-stat));
     retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
   }
-  
+
   /* event queue */
   /* create event queue */
   memset(&eq_attr, 0, sizeof(eq_attr));
@@ -1253,7 +1253,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
   }
 
   /* INFO("This is a %s\n",  fi_tostr(fi,FI_TYPE_INFO)); */
-  
+
   /* connectionless endpoint needs address vector */
   memset(&av_attr, 0, sizeof(av_attr));
   av_attr.type = FI_AV_MAP;
@@ -1262,14 +1262,14 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
     ERR("fi_av_open failed: %d (%s)\n", stat, fi_strerror(-stat));
     retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
   }
-  
+
   /* bind address vector to endpoint */
   stat = fi_ep_bind(ep, &av->fid, 0);
   if(stat!=0) {
     ERR("fi_ep_bind for av failed: %d (%s)\n", stat, fi_strerror(-stat));
     retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
   }
-  
+
   /* bind event queue to endpoint */
   stat = fi_ep_bind(ep, &eq->fid, 0);
   if(stat!=0) {
@@ -1280,7 +1280,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
       eq=NULL;
     } else {
       retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
-    }      
+    }
   }
   /* bind cq to endpoint */
   stat = fi_ep_bind(ep, &cq->fid, FI_TRANSMIT|FI_RECV);
@@ -1288,7 +1288,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
     ERR("fi_ep_bind for cq failed: %d (%s)\n", stat, fi_strerror(-stat));
     retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
   }
-  
+
   /* enable! */
   stat = fi_enable(ep);
   if(stat!=0) {
@@ -1380,7 +1380,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
       ERR("Failed to retrieve key for component info memory registration: %d (%s)\n");
       retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
     }
-    assert(sizeof(key)<=MSTRO_OFI_KEY_LEN_MAX); 
+    assert(sizeof(key)<=MSTRO_OFI_KEY_LEN_MAX);
     /* keysize is 4 on verbs ... we try to let the compiler handle byte order issues here by casting */
     switch(keysize) {
         case 1: {
@@ -1402,7 +1402,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
 	   memcpy(mr_key, &key, sizeof(key));
 	   break;
 	}
-        default: 
+        default:
             ERR("Unsupported MR key size %d\n", keysize);
 	    assert(0);
     }
@@ -1417,13 +1417,13 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
       addr=(uint64_t)&g_component_descriptor;
     }
   }
-               
-    
+
+
 
   {
     uint64_t k = 0;
     memcpy(&k, mr_key, keysize); /* just for printing */
-    
+
     INFO("Component info RDMA block registered%s: local %p, MR %p, addr %" PRIx64 ", desc %p, keysize %zu (key uint64 start: 0x%" PRIx64 ")\n",
          fi->domain_attr->mr_mode & FI_MR_RAW ? " (raw keys)" : "",
          &g_component_descriptor, mr, addr, fi_mr_desc(mr), keysize, k);
@@ -1440,7 +1440,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
   dst->component_info_addr = addr;
   dst->component_info_keysize = keysize;
   dst->component_info_raw_key = mr_key;
-                       
+
 
   dst->next = NULL;
 
@@ -1450,7 +1450,7 @@ mstro_ep_build_from_ofi(struct mstro_endpoint *dst,
     ERR("Failed to construct worker mgmt ep description: %d\n", retstat);
     goto BAILOUT_FAIL;
   }
-  
+
   retstat = mstro_ep_desc_serialize(&dst->addr_serialized,
                                     dst);
   if(retstat!=MSTRO_OK) {
@@ -1483,7 +1483,7 @@ BAILOUT_FAIL:
   /* wait sets (none) */
   CLEANUP(fabric,"fabric");
 #undef CLEANUP
-  
+
 BAILOUT:
   return retstat;
 }
@@ -1502,7 +1502,7 @@ mstro_endpoint_describe(struct mstro_endpoint *ep)
   char addrbuf[EP_DESC_BUF_MAX];
 
   ep_name_buf[0]='\0';
-  
+
   size=0;
   ret = fi_getname(&ep->ep->fid,NULL,&size);
   if(ret!=-FI_ETOOSMALL) {
@@ -1511,16 +1511,16 @@ mstro_endpoint_describe(struct mstro_endpoint *ep)
   }
   assert(size<EP_DESC_BUF_MAX);
   fi_getname(&ep->ep->fid,addrbuf,&size);
-  
+
   size=0;
   fi_av_straddr(ep->av, addrbuf, NULL, &size);
   assert(size<EP_DESC_BUF_MAX);
 
   fi_av_straddr(ep->av, addrbuf, strbuf, &size);
-  
+
   size=snprintf(ep_name_buf, EP_DESC_BUF_MAX,
                 "OFI EP prov %s name %s straddr %s",
-                ep->fi->fabric_attr->prov_name, 
+                ep->fi->fabric_attr->prov_name,
                 ep->fi->fabric_attr->name, strbuf);
   assert(size<1024);
 DONE:
@@ -1655,7 +1655,7 @@ mstro_ofi__order_fi_list(struct fi_info **fi)
 {
   if(fi==NULL)
     return MSTRO_INVARG;
- 
+
   struct fi_info *head = *fi;
   LL_SORT(head, mstro_ofi__fi_info_cmp);
   *fi = head;
@@ -1702,11 +1702,11 @@ mstro_ofi_init(void)
   hints->mode = MSTRO_OFI_MODE;
   hints->ep_attr->type = MSTRO_OFI_EP_TYPE;
   hints->domain_attr->mr_mode = MSTRO_OFI_MRMODE;
-  
+
   /* we really want 1.8 or above */
   stat = fi_getinfo(MSTRO_OFI_VERSION, NULL, NULL, 0, hints, &fi);
   fi_freeinfo(hints);
-  
+
   if(stat!=0) {
     ERR("fi_getinfo failed: %d (%s)\n", stat, fi_strerror(-stat));
     retstat=MSTRO_FAIL; goto BAILOUT_FAIL;
@@ -1748,7 +1748,7 @@ mstro_ofi_init(void)
     goto BAILOUT_FAIL;
   }
 
-  /* also do it for the PM descriptor block we'll RDMA read into */ 
+  /* also do it for the PM descriptor block we'll RDMA read into */
   assert(sizeof(g_pm_component_descriptor)%sysconf(_SC_PAGESIZE)==0);
   retstat = mstro_memlock(&g_pm_component_descriptor, sizeof(g_pm_component_descriptor));
   if(retstat!=MSTRO_OK) {
@@ -1762,11 +1762,11 @@ mstro_ofi_init(void)
   LL_FOREACH(fi,tmp) {
     NOISE("index %zu\n", i);
     i++;
-    DEBUG("potential endpoint: %s %s\n", tmp->fabric_attr->prov_name, 
+    DEBUG("potential endpoint: %s %s\n", tmp->fabric_attr->prov_name,
 		    tmp->fabric_attr->name);
     retstat = mstro_ep_build_from_ofi(&(g_endpoints->eps[g_endpoints->size]),
                                       tmp);
-    
+
     if(retstat!=MSTRO_OK) {
       WARN("Failed to build EP %zu, trying others\n", i);
     } else {
@@ -1799,18 +1799,18 @@ mstro_ofi_init(void)
   }
   /* fix pointer into invalid last entry */
   g_endpoints->eps[g_endpoints->size-1].next = NULL;
-  
+
   if(g_endpoints!=NULL) {
     INFO("%zu usable endpoints\n", g_endpoints->size);
     retstat = MSTRO_OK;
   } else {
     ERR("No usable endpoints found\n");
   }
-  
+
   goto BAILOUT;
 BAILOUT_FAIL:
   ; /* FIXME: free resources */
-  
+
 BAILOUT:
   return retstat;
 }
@@ -1842,7 +1842,7 @@ mstro_ofi_finalize(void)
       }
       /* every EP has one lock on the component descriptor */
       mstro_memunlock(&g_component_descriptor, sizeof(g_component_descriptor));
-      
+
 #define CLOSE_FID(member, descr) do {                                   \
         if(e->member) {                                                 \
           DEBUG("Closing down %s for ep %zu\n", descr, i);              \
@@ -1851,7 +1851,7 @@ mstro_ofi_finalize(void)
                         descr, i, s, fi_strerror(-s)); }                \
         }                                                               \
       } while(0)
-      
+
       CLOSE_FID(ep,"EP");
       CLOSE_FID(cq,"CQ");
       CLOSE_FID(av,"AV");
@@ -1859,16 +1859,16 @@ mstro_ofi_finalize(void)
       CLOSE_FID(domain,"DOMAIN");
       //      CLOSE_FID(fi,"FABRIC");
 #undef CLOSE_FID
-      
+
     }
   }
   DEBUG("OFI endpoints closed\n");
-  
+
   /* It's safe to call this even if we've not yet locked the page; we
    * ignore the error that we'd get in this case */
   mstro_memunlock(&g_component_descriptor, sizeof(g_component_descriptor));
   mstro_memunlock(&g_pm_component_descriptor, sizeof(g_pm_component_descriptor));
-  
+
   return MSTRO_OK;
 }
 
@@ -1890,7 +1890,7 @@ struct mstro_ofi_msg_context_ {
   };
   bool has_completion;              /**< indicates if pthread waiting
                                      * infrastructure is initialized in this
-                                     * context and should be used*/       
+                                     * context and should be used*/
   pthread_cond_t completion;        /**< Condition variable for originator of
                                      * message to */
   pthread_mutex_t lock;             /**< a lock, mostly because cond_wait
@@ -1901,7 +1901,7 @@ typedef struct mstro_ofi_msg_context_ * mstro_ofi_msg_context;
 
 
 /** Create a message completion context for @arg msg.
-    
+
     If @arg want_completion is true a mutex and a condition variable
     will be initialized and the completion handler can signal
     completion on that condition variable to the originator.
@@ -1923,7 +1923,7 @@ mstro_ofi__msg_context_create(mstro_ofi_msg_context *result_p,
   /* RDMA ops don't have non-NULL msg */
   /* if(msg==NULL) */
   /*   return MSTRO_INVARG; */
-  
+
   mstro_ofi_msg_context ctx
       = malloc(sizeof(struct mstro_ofi_msg_context_));
   if(ctx==NULL) {
@@ -1939,7 +1939,7 @@ mstro_ofi__msg_context_create(mstro_ofi_msg_context *result_p,
       free(ctx);
       ctx=NULL;
       goto BAILOUT;
-    } 
+    }
     res = pthread_cond_init(&ctx->completion, NULL);
     if(res!=0) {
       ERR("Failed to init ofi msg cond var: %d\n", res);
@@ -1957,7 +1957,7 @@ mstro_ofi__msg_context_create(mstro_ofi_msg_context *result_p,
   stat = MSTRO_OK;
 BAILOUT:
   *result_p = ctx;
-  return stat;  
+  return stat;
 }
 
 /** destroy an OFI message context */
@@ -1975,7 +1975,45 @@ mstro_ofi__msg_context_destroy(mstro_ofi_msg_context ctx)
   return MSTRO_OK;
 }
 
-
+/** Check the schema list provided from pm is compatible with the component schema list */
+/* compatible means that pm supports all of the schemas in the component schema list or more */
+/* FIXME check that the schema versions are compatible between the pm and the component */
+bool schema_list_compatible_p(const char * component_schema_list, const char * pm_schema_list)
+{
+  char *schema_name;
+  char *end_list_token;
+
+  if (component_schema_list == NULL)
+  {
+    /*nothing to look for here*/
+    //DEBUG("[schema_list_compatible_p] component schema list is NULL");
+    return true;
+  }
+  else if (pm_schema_list ==NULL) {
+    /*component_schema_list is not empty but pm_schema_list is empty ... we should look no further*/
+    //DEBUG("[schema_list_compatible_p] pm schema list is NULL");
+    return false;
+  }
+  // split the schema names in the component_schema_list
+  schema_name = strtok_r(component_schema_list, SCHEMA_LIST_SEP, &end_list_token);
+  // for each schema name in the component_schema_list ...try to find it in the pm_schema_list
+  while( schema_name != NULL )
+  {
+    DEBUG("Looking for schema %s \n", schema_name);
+    /*if we did not find the component schema name in the pm_schema_list*/
+    if(strstr(pm_schema_list, schema_name) == NULL) {
+      DEBUG("Schema %s not found in pm schema list\n", schema_name);
+      return false;
+    }
+    DEBUG("Found schema %s in %s\n", schema_name, pm_schema_list);
+    // read the next schema name
+    schema_name = strtok_r(NULL, SCHEMA_LIST_SEP, &end_list_token);
+  }
+
+  /*We reach here only if all component schemas were found in the pm_schema_list*/
+  return true;
+
+}
 
 /** select the best match between remote and local endpoints */
 mstro_status
@@ -2053,7 +2091,7 @@ mstro_ofi__select_endpoint(struct mstro_endpoint_descriptor_ *remote,
             stat = MSTRO_FAIL;
             goto BAILOUT;
           }
-          
+
           uint64_t mr_addr=0;
           /* since the EP accepted the address we interpolate that
            * MR_RAW will be set identically (while we really should be
@@ -2075,7 +2113,7 @@ mstro_ofi__select_endpoint(struct mstro_endpoint_descriptor_ *remote,
           }
           DEBUG("Checking for PM config block MR at (remote addr) 0x%" PRIx64 ", key %" PRIx64 "\n",
                 mr_addr, mr_key);
-          
+
           assert(ctx->msg==NULL);
           assert(my_ep->peer_info_mr!=NULL); /* incoming buffer has been registered at local endpoint set creation */
           void * local_buf_mr_desc = fi_mr_desc(my_ep->peer_info_mr);
@@ -2123,8 +2161,15 @@ mstro_ofi__select_endpoint(struct mstro_endpoint_descriptor_ *remote,
             INFO("Component descriptor mismatch, illegal type or other workflow, skipping\n");
             continue;
           }
+          DEBUG("PM schema list %s \n", g_pm_component_descriptor.schema_list);
+          DEBUG("Component schema list %s \n", g_component_descriptor.schema_list);
+          if(!schema_list_compatible_p(g_component_descriptor.schema_list, g_pm_component_descriptor.schema_list))
+          {
+            INFO("Component descriptor mismatch, PM does not support the schema list of component, skipping\n");
+            continue;
+          }
         } /* end of possible config block verification */
-        
+
         *remote_addr_p = translated_addr;
         *local_p = my_ep;
         stat=MSTRO_OK;
@@ -2161,7 +2206,7 @@ mstro_ofi__submit_message(struct mstro_endpoint *ep,
   assert(env!=NULL);
   DEBUG("Sending message %s, size %zu, %p using ep %p\n",
         env->descriptor->name, env->payload_size, env, ep);
-  
+
   int res;
   do {
     assert(env!=context); /* catch common mistake: context needs to be a
@@ -2174,7 +2219,7 @@ mstro_ofi__submit_message(struct mstro_endpoint *ep,
       /* DEBUG("tsend with tag %"PRIu64"\n", (uint64_t)msg->type); */
 
     } else {
-      res = fi_send(ep->ep, env->data, env->payload_size, 
+      res = fi_send(ep->ep, env->data, env->payload_size,
                     descriptor, dst, context);
     }
     if(res!=0) {
@@ -2205,9 +2250,9 @@ mstro_ofi__submit_message_wait(struct mstro_endpoint *ep, fi_addr_t dst,
     ERR("Failed to acquire msg context lock: %d\n", ret);
     return MSTRO_FAIL;
   }
-  
+
   stat = mstro_ofi__submit_message(ep, dst, msg, NULL, ctx);
-  if(stat!=MSTRO_OK) 
+  if(stat!=MSTRO_OK)
     return stat;
 
   ret = pthread_cond_wait(&ctx->completion, &ctx->lock);
@@ -2240,8 +2285,8 @@ mstro_ofi__submit_message_nowait(struct mstro_endpoint *ep, fi_addr_t dst,
   } else {
     ;
   }
-        
-    
+
+
   /* for non-completion contexts the message handler frees the
    * context */
   return mstro_ofi__submit_message(ep, dst, msg, NULL, ctx);
@@ -2257,7 +2302,7 @@ mstro_ofi__expect_message_wait(struct fid_ep *ep, fi_addr_t src,
       = mstro_ofi__msg_context_create(&ctx, msg, true);
   if(stat!=MSTRO_OK)
     return stat;
-  
+
   int ret = pthread_mutex_lock(&ctx->lock);
   if(ret!=0) {
     ERR("Failed to acquire msg context lock: %d\n", ret);
@@ -2266,7 +2311,7 @@ mstro_ofi__expect_message_wait(struct fid_ep *ep, fi_addr_t src,
 
   /* FIXME: split out do loop like for submit so that we don't need
    * fi_recv in arbitrary places anymore */
-  
+
   int res;
   do {
     if(MSTRO_MSG_HAS_TAG(msg)) {
@@ -2354,7 +2399,7 @@ mstro_pm__register_app(Mstro__Pool__Join *join_msg,
   }
   INFO("EPD serial %s\n", join_msg->serialized_endpoint);
   INFO("EPD parsed as %s\n", mstro_ep_desc_describe(epd));
-  
+
   uint32_t addr_format;
   void *remote_addr;
   size_t addrlen;
@@ -2384,7 +2429,7 @@ mstro_pm__register_app(Mstro__Pool__Join *join_msg,
 
   DEBUG("App %s advertises %zu transport methods\n",
         join_msg->component_name, join_msg->transport_methods->n_supported);
-  
+
   /* insert into registry table */
   s = mstro_pm_app_register(ep, translated_addr,
                             strdup(join_msg->serialized_endpoint),
@@ -2398,7 +2443,7 @@ mstro_pm__register_app(Mstro__Pool__Join *join_msg,
     join_msg->transport_methods = NULL; /* we kept a reference to it, caller will free the message */
     DEBUG("Assigned app ID %"PRIu64"\n", (*entry_p)->appid);
   }
-  
+
 BAILOUT:
   return s;
 BAILOUT_FREE:
@@ -2546,24 +2591,24 @@ mstro_ofi__check_and_handle_cq(struct mstro_endpoint *ep,
   INFO("Completion of op with ctx %p message %p%s of size %zu on endpoint %p\n",
        ctx, ctx->msg, re_post ? " (slot i)" : "",
        entry.len, ep);
-  
+
   if(re_post) {
     DEBUG("Posting fresh recv on slot %p", expected_slot_p);
     status = mstro_ofi__loop_post_recv(ep->ep, expected_slot_p, expected_ctx_p);
   }
-  
-  
+
+
   if(entry.flags&FI_RECV) {
     assert(ctx->msg!=NULL);
     ctx->msg->payload_size = entry.len; /* this permits us to not pass ofi 'entry' to pc/pm handler */
     const struct mstro_msg_envelope *msg = ctx->msg;
     status = incoming_msg_handler(msg, ep);
-    
+
     if(status!=MSTRO_OK) {
       ERR("Error handling incoming message, dropping it: %d (%s)\n",
           status, mstro_status_description(status));
     }
-    
+
     if(re_post) {
       ; /* fresh receive will be posted later, and slot messages have no completion watchers */
     } else {
@@ -2571,42 +2616,42 @@ mstro_ofi__check_and_handle_cq(struct mstro_endpoint *ep,
       DEBUG("non-slot message, cleaning up\n");
       if(ctx->has_completion) {
         DEBUG("ctx asks for completion notification\n");
-        status = mstro_ofi__maybe_notify_completion(ctx);          
+        status = mstro_ofi__maybe_notify_completion(ctx);
       } else {
         DEBUG("ctx wants no completion notification, cleaning up ctx\n");
         mstro_msg_envelope_free(ctx->msg);
         status = mstro_ofi__msg_context_destroy(ctx);
       }
     }
-    
+
   } else if(entry.flags&FI_SEND) {
     assert(ctx->msg!=NULL);
     struct mstro_msg_envelope *msg = ctx->msg;
-    
+
     /* completion of our sent messages */
     DEBUG("Send of message %p type %s completed\n",
           msg, msg->descriptor->name);
     /* otherwise clean it up */
     if(ctx->has_completion) {
       DEBUG("ctx asks for completion notification\n");
-      status = mstro_ofi__maybe_notify_completion(ctx);          
+      status = mstro_ofi__maybe_notify_completion(ctx);
     } else {
       DEBUG("ctx wants no completion notification, cleaning up ctx\n");
       mstro_msg_envelope_free(msg);
       status = mstro_ofi__msg_context_destroy(ctx);
     }
-    
+
   } else if(entry.flags&FI_RMA) {
     assert(ctx->msg==NULL);
     DEBUG("RDMA op completed, ctx %p\n", ctx);
     assert(ctx->has_completion); /* it does not make sense to
                                   * not have this set */
-    status = mstro_ofi__maybe_notify_completion(ctx);          
+    status = mstro_ofi__maybe_notify_completion(ctx);
   } else {
     DEBUG("Unexpected completion, flags %ull\n", entry.flags);
   }
-  
-  return status;      
+
+  return status;
 }
 
 static inline
@@ -2651,15 +2696,15 @@ mstro_ofi__loop(_Atomic bool *terminate,
   } while(false==atomic_load(terminate));
   DEBUG("PM comm thread quits: loop status %d, global status %d\n",
         s, status);
-  
+
 BAILOUT:
    if(slots) {
-     for(i=0; i<g_endpoints->size; i++) 
+     for(i=0; i<g_endpoints->size; i++)
        mstro_msg_envelope_free(slots[i]);
      free(slots);
    }
    if(ctxts) {
-     for(i=0; i<g_endpoints->size; i++) 
+     for(i=0; i<g_endpoints->size; i++)
        mstro_ofi__msg_context_destroy(ctxts[i]);
      free(ctxts);
    }
@@ -2672,7 +2717,7 @@ mstro_ofi_pm_loop(_Atomic bool *terminate)
   g_pool_app_id = MSTRO_APP_ID_MANAGER;
 
   return mstro_ofi__loop(terminate, mstro_pm_handle_msg);
-}  
+}
 
 mstro_status
 mstro_ofi_pc_loop(_Atomic bool *terminate)
@@ -2756,7 +2801,7 @@ mstro_pc_thread(void *closure)
         s, mstro_status_description(s));
   }
   DEBUG("pool client thread stopping\n");
-  
+
   return NULL;
 }
 */
@@ -2800,9 +2845,9 @@ mstro_pc_terminate(void)
         leave.base.descriptor->name);
     goto BAILOUT;
   }
-    
+
   status = mstro_pmp_send_nowait(MSTRO_APP_ID_MANAGER, &msg);
-  
+
   /* The BYE message will be handled by the normal incoming message handler
    * We can tell it's done by checking g_mstro_pm_attached */
   switch(status) {
@@ -2864,7 +2909,7 @@ mstro_pm_attach(const char *remote_pm_info)
   mstro_endpoint_descriptor pm_epd=NULL;
 
   mstro_status s=mstro_ep_desc_deserialize(&pm_epd, remote_pm_info);
-  
+
   if(s!=MSTRO_OK) {
     ERR("Failed to parse pool manager info: %d (%s%s)\n",
         s, mstro_status_description(s),
@@ -2881,7 +2926,7 @@ mstro_pm_attach(const char *remote_pm_info)
   {
     mstro_endpoint_descriptor tmp;
     size_t num_cookies = 0;
-    
+
     LL_FOREACH(pm_epd, tmp) {
       INFO("Pool manager endpoint: %s\n", mstro_ep_desc_describe(tmp));
       if(tmp->type==MSTRO_EP_OFI_GNI) {
@@ -2900,13 +2945,13 @@ mstro_pm_attach(const char *remote_pm_info)
       }
     }
   }
-  
+
   s=mstro_ofi_init();
   if(s!=MSTRO_OK) {
     ERR("Failed to initialize OFI layer\n");
     goto BAILOUT;
   }
-  
+
   /* start handler thread: run pool client loop. It will stop when mstro_pc_terminate is called */
   /* we need to start it before endpoint selection since we do RDMA
    * read for the config block, and that needs to report completion
@@ -2919,30 +2964,30 @@ mstro_pm_attach(const char *remote_pm_info)
     goto BAILOUT;
   }
   g_component_descriptor.type = MSTRO_COMPONENT_TYPE_APP;
-  
-  
+
+
   s=mstro_ofi__select_endpoint(pm_epd, &g_pm_endpoint, &g_pm_addr);
   if(s!=MSTRO_OK) {
     ERR("Failed to select a suitable endpoint to talk to pool manager\n");
     goto BAILOUT;
   }
   assert(g_pm_endpoint!=NULL);
-  
+
   /* register pool manager as a pmp app entry */
   s = mstro_pm_app_register_manager(g_pm_endpoint, g_pm_addr);
   if(s!=MSTRO_OK) {
     ERR("Failed to register Pool Manager in registry\n");
     goto BAILOUT;
   }
-  
-  
+
+
   /* send JOIN */
-  
-  
+
+
   /* FIXME make function set_transport_default() beware protobuf stack init
    */
   /* if we have OFI we'll add another entry */
-  
+
   Mstro__Pool__TransportKind transport_kinds[] =
       { MSTRO__POOL__TRANSPORT_KIND__GFS
 #ifdef HAVE_MIO
@@ -2950,18 +2995,18 @@ mstro_pm_attach(const char *remote_pm_info)
 #endif
       };
   size_t num_available_transports = sizeof(transport_kinds)/sizeof(transport_kinds[0]);
-  
+
   Mstro__Pool__TransportMethods transport_methods
       = MSTRO__POOL__TRANSPORT_METHODS__INIT;
   transport_methods.n_supported = num_available_transports;
   transport_methods.supported
       = (Mstro__Pool__TransportKind*)&transport_kinds;
-  
+
   const char* env_transport_default = getenv(MSTRO_ENV_TRANSPORT_DEFAULT);
   if (env_transport_default != NULL) {
     int i;
     int found = 0;
-    for (	i = 0;	i < MSTRO__POOL__TRANSPORT_KIND__NUMBER_OF_KINDS; i++ ) { 
+    for (	i = 0;	i < MSTRO__POOL__TRANSPORT_KIND__NUMBER_OF_KINDS; i++ ) {
       if (!strcmp(env_transport_default, g_transport_registry[i].name)) {
         transport_methods.supported[0] = (Mstro__Pool__TransportKind)i;
         found = 1;
@@ -2978,13 +3023,13 @@ mstro_pm_attach(const char *remote_pm_info)
   for (i = 0; i < transport_methods.n_supported; i++ )
     INFO("#%d %s \n", i, g_transport_registry[transport_methods.supported[i]].name);
   /**/
-  
+
   Mstro__Pool__Join join = MSTRO__POOL__JOIN__INIT;
   join.protocol_version = MSTRO_POOL_PROTOCOL_VERSION;
   join.serialized_endpoint = g_pm_endpoint->addr_serialized;
   join.transport_methods = &transport_methods;
   join.component_name = g_initdata->component_name;
-  
+
   Mstro__Pool__MstroMsg msg = MSTRO__POOL__MSTRO_MSG__INIT;
   s = mstro_pmp_package(&msg, (ProtobufCMessage*)&join);
   if(s!=MSTRO_OK) {
@@ -2992,13 +3037,13 @@ mstro_pm_attach(const char *remote_pm_info)
         join.base.descriptor->name);
     goto BAILOUT;
   }
-  
+
   s = mstro_pmp_send_nowait(MSTRO_APP_ID_MANAGER, &msg);
 
   /* The WELCOME message will be handled by the normal incoming
    * message handler.  We can tell it's done by checking
    * g_pool_app_id */
-  
+
   switch(s) {
     case MSTRO_OK: {
       /* spin-wait */
@@ -3010,7 +3055,7 @@ mstro_pm_attach(const char *remote_pm_info)
       }
       break;
     }
-    case MSTRO_NO_PM: 
+    case MSTRO_NO_PM:
       ERR("Confusion: JOIN message failed with 'no PM info' error\n");
       /* fall-thru */
     default:
@@ -3018,15 +3063,13 @@ mstro_pm_attach(const char *remote_pm_info)
           s, mstro_status_description(s));
       return s;
   }
-  
-  
+
+
   /* Welcome received */
-  INFO("Welcome message received, our app id is %"PRIu64"\n", 
+  INFO("Welcome message received, our app id is %"PRIu64"\n",
        g_pool_app_id);
-  
+
 
 BAILOUT:
   return s;
 }
-
-
diff --git a/maestro/pool.c b/maestro/pool.c
index 201fc667c1b85f8b1b2cae148e364aa05e3626d6..4fd09ec46454ba78be1c2856a735984a6a13c08b 100644
--- a/maestro/pool.c
+++ b/maestro/pool.c
@@ -1816,10 +1816,3 @@ mstro_subscription_dispose(mstro_subscription s)
 
 
 
-mstro_status
-mstro_pool__resolve_cdoid(const struct mstro_cdo_id *id,
-                          const char **name_p)
-{
-  /* we use the subscription infrastructure for this */
-  return mstro_pool_resolve_cdoid(id, name_p);
-}
diff --git a/maestro/pool_client.c b/maestro/pool_client.c
index 774d091190795c977ad40f313e881e400fa4d5de..a2e169e20fac4c28f7447de9c442290a02e02338 100644
--- a/maestro/pool_client.c
+++ b/maestro/pool_client.c
@@ -78,15 +78,19 @@ mstro_pc__handle_resolve_reply(const Mstro__Pool__ResolveReply *reply)
 }
 
 
-/* store @arg new_attributes in the serialized attributes slot of @arg cdo, and replace the attribute dict values accordingly. Consumes @arg new_attributes. */
+/* store @arg new_attributes in the serialized attributes slot of @arg
+ * cdo, and replace the attribute dict values accordingly. Consumes
+ * @arg new_attributes. */
 static inline
 mstro_status
-mstro_cdo__replace_attributes(mstro_cdo cdo, Mstro__Pool__Attributes *new_attributes)
+mstro_cdo__replace_attributes(mstro_cdo cdo,
+                              Mstro__Pool__Attributes *new_attributes)
 {
   /* We can't lock a CDO on it's own. But this function should only be
-   * called before SEAL state change completes, and so users can't be
-   * accessing the CDO legally, and it's way before the CDO could be
-   * in the pool, so transport etc can't touch it either. */
+   * called before SEAL state change completes, or during WITHDRAW
+   * etc. and so users can't be accessing the CDO legally, and it's
+   * way before or after the CDO could be in the pool, so transport
+   * etc can't touch it either. */
   assert(mstro_cdo_state_check(cdo, MSTRO_CDO_STATE_DECLARED));
 
   mstro_status s = MSTRO_UNIMPL;
@@ -269,6 +273,31 @@ mstro_pc__select_transfer_method(mstro_cdo cdo,
   return MSTRO_OK;
 }
 
+static inline
+mstro_status
+mstro_pc__construct_gfs_path_for_cdo(const mstro_cdo src_cdo,
+                                     char **path)
+{
+  mstro_status s=MSTRO_UNIMPL;
+  assert(path!=NULL);
+  assert(src_cdo!=NULL);
+
+  size_t l = g_mstro_transport_gfs_dir_len + strlen(src_cdo->name) + 1;
+  *path = malloc(l);
+  if(*path==NULL) {
+    return MSTRO_NOMEM;
+  }
+  strcpy(*path, g_mstro_transport_gfs_dir);
+  
+  /* FIXME: possibly add a (configurable?) number of subdirectories
+   * based on first segment of CDO name */
+  
+  strcpy((*path)+g_mstro_transport_gfs_dir_len, src_cdo->name);
+  DEBUG("Constructed GFS transport path %s\n", *path);
+  return MSTRO_OK;
+}
+
+
 static inline
 mstro_status
 mstro_pc__handle_initiate_transfer(const Mstro__Pool__InitiateTransfer* init)
@@ -357,22 +386,28 @@ mstro_pc__handle_initiate_transfer(const Mstro__Pool__InitiateTransfer* init)
   Mstro__Pool__TransferTicket ticket = MSTRO__POOL__TRANSFER_TICKET__INIT;
   ticket.cdoid = &id;
 
-  const int64_t *size;
-  enum mstro_cdo_attr_value_type type;
-  if (! (MSTRO_OK == mstro_cdo_attribute_get(src_cdo,
-                                             MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                             &type, (const void**)&size))) {
-    ERR("Couldn't retrieve CDO %s local_size needed for transport\n", src_cdo->name);
+  mstro_status s=MSTRO_UNIMPL;
+  const void *size=NULL;
+  enum mstro_cdo_attr_value_type vt;
+  s = mstro_attribute_dict_get(src_cdo->attributes,
+                               MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                               &vt, &size, NULL, false);
+  if(s==MSTRO_NOENT && vt==MSTRO_CDO_ATTR_VALUE_INVALID) {
+    ERR("CDO has mamba-array but no local-size\n");
+    return MSTRO_FAIL;
+  }
+  if(s!=MSTRO_OK) {
+    ERR("Failed to retrieve local-size on CDO\n");
     return MSTRO_FAIL;
   }
-  int64_t realsize = *size;
+
+  int64_t realsize = *(int64_t*)size;
   if(realsize==-1) {
     DEBUG("Source CDO empty, doing NULL transfer\n");
     realsize = 0;
   }
-  if (!g_mio_available
-      || (( realsize % getpagesize()) != 0 
-          && init->methods->supported[0] == MSTRO__POOL__TRANSPORT_KIND__MIO)
+  if (   init->methods->supported[0] == MSTRO__POOL__TRANSPORT_KIND__MIO
+      && (!g_mio_available || (realsize % getpagesize()) != 0 )
       ){
     WARN("Not issuing a ticket with MIO. Either not available or CDO size (%zu)"
          " is not a multiple of the page size (%d)."
@@ -398,8 +433,13 @@ mstro_pc__handle_initiate_transfer(const Mstro__Pool__InitiateTransfer* init)
   switch(ticket.ticket_case) {
     case MSTRO__POOL__TRANSFER_TICKET__TICKET_GFS:
       INFO("TICKET CASE GFS\n");
-      gfs.path = src_cdo->name;
-      gfs.keep_file = 0; // Arbitrarily rm the transport file on dst
+      status= mstro_pc__construct_gfs_path_for_cdo(src_cdo, &gfs.path);
+      if(status!=MSTRO_OK) {
+        ERR("Failed to construct GFS path for SRC-CDO: %d (%s)\n",
+            status, mstro_status_description(status));
+        return status;
+      }
+      gfs.keep_file = 1; // don't arbitrarily rm the transport file on dst
       break;
     case MSTRO__POOL__TRANSFER_TICKET__TICKET_MIO:
       INFO("TICKET CASE MIO\n");
@@ -473,7 +513,7 @@ mstro_pc__handle_initiate_transfer(const Mstro__Pool__InitiateTransfer* init)
   INFO("Issued ticket for CDO %s, and starting execute process\n", src_cdo->name);
   
   //INFO("TransferTicket using path %s\n", ticket.gfs->path);
-  INFO("TransferTicket cdo size %zu\n", ticket.data_size);
+  INFO("TransferTicket cdo size %" PRIi64 "\n", ticket.data_size);
   
   /* Execute transport (non-blocking) */
   status = mstro_transport_execute(src_cdo, &ticket);
diff --git a/maestro/pool_manager.c b/maestro/pool_manager.c
index 95fff9b67c9e4e78435e71ceca4a7e3e9b3a6939..5627de67fa04c3cef9b60f3f99e990fac9af2bda 100644
--- a/maestro/pool_manager.c
+++ b/maestro/pool_manager.c
@@ -441,6 +441,31 @@ mstro_pm__msg_free(Mstro__Pool__MstroMsg *msg)
     mstro__pool__mstro_msg__free_unpacked(msg, NULL);
   }
 }
+
+
+/** if the application receiving EV needs to be told the name, send
+ * it, otherwise set it to NULL in the EV structure */
+static inline
+mstro_status
+mstro_pm__possibly_fill_event_cdoname(
+    const struct mstro_cdo_id *cdoid,
+    mstro_app_id recipient,
+    Mstro__Pool__Event *ev)
+{
+  recipient = recipient; /* avoid unused warning */
+  
+  /** FIXME: for the moment we just always send the name, could be improved */
+  mstro_status s=
+      mstro_pm_cdo_registry_cdo_name_lookup(cdoid,
+                                            (const char **)&ev->cdo_name);
+  if(s!=MSTRO_OK) {
+    WITH_CDO_ID_STR(idstr, cdoid, {
+        ERR("Cannot find CDO registry entry for |%s|\n",
+            idstr);});
+    return MSTRO_NOENT;
+  }
+  return MSTRO_OK;  
+}
 
 
 /*** DECLARE ***/
@@ -505,6 +530,7 @@ mstro_pm__handle_declare_phase2(mstro_event event,
    * on stack and will only be alive until notify-and-continue is
    * done */
   ev.origin_id = cont->msg->token->appid;
+  ev.cdo_name = cont->msg->declare->cdo_name;
 
   status = mstro_pm__event_notify_and_continue(
       &ev,
@@ -513,7 +539,7 @@ mstro_pm__handle_declare_phase2(mstro_event event,
       mstro_pm__handle_declare_phase3,
       cont->msg, NULL, NULL);
   if(status!=MSTRO_OK) {
-    ERR("Failed notifying subscribers of DECALRE:after event: %d (%s)\n",
+    ERR("Failed notifying subscribers of DECLARE:after event: %d (%s)\n",
         status, mstro_status_description(status));
     goto BAILOUT_FREE;
   }
@@ -543,6 +569,7 @@ mstro_pm__handle_declare(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_DECLARE;
   ev.declare = msg->declare;
   ev.origin_id = msg->token->appid;
+  ev.cdo_name = msg->declare->cdo_name;
 
   mstro_status status
       = mstro_pm__event_notify_and_continue(
@@ -966,6 +993,10 @@ mstro_pm__handle_seal_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_SEAL;
   ev.seal = cont->msg->seal;
   ev.origin_id = cont->msg->token->appid;
+  mstro_status s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   status = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1020,6 +1051,10 @@ mstro_pm__handle_seal(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_SEAL;
   ev.seal = msg->seal;
   ev.origin_id = msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1088,6 +1123,10 @@ mstro_pm__handle_offer_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_OFFER;
   ev.offer = cont->msg->offer;
   ev.origin_id = cont->msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1114,8 +1153,10 @@ mstro_pm__handle_offer(Mstro__Pool__MstroMsg *msg)
 {
   Mstro__Pool__Offer *offer = msg->offer;
   mstro_app_id app_id = msg->token->appid->id;
-  
   assert(offer!=NULL); assert(app_id!=MSTRO_APP_ID_INVALID);
+  struct mstro_cdo_id cdoid = { .qw[0] = offer->cdoid->qw0,
+                                .qw[1] = offer->cdoid->qw1 };
+  
   DEBUG("CDO OFFER from %zu\n", app_id);
   mstro_status s=MSTRO_FAIL;
   
@@ -1130,6 +1171,10 @@ mstro_pm__handle_offer(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_OFFER;
   ev.offer = msg->offer;
   ev.origin_id = msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1201,7 +1246,11 @@ mstro_pm__handle_require_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_REQUIRE;
   ev.require = cont->msg->require;
   ev.origin_id = cont->msg->token->appid;
-
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
+ 
   s = mstro_pm__event_notify_and_continue(
       &ev,
       false,
@@ -1229,6 +1278,9 @@ mstro_pm__handle_require(Mstro__Pool__MstroMsg *msg)
   Mstro__Pool__Require *require = msg->require;
   mstro_app_id app_id = msg->token->appid->id;
   assert(require!=NULL); assert(app_id!=MSTRO_APP_ID_INVALID);
+  struct mstro_cdo_id cdoid = { .qw[0] = require->cdoid->qw0,
+                                .qw[1] = require->cdoid->qw1 };
+
   DEBUG("CDO REQUIRE from %zu\n", app_id);
   mstro_status s;
   
@@ -1243,6 +1295,10 @@ mstro_pm__handle_require(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_REQUIRE;
   ev.require = msg->require;
   ev.origin_id = msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1295,8 +1351,12 @@ mstro_pm__handle_retract_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_RETRACT;
   ev.retract = cont->msg->retract;
   ev.origin_id = cont->msg->token->appid;
+  mstro_status s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
-  mstro_status s = mstro_pm__event_notify_and_continue(
+  s = mstro_pm__event_notify_and_continue(
       &ev,
       false,
       NULL,
@@ -1322,7 +1382,13 @@ mstro_pm__handle_retract(Mstro__Pool__MstroMsg *msg)
 {
   Mstro__Pool__Retract *retract = msg->retract;
   mstro_app_id app_id = msg->token->appid->id;
-  retract = retract; app_id=app_id; /* avoid unused arg warning */
+  assert(retract!=NULL);
+
+  app_id=app_id; /* avoid unused arg warning */
+  struct mstro_cdo_id cdoid = { .qw[0] = retract->cdoid->qw0,
+                                .qw[1] = retract->cdoid->qw1 };
+  
+
 
   Mstro__Pool__Event ev = MSTRO__POOL__EVENT__INIT;
   /* serial and handle set later */
@@ -1330,8 +1396,13 @@ mstro_pm__handle_retract(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_RETRACT;
   ev.retract = msg->retract;
   ev.origin_id = msg->token->appid;
+  mstro_status s =
+      mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
-  mstro_status s = mstro_pm__event_notify_and_continue(
+  s = mstro_pm__event_notify_and_continue(
       &ev,
       true,
       NULL,
@@ -1401,6 +1472,10 @@ mstro_pm__handle_demand_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_DEMAND;
   ev.demand = cont->msg->demand;
   ev.origin_id = cont->msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1429,6 +1504,11 @@ mstro_pm__handle_demand(Mstro__Pool__MstroMsg *msg)
   Mstro__Pool__Demand *demand = msg->demand;
   mstro_app_id app_id = msg->token->appid->id;
   assert(demand!=NULL); assert(app_id!=MSTRO_APP_ID_INVALID);
+
+  struct mstro_cdo_id cdoid = { .qw[0] = demand->cdoid->qw0,
+                                .qw[1] = demand->cdoid->qw1 };
+  
+
   DEBUG("CDO DEMAND from %zu\n", app_id);
   mstro_status s;
   
@@ -1443,6 +1523,10 @@ mstro_pm__handle_demand(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_DEMAND;
   ev.demand = msg->demand;
   ev.origin_id = msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1511,6 +1595,10 @@ mstro_pm__handle_withdraw_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_WITHDRAW;
   ev.withdraw = cont->msg->withdraw;
   ev.origin_id = cont->msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1555,6 +1643,10 @@ mstro_pm__handle_withdraw(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_WITHDRAW;
   ev.withdraw = msg->withdraw;
   ev.origin_id = msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1610,6 +1702,10 @@ mstro_pm__handle_dispose_phase2(mstro_event event,
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_DISPOSE;
   ev.dispose = cont->msg->dispose;
   ev.origin_id = cont->msg->token->appid;
+  s = mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
   s = mstro_pm__event_notify_and_continue(
       &ev,
@@ -1636,7 +1732,12 @@ mstro_pm__handle_dispose(Mstro__Pool__MstroMsg *msg)
 {
   Mstro__Pool__Dispose *dispose = msg->dispose;
   mstro_app_id app_id = msg->token->appid->id;
-  dispose=dispose; app_id=app_id; /* avoid unused arg warning */
+  app_id=app_id; /* avoid unused arg warning */
+  assert(dispose!=NULL);
+
+  struct mstro_cdo_id cdoid = { .qw[0] = dispose->cdoid->qw0,
+                                .qw[1] = dispose->cdoid->qw1 };
+
 
   Mstro__Pool__Event ev = MSTRO__POOL__EVENT__INIT;
   /* serial and handle set later */
@@ -1644,8 +1745,13 @@ mstro_pm__handle_dispose(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_DISPOSE;
   ev.dispose = msg->dispose;
   ev.origin_id = msg->token->appid;
+  mstro_status s =
+      mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
-  mstro_status s = mstro_pm__event_notify_and_continue(
+  s = mstro_pm__event_notify_and_continue(
       &ev,
       true,
       NULL,
@@ -1684,6 +1790,9 @@ mstro_pm__handle_transfer_completed(Mstro__Pool__MstroMsg *msg)
   mstro_app_id app_id = msg->token->appid->id;
   assert(completion!=NULL); assert(app_id!=MSTRO_APP_ID_INVALID);
 
+  struct mstro_cdo_id cdoid = { .qw[0] = completion->cdoid->qw0,
+                                .qw[1] = completion->cdoid->qw1 };
+  
   DEBUG("TRANSFER COMPLETION from %zu\n", app_id);
 
   /* produce completed:after event */
@@ -1693,8 +1802,13 @@ mstro_pm__handle_transfer_completed(Mstro__Pool__MstroMsg *msg)
   ev.payload_case = MSTRO__POOL__EVENT__PAYLOAD_TRANSFER_COMPLETED;
   ev.transfer_completed = msg->transfer_completed;
   ev.origin_id = msg->token->appid;
+  mstro_status s =
+      mstro_pm__possibly_fill_event_cdoname(&cdoid, app_id, &ev);
+  if(s!=MSTRO_OK) {
+    goto BAILOUT_FREE;
+  }
 
-  mstro_status s = mstro_pm__event_notify_and_continue(
+  s = mstro_pm__event_notify_and_continue(
       &ev,
       false,
       NULL,
diff --git a/maestro/pool_manager_registry.c b/maestro/pool_manager_registry.c
index bda4204003eeeeedaeb3976072700327dc49d180..af22504a0e1f9541b2bc38052a4c92df542f8685 100644
--- a/maestro/pool_manager_registry.c
+++ b/maestro/pool_manager_registry.c
@@ -1133,7 +1133,7 @@ mstro_pm_cdo_app_match(mstro_app_id origin, const struct mstro_cdo_id *id,
   /*           str, origin, cdo_selector, cdo_selector->query); */
   /*   }); */
   mstro_status status = MSTRO_FAIL;
-  /* now fetcch attributes from per-app-CDO entry, call subscription-module checker and return result */
+  /* now fetch attributes from per-app-CDO entry, call subscription-module checker and return result */
   WITH_LOCKED_CDO_REGISTRY({
       struct per_app_cdo_entry *entry;
       status = mstro_pm_cdo_app_lookup(id, origin, &entry);
diff --git a/maestro/subscription_registry.c b/maestro/subscription_registry.c
index db4402a3d2912abf35f85155a9dc6fa8e5b594da..cb34610d24b8a738ae25ae3859dc5c0d310c942b 100644
--- a/maestro/subscription_registry.c
+++ b/maestro/subscription_registry.c
@@ -324,6 +324,7 @@ mstro__subscr_resolver_lookup_cdoid(const struct mstro_cdo_id *id,
           goto ABORT;
         }
       }
+      DEBUG("Entering wait for resolver lookup reply\n");
       /* setup done, time to wait. ResolveReply will be handled by
        * @ref mstro_pool_resolve_reply_bh will will trigger our
        * event */
@@ -1424,13 +1425,15 @@ const Mstro__Pool__KvEntry *
 mstro_subscription__pool_attr_find(const Mstro__Pool__Attributes__Map *kv_map,
                                    const char *key)
 {
-  if(kv_map==NULL) 
+  if(kv_map==NULL) {
+    DEBUG("Empty kv map when looking for key |%s|\n", key);
     return NULL;
-  else {
-    for(size_t i=0; i<kv_map->n_map; i++) 
+  } else {
+    for(size_t i=0; i<kv_map->n_map; i++) {
       if(strcmp(key,kv_map->map[i]->key)==0) {
         return kv_map->map[i];
       }
+    }
     return NULL;
   }      
 }
@@ -1487,18 +1490,18 @@ mstro_subscription__csq_eval(const struct mstro_csq_val *csq,
         entry = mstro_subscription__pool_attr_find(attributes->kv_map, key);
         if(entry==NULL) {
           if(complement) {
-            DEBUG("has-not matched for |%s|\n", key);
+            DEBUG("HAS-NOT matched for |%s|\n", key);
             return MSTRO_OK;
           } else {
-            DEBUG("has did not match for |%s|\n", key);
+            DEBUG("HAS did not match for |%s|\n", key);
             return MSTRO_NOMATCH;
           }
         } else {
           if(complement) {
-            DEBUG("has-not did not match for |%s|\n", key);
+            DEBUG("HAS-NOT did not match for |%s|\n", key);
             return MSTRO_NOMATCH;
           } else {
-            DEBUG("has matched for |%s|\n", key);
+            DEBUG("HAS matched for |%s|\n", key);
             return MSTRO_OK;
           }
         }
@@ -1526,7 +1529,8 @@ mstro_subscription__csq_eval(const struct mstro_csq_val *csq,
             return MSTRO_NOMATCH;
           }
         } else {
-          /* key matched. Now need to parse the value in the csq and call the appropriate comparison function */
+          /* key matched. Now need to parse the value in the csq and
+           * call the appropriate comparison function */
           const Mstro__Pool__AVal *aval = entry->val;
           void *rhsval=NULL;
           size_t rhsval_size=0;
@@ -1605,7 +1609,8 @@ mstro_subscription_selector_eval(const struct mstro_cdo_id *cdoid,
 
   struct mstro_csq_val *csq = sel->csq;
   if(csq==NULL) {
-    /* FIXME: maybe we need to create it on the first use of SEL if we're on the PM? */
+    /* FIXME: maybe we need to create it on the first use of SEL if
+     * we're on the PM? */
     ERR("No parsed query (CSQ) available\n");
     return MSTRO_FAIL;
   }
@@ -1633,7 +1638,7 @@ mstro_subscription_selector_check(struct mstro_subscription_ *s,
     /* declare is special: no CDOID known yet, no attribuites, only
      * string name. Efficient subscriptions will subscribe to
      * DECLARE_ACK instead. */
-    WARN("DECLARE event always matches -- FIXME\n");
+    WARN("DECLARE event always matches -- FIXME: only name could be checked (and we don't)\n");
     return MSTRO_OK;
   } else {
     /* we always need the CDO id. Unfortunately it's slightly buried in the EV */
@@ -1656,7 +1661,8 @@ mstro_subscription_selector_check(struct mstro_subscription_ *s,
         ERR("Failed to serialized attribute message for CDO\n");
         return status;
       }
-      status = mstro_subscription_selector_eval(&id, s->cdo_selector, attributes);
+      status = mstro_subscription_selector_eval(&id, s->cdo_selector,
+                                                attributes);
     }
   }
   return status;
@@ -1756,12 +1762,15 @@ mstro_pool_event_advertise(Mstro__Pool__Event *ev,
             
             if(s->subscription->needs_ack) {
               uint64_t num_outstanding
-                  = atomic_fetch_add_explicit(&ctx->nr_outstanding_acks, 1, memory_order_release);
-              DEBUG("Now %" PRIu64 " outstanding acks for this event\n", num_outstanding);
+                  = atomic_fetch_add_explicit(&ctx->nr_outstanding_acks,
+                                              1, memory_order_release);
+              DEBUG("Now %" PRIu64 " outstanding acks for this event\n",
+                    num_outstanding);
 
               mstro_event ack_event;
               status = mstro_event_create(g_subscription_table.edom,
-                                          event_ack_cb, ctx, NULL, true, &ack_event);
+                                          event_ack_cb, ctx, NULL, true,
+                                          &ack_event);
               if(status!=MSTRO_OK) {
                 ERR("Failed to create event for subscription ack, skipping subscriber %" PRIu64 "\n",
                     s->subscriber);
@@ -1901,9 +1910,25 @@ mstro_pool_event_consume(const Mstro__Pool__Event *eventmsg)
       const char* cdo_name;
       cdo_name = malloc(MSTRO_CDO_NAME_MAX);
       assert(cdo_name!=NULL);
-      mstro_status s = mstro__subscr_resolver_lookup_cdoid(
-                         (struct mstro_cdo_id*)eventmsg->offer->cdoid, &cdo_name);
-      ev->offer.cdo_name = (char*)cdo_name;
+      struct mstro_cdo_id id = {
+        .qw[0] = eventmsg->offer->cdoid->qw0,
+        .qw[1] = eventmsg->offer->cdoid->qw1
+      };
+      /* we cannot call the resolver in here, as that would lock up
+       * our thread in the PC. But CDO-related events should have a
+       * NAME value */
+      if(eventmsg->cdo_name==NULL) {
+        ERR("OFFER event missing a CDO name\n");
+        free(ev);
+        return MSTRO_FAIL;
+      } else {
+        ev->offer.cdo_name = strdup(eventmsg->cdo_name);
+        if(ev->declare.cdo_name == NULL) {
+          ERR("Failed to allocate event data\n");
+          free(ev);
+          return MSTRO_NOMEM;
+        }
+      }
       DEBUG("Event: OFFER for |%s| from %" PRIu64 "\n",
               ev->offer.cdo_name, ev->offer.appid);
       break;
@@ -1914,23 +1939,21 @@ mstro_pool_event_consume(const Mstro__Pool__Event *eventmsg)
              && eventmsg->origin_id->id!=MSTRO_APP_ID_INVALID);
       assert(eventmsg->seal->cdoid!=NULL);
       ev->seal.appid = eventmsg->origin_id->id;
-      {
-        WARN("FIXME: Missing CDO resolver, returning ID string\n");
-        struct mstro_cdo_id id = {.qw[0] = eventmsg->seal->cdoid->qw0,
-                                  .qw[1] = eventmsg->seal->cdoid->qw1 };
-
-        WITH_CDO_ID_STR(idstr, &id, {
-            ev->seal.cdo_name = strdup(idstr);
-          });
+      if(eventmsg->cdo_name==NULL) {
+        ERR("SEAL event missing a CDO name\n");
+        free(ev);
+        return MSTRO_FAIL;
+      } else {
+        ev->seal.cdo_name = strdup(eventmsg->cdo_name);
         if(ev->seal.cdo_name == NULL) {
           ERR("Failed to allocate event data\n");
           free(ev);
           return MSTRO_NOMEM;
         }
-
-        DEBUG("Event: SEAL for |%s| from %" PRIu64 "\n",
-              ev->seal.cdo_name, ev->seal.appid);
       }
+
+      DEBUG("Event: SEAL for |%s| from %" PRIu64 "\n",
+            ev->seal.cdo_name, ev->seal.appid);
       break;
       
     case MSTRO_POOL_EVENT_REQUIRE:
@@ -1939,23 +1962,21 @@ mstro_pool_event_consume(const Mstro__Pool__Event *eventmsg)
              && eventmsg->origin_id->id!=MSTRO_APP_ID_INVALID);
       assert(eventmsg->require->cdoid!=NULL);
       ev->require.appid = eventmsg->origin_id->id;
-      {
-        WARN("FIXME: Missing CDO resolver, returning ID string\n");
-        struct mstro_cdo_id id = {.qw[0] = eventmsg->require->cdoid->qw0,
-                                  .qw[1] = eventmsg->require->cdoid->qw1 };
-
-        WITH_CDO_ID_STR(idstr, &id, {
-            ev->require.cdo_name = strdup(idstr);
-          });
+      if(eventmsg->cdo_name==NULL) {
+        ERR("REQUIRE event missing a CDO name\n");
+        free(ev);
+        return MSTRO_FAIL;
+      } else {
+        ev->require.cdo_name = strdup(eventmsg->cdo_name);
         if(ev->require.cdo_name == NULL) {
           ERR("Failed to allocate event data\n");
           free(ev);
           return MSTRO_NOMEM;
         }
-
-        DEBUG("Event: REQUIRE for |%s| from %" PRIu64 "\n",
-              ev->require.cdo_name, ev->require.appid);
       }
+      
+      DEBUG("Event: REQUIRE for |%s| from %" PRIu64 "\n",
+            ev->require.cdo_name, ev->require.appid);
       break;
 
     case MSTRO_POOL_EVENT_WITHDRAW:
@@ -1964,23 +1985,22 @@ mstro_pool_event_consume(const Mstro__Pool__Event *eventmsg)
              && eventmsg->origin_id->id!=MSTRO_APP_ID_INVALID);
       assert(eventmsg->withdraw->cdoid!=NULL);
       ev->withdraw.appid = eventmsg->origin_id->id;
-      {
-        WARN("FIXME: Missing CDO resolver, returning ID string\n");
-        struct mstro_cdo_id id = {.qw[0] = eventmsg->withdraw->cdoid->qw0,
-                                  .qw[1] = eventmsg->withdraw->cdoid->qw1 };
-        
-        WITH_CDO_ID_STR(idstr, &id, {
-            ev->withdraw.cdo_name = strdup(idstr);
-          });
+
+      if(eventmsg->cdo_name==NULL) {
+        ERR("WITHDRAW event missing a CDO name\n");
+        free(ev);
+        return MSTRO_FAIL;
+      } else {
+        ev->withdraw.cdo_name = strdup(eventmsg->cdo_name);
         if(ev->withdraw.cdo_name == NULL) {
           ERR("Failed to allocate event data\n");
           free(ev);
           return MSTRO_NOMEM;
         }
-        
-        DEBUG("Event: WITHDRAW for |%s| from %" PRIu64 "\n",
-              ev->withdraw.cdo_name, ev->withdraw.appid);
       }
+      
+      DEBUG("Event: WITHDRAW for |%s| from %" PRIu64 "\n",
+            ev->withdraw.cdo_name, ev->withdraw.appid);
       break;
 
     case MSTRO_POOL_EVENT_APP_BYE:
@@ -2155,9 +2175,11 @@ mstro_subscriptions_finalize(void)
   return status;
 }
 
+/* called on pool client to handle resolver reply */
 mstro_status
 mstro_pool_resolve_reply_bh(const Mstro__Pool__ResolveReply *reply)
 {
+  DEBUG("Resolver reply received\n");
   if(reply==NULL ||reply->query==NULL) {
     return MSTRO_INVMSG;
   }
diff --git a/protocols/mstro_pool.pb-c.c b/protocols/mstro_pool.pb-c.c
index b08e90ef01b64e05a8f8d64d80e1fb9cd576bd34..e241057209aa3eef7544237f28ab8682b6fbc8f7 100644
--- a/protocols/mstro_pool.pb-c.c
+++ b/protocols/mstro_pool.pb-c.c
@@ -4890,7 +4890,7 @@ const ProtobufCMessageDescriptor mstro__pool__unsubscribe__descriptor =
   (ProtobufCMessageInit) mstro__pool__unsubscribe__init,
   NULL,NULL,NULL    /* reserved[123] */
 };
-static const ProtobufCFieldDescriptor mstro__pool__event__field_descriptors[23] =
+static const ProtobufCFieldDescriptor mstro__pool__event__field_descriptors[24] =
 {
   {
     "subscription_handle",
@@ -4940,6 +4940,18 @@ static const ProtobufCFieldDescriptor mstro__pool__event__field_descriptors[23]
     0,             /* flags */
     0,NULL,NULL    /* reserved1,reserved2, etc */
   },
+  {
+    "cdo_name",
+    5,
+    PROTOBUF_C_LABEL_NONE,
+    PROTOBUF_C_TYPE_STRING,
+    0,   /* quantifier_offset */
+    offsetof(Mstro__Pool__Event, cdo_name),
+    NULL,
+    &protobuf_c_empty_string,
+    0,             /* flags */
+    0,NULL,NULL    /* reserved1,reserved2, etc */
+  },
   {
     "declare",
     16,
@@ -5170,36 +5182,37 @@ static const ProtobufCFieldDescriptor mstro__pool__event__field_descriptors[23]
   },
 };
 static const unsigned mstro__pool__event__field_indices_by_name[] = {
-  20,   /* field[20] = bye */
-  4,   /* field[4] = declare */
-  5,   /* field[5] = declare_ack */
-  11,   /* field[11] = demand */
-  13,   /* field[13] = dispose */
-  17,   /* field[17] = join */
+  21,   /* field[21] = bye */
+  4,   /* field[4] = cdo_name */
+  5,   /* field[5] = declare */
+  6,   /* field[6] = declare_ack */
+  12,   /* field[12] = demand */
+  14,   /* field[14] = dispose */
+  18,   /* field[18] = join */
   2,   /* field[2] = kind */
-  19,   /* field[19] = leave */
-  8,   /* field[8] = offer */
+  20,   /* field[20] = leave */
+  9,   /* field[9] = offer */
   3,   /* field[3] = origin_id */
-  9,   /* field[9] = require */
-  12,   /* field[12] = retract */
-  6,   /* field[6] = seal */
-  7,   /* field[7] = seal_group */
+  10,   /* field[10] = require */
+  13,   /* field[13] = retract */
+  7,   /* field[7] = seal */
+  8,   /* field[8] = seal_group */
   1,   /* field[1] = serial */
-  21,   /* field[21] = subscribe */
+  22,   /* field[22] = subscribe */
   0,   /* field[0] = subscription_handle */
-  16,   /* field[16] = transfer_completed */
-  14,   /* field[14] = transport_init */
-  15,   /* field[15] = transport_ticket */
-  22,   /* field[22] = unsubscribe */
-  18,   /* field[18] = welcome */
-  10,   /* field[10] = withdraw */
+  17,   /* field[17] = transfer_completed */
+  15,   /* field[15] = transport_init */
+  16,   /* field[16] = transport_ticket */
+  23,   /* field[23] = unsubscribe */
+  19,   /* field[19] = welcome */
+  11,   /* field[11] = withdraw */
 };
 static const ProtobufCIntRange mstro__pool__event__number_ranges[3 + 1] =
 {
   { 1, 0 },
-  { 16, 4 },
-  { 32, 17 },
-  { 0, 23 }
+  { 16, 5 },
+  { 32, 18 },
+  { 0, 24 }
 };
 const ProtobufCMessageDescriptor mstro__pool__event__descriptor =
 {
@@ -5209,7 +5222,7 @@ const ProtobufCMessageDescriptor mstro__pool__event__descriptor =
   "Mstro__Pool__Event",
   "mstro.pool",
   sizeof(Mstro__Pool__Event),
-  23,
+  24,
   mstro__pool__event__field_descriptors,
   mstro__pool__event__field_indices_by_name,
   3,  mstro__pool__event__number_ranges,
diff --git a/protocols/mstro_pool.pb-c.h b/protocols/mstro_pool.pb-c.h
index 553abf0d71b41bfc28fdab7022608418d7f3d72f..7c17cfeea05b198482f42d01174d0ccfa581c87e 100644
--- a/protocols/mstro_pool.pb-c.h
+++ b/protocols/mstro_pool.pb-c.h
@@ -1143,7 +1143,8 @@ struct  _Mstro__Pool__Event
 {
   ProtobufCMessage base;
   /*
-   ** the (workflow-unique) event id. IDs can be reused over the course of a workflow. 
+   ** the (workflow-unique) event id.
+   *IDs can be reused over the course of a workflow. 
    */
   Mstro__Pool__SubscriptionHandle *subscription_handle;
   uint64_t serial;
@@ -1152,9 +1153,14 @@ struct  _Mstro__Pool__Event
    */
   Mstro__Pool__EventKind kind;
   /*
-   ** the application that caused the event. Can be invalid or pool manager, or a joined component 
+   ** the application that caused the event.
+   *Can be invalid or pool manager, or a joined component 
    */
   Mstro__Pool__Appid *origin_id;
+  /*
+   ** the CDO concerned. Will be unset for non-CDO events 
+   */
+  char *cdo_name;
   Mstro__Pool__Event__PayloadCase payload_case;
   union {
     /*
@@ -1240,7 +1246,7 @@ struct  _Mstro__Pool__Event
 };
 #define MSTRO__POOL__EVENT__INIT \
  { PROTOBUF_C_MESSAGE_INIT (&mstro__pool__event__descriptor) \
-    , NULL, 0, MSTRO__POOL__EVENT_KIND__INVALID_EVENT, NULL, MSTRO__POOL__EVENT__PAYLOAD__NOT_SET, {0} }
+    , NULL, 0, MSTRO__POOL__EVENT_KIND__INVALID_EVENT, NULL, (char *)protobuf_c_empty_string, MSTRO__POOL__EVENT__PAYLOAD__NOT_SET, {0} }
 
 
 struct  _Mstro__Pool__EventAck
diff --git a/protocols/mstro_pool.proto b/protocols/mstro_pool.proto
index d0bc27f0ee865de7d0053b4c055e04f742c0378c..8cc097b041ebc218bb4f6845b31b652a5b750bb8 100644
--- a/protocols/mstro_pool.proto
+++ b/protocols/mstro_pool.proto
@@ -660,17 +660,22 @@ message Unsubscribe {
 
 /** Events that match a subscription */
 message Event {
-  /** the (workflow-unique) event id. IDs can be reused over the course of a workflow. */
+  /** the (workflow-unique) event id.
+      IDs can be reused over the course of a workflow. */
   SubscriptionHandle subscription_handle = 1;
   fixed64 serial                         = 2;
   /** The kind of event */
   EventKind kind                         = 3;
-  /** the application that caused the event. Can be invalid or pool manager, or a joined component */
+  /** the application that caused the event.
+      Can be invalid or pool manager, or a joined component */
   Appid origin_id                        = 4;
+  /** the CDO concerned. Will be unset for non-CDO events */
+  string cdo_name                        = 5;
   /** the data associated with the event
    *
    * In most cases this conincides with the payload sent int the
-   * original message associated with the event, except that VSM messages are unsupported.
+   * original message associated with the event,
+   * except that VSM messages are unsupported.
    */
   oneof payload {
     /** for kind DECLARE */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8beb8d0608c58da39e00a75edea32c8a8f3700df..e9c51d30a780227fa2412cc8763e3f7702d661a2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -76,16 +76,16 @@ TESTS = check_version check_init check_uuid \
 	check_pm_declare.sh \
 	check_pm_interlock.sh \
 	check_subscribe.sh \
-	check_pm_declare_group.sh 
+	check_pm_declare_group.sh
 
 
 XFAIL_TESTS = \
-	check_subscribe_local 
+	check_subscribe_local
 
 # too expensive, actually more of a benchmark:
-#	check_pool_local_multi 
+#	check_pool_local_multi
 # broken for now:
-# 	check_mempool 
+# 	check_mempool
 
 check_PROGRAMS = check_version check_init check_uuid \
 		 coverage check_memlock \
@@ -117,13 +117,13 @@ check_PROGRAMS = check_version check_init check_uuid \
 		 simple_injector \
 		 simple_archiver \
 		 simple_telemetry_listener \
-		 check_events 
+		 check_events
 
 
 if WITH_MIO
 AM_CPPFLAGS += $(TEST_INCLUDES) -I$(top_srcdir)/deps/mio/src -D_REENTRANT -D_GNU_SOURCE -DM0_INTERNAL= -DM0_EXTERN=extern -Wno-attributes
 check_PROGRAMS += check_transport_mio
-TESTS += check_transport_mio 
+TESTS += check_transport_mio
 endif
 
 CLIENT1_OPS="DECLARE cdo1 1025\
@@ -155,9 +155,9 @@ INJECTOR_OPS="\
 
 
 
-simple_interlock_client_1_SOURCES = simple_interlock_client.c 
+simple_interlock_client_1_SOURCES = simple_interlock_client.c
 simple_interlock_client_1_CPPFLAGS = $(AM_CPPFLAGS) -DCLIENT_ARGS=$(CLIENT1_OPS)
-simple_interlock_client_2_SOURCES = simple_interlock_client.c 
+simple_interlock_client_2_SOURCES = simple_interlock_client.c
 simple_interlock_client_2_CPPFLAGS = $(AM_CPPFLAGS) -DCLIENT_ARGS=$(CLIENT2_OPS)
 simple_injector_SOURCES = simple_interlock_client.c
 simple_injector_CPPFLAGS = $(AM_CPPFLAGS) -DCLIENT_ARGS=$(INJECTOR_OPS)
@@ -165,14 +165,16 @@ simple_injector_CPPFLAGS = $(AM_CPPFLAGS) -DCLIENT_ARGS=$(INJECTOR_OPS)
 simple_group_injector_SOURCES = simple_group_client.c
 simple_group_injector_CPPFLAGS = $(AM_CPPFLAGS) -DINJECT_GROUP_MEMBERS=1
 
-#check_mempool 
+#check_mempool
 check_SCRIPTS = run_demo.sh \
 		check_pm_declare.sh \
 		check_pm_declare_group.sh \
 		check_pm_interlock.sh \
 		check_subscribe.sh
 
-EXTRA_DIST = $(check_SCRIPTS) generate-mio-config.sh demo_mvp_d3_2_config.yaml run_demo.sh
+user_defined_test_schemas = benchmark_attributes.yaml test_attributes.yaml
+
+EXTRA_DIST = $(check_SCRIPTS) $(user_defined_test_schemas) generate-mio-config.sh demo_mvp_d3_2_config.yaml run_demo.sh
 
 clean-local: clean-local-check
 .PHONY: clean-local-check
@@ -181,6 +183,11 @@ clean-local-check:
 	-rm -f $(top_builddir)/tests/consumer_CDO\ [0-9]*
 	-rm -f $(top_builddir)/tests/mio-config*.yaml
 	-rm -f $(top_builddir)/tests/m0trace.*
+	-rm -f $(top_builddir)/tests/C-handle\ [0-9]*
+	-rm -f $(top_builddir)/tests/C-name\ [0-9]*
+	-rm -f $(top_builddir)/tests/{Consumer,Producer}-done-CDO
+	-rm -f $(top_builddir)/tests/Test\ group
+	-rm -f $(top_builddir)/tests/cdo[0-9]*
 
 
 .PHONY: all
diff --git a/tests/benchmark_attributes.yaml b/tests/benchmark_attributes.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4d2f49cc540b9a768aeae991c8731af542d61476
--- /dev/null
+++ b/tests/benchmark_attributes.yaml
@@ -0,0 +1,30 @@
+# A user-defined schema. The minimum is name and version
+schema-name: Benchmark Attributes
+schema-version: 0
+
+schema-namespace: ".maestro.benchmark."
+
+# attributes section is optional; if given it needs to have a sequence value
+maestro-attributes:
+
+# Top-level attributes
+
+  - key: "b_1"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "b_2"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  - key: "b_3"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
+
+  
\ No newline at end of file
diff --git a/tests/check_cdo_selectors.c b/tests/check_cdo_selectors.c
index e2ed5bfb75127893f7ce435c2026fef5ba8ddab2..6a55f3c22992cea584e74fc88925cf90f42446a8 100644
--- a/tests/check_cdo_selectors.c
+++ b/tests/check_cdo_selectors.c
@@ -154,4 +154,12 @@ CHEAT_TEST(core_cdo_selector_or_and,
            )
 
 
-           
+CHEAT_TEST(ecmwf_int_comparison,
+           /* https://gitlab.version.fz-juelich.de/maestro/maestro-core/-/issues/16 */
+	   /* parsing of integral CDO selector values */
+           const char *s = "(.maestro.ecmwf.param = 2)";
+	   cheat_assert(MSTRO_OK==mstro_selector_parse(s,&q));
+	   cheat_yield();
+	   cheat_assert(MSTRO_OK==mstro_csq_val_dispose(q));
+	   cheat_yield();
+	  )
diff --git a/tests/check_declaration_seal.c b/tests/check_declaration_seal.c
index b00a215d86f0b1f8a0b50106e23ac64930ad8796..a7266c6c0da0f5a1aa04251d248fb7e5c0b75212 100644
--- a/tests/check_declaration_seal.c
+++ b/tests/check_declaration_seal.c
@@ -39,6 +39,15 @@
 #define __BASE_FILE__ __FILE__
 #endif
 
+#ifdef  TOPSRCDIR
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define SRC_PATH TOSTRING(TOPSRCDIR) "/tests"
+#else
+#define SRC_PATH "."
+#endif
+
 #include "cheat.h"
 
 #include "maestro.h"
@@ -52,7 +61,7 @@ CHEAT_TEST(cdo_declaration_seal_works,
            mstro_cdo cdo=NULL;
            enum mstro_cdo_attr_value_type type;
            const void* val;
-           uint64_t size;
+           int64_t size;
            size = 16000;
            cheat_assert(MSTRO_OK
                         == mstro_cdo_declare(
@@ -88,16 +97,16 @@ CHEAT_TEST(cdo_declaration_seal_works,
            cheat_assert(!(MSTRO_OK
                         == mstro_cdo_attribute_set(
                             cdo, "maestro.core.cdo.scope.local-size",
-                            &size))); // not an absolute path, not a valid relative path
+                            &size, true))); // not an absolute path, not a valid relative path
 
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_set(
                             cdo, ".maestro.core.cdo.scope.local-size",
-                            &size)); 
+                            &size, true));
 
            cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo));
 
-           
+
            cheat_assert(MSTRO_FAIL
                         == mstro_cdo_attribute_set_yaml(
                             cdo, "allocate-now: \"no\""));
@@ -106,26 +115,26 @@ CHEAT_TEST(cdo_declaration_seal_works,
                             cdo, "allocate-now", &type, &val));
            fprintf(stdout, "check: allocate-now: %d (%p)\n", *(const bool*)val, val);
 
-           
+
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_get(
-                            cdo, "persist", &type, &val)); 
+                            cdo, "persist", &type, &val));
            fprintf(stdout, "check: persist: %d (%p)\n", *(const bool*)val, val);
 
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_get(
-                            cdo, "name", &type, &val)); 
+                            cdo, "name", &type, &val));
            fprintf(stdout, "check: name == %s (%p)\n", (const char*)val, val);
 
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_get(
-                            cdo, "scope.local-size", &type, &val)); 
+                            cdo, "scope.local-size", &type, &val));
            fprintf(stderr, "check: scope.local-size == %d (%p)\n",
                    *(const int*)val, val);
 
            cheat_assert(MSTRO_NOENT
                         == mstro_cdo_attribute_get(
-                            cdo, "totally_an_attribute", &type, &val)); 
+                            cdo, "totally_an_attribute", &type, &val));
 
            cheat_assert(MSTRO_OK == mstro_cdo_dispose(cdo));
 
@@ -133,3 +142,53 @@ CHEAT_TEST(cdo_declaration_seal_works,
            )
 
 
+/* put a path to user-defined schema*/
+
+CHEAT_TEST(user_defined_schemas_works,
+
+           setenv("MSTRO_SCHEMA_LIST","test_attributes.yaml", 1);
+           setenv("MSTRO_SCHEMA_PATH",SRC_PATH, 1);
+
+           cheat_assert(MSTRO_OK == mstro_init("Tests","DECLARE",0));
+
+           mstro_cdo cdo;
+           cheat_assert(MSTRO_OK ==mstro_cdo_declare("test cdo", MSTRO_ATTR_DEFAULT, &cdo));
+           cheat_assert(MSTRO_OK ==mstro_cdo_attribute_set(cdo, ".maestro.test.t_1", "test value",true));
+           cheat_assert(MSTRO_OK ==mstro_cdo_declaration_seal(cdo));
+
+           cheat_assert(MSTRO_OK ==mstro_cdo_offer(cdo));
+
+           cheat_assert(MSTRO_OK ==mstro_cdo_withdraw(cdo));
+           cheat_assert(MSTRO_OK ==mstro_cdo_dispose(cdo));
+           cheat_assert(MSTRO_OK == mstro_finalize());
+
+          )
+
+/* put a path to user-defined schema with wrong path and wrong separators*/
+
+CHEAT_TEST(user_defined_schemas_doesnot_exist,
+
+           setenv("MSTRO_SCHEMA_LIST", "user_attributes.yaml", 1);
+           cheat_assert(MSTRO_OK != mstro_init("Tests","DECLARE",0));
+           cheat_assert(MSTRO_FAIL == mstro_finalize());
+
+          )
+
+CHEAT_TEST(user_defined_schemas_paths,
+            setenv("MSTRO_SCHEMA_LIST","benchmark_attributes.yaml;test_attributes.yaml",1);
+            setenv("MSTRO_SCHEMA_PATH",SRC_PATH, 1);
+
+            cheat_assert(MSTRO_OK == mstro_init("Tests","DECLARE",0));
+            mstro_cdo cdo;
+            cheat_assert(MSTRO_OK ==mstro_cdo_declare("test cdo", MSTRO_ATTR_DEFAULT, &cdo));
+            cheat_assert(MSTRO_OK ==mstro_cdo_attribute_set(cdo, ".maestro.test.t_1", "test value", true));
+            cheat_assert(MSTRO_OK ==mstro_cdo_attribute_set(cdo, ".maestro.benchmark.b_1", "bechmark value", true));
+            cheat_assert(MSTRO_OK ==mstro_cdo_declaration_seal(cdo));
+
+            cheat_assert(MSTRO_OK ==mstro_cdo_offer(cdo));
+
+            cheat_assert(MSTRO_OK ==mstro_cdo_withdraw(cdo));
+            cheat_assert(MSTRO_OK ==mstro_cdo_dispose(cdo));
+            cheat_assert(MSTRO_OK == mstro_finalize());
+
+  )
diff --git a/tests/check_layout.c b/tests/check_layout.c
index e3d4f4190fefa72a2b88e4415d8a81ebf60d2033..3570ff2ac0c3ac7957d2ef37b17d780902f822fa 100644
--- a/tests/check_layout.c
+++ b/tests/check_layout.c
@@ -130,32 +130,32 @@ CHEAT_TEST(layout_attribute_works,
   cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo_src));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                    MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                   src_data));
+                                                   src_data, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                                   (void**)&bytes));
+                                                   (void**)&bytes, true));
 
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src, 
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_src));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_src, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_src));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_src, true));
   cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_src));
   cheat_assert(MSTRO_OK == mstro_cdo_offer(cdo_src));
 
   /* Consumer side */
   cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo_dst));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_dst,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_dst,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_dst,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_dst));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_dst, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_dst,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_dst));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_dst, true));
   cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_dst));
     cheat_assert(MSTRO_OK == mstro_cdo_require(cdo_dst));
 
@@ -167,18 +167,18 @@ CHEAT_TEST(layout_attribute_works,
   cheat_assert(MSTRO_OK == mstro_cdo_declare(name_unpooled, MSTRO_ATTR_DEFAULT, &cdo_unpooled));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
                                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                                   (void**)&bytes));
+                                                   (void**)&bytes, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
                                                    MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                   unpooled_data));
+                                                   unpooled_data, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_dst));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_dst, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled,
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_dst));
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_dst, true));
   cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_unpooled));
   /* at this point cdo_unpooled has a mamba array (raw-ptr wrapper) */
   
@@ -200,18 +200,18 @@ CHEAT_TEST(layout_attribute_works,
   cheat_assert(MSTRO_OK == mstro_cdo_declare(name_unpooled, MSTRO_ATTR_DEFAULT, &cdo_unpooled_t));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t,
                                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                                   (void**)&bytes));
+                                                   (void**)&bytes, true));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t,
                                                    MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                   unpooled_data_t));
+                                                   unpooled_data_t, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t, 
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz)); 
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ELEMENT_SIZE, &elsz, true)); 
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t, 
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims)); 
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_NDIMS, &ndims, true)); 
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t, 
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_src)); 
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_DIMS_SIZE, dimsz_src, false)); 
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_unpooled_t, 
-			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_src)); 
+			  MSTRO_ATTR_CORE_CDO_LAYOUT_ORDER, &patt_src, true)); 
   cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_unpooled_t));
 
   cheat_assert(MSTRO_OK == mstro_transform_layout(cdo_unpooled, cdo_unpooled_t, 
diff --git a/tests/check_pool_local_rawptr.c b/tests/check_pool_local_rawptr.c
index 24ff530aefd8ebecff4fe83465599e23c7d69381..55ba01edf81f3bbf7444eab08b5b507890222438 100644
--- a/tests/check_pool_local_rawptr.c
+++ b/tests/check_pool_local_rawptr.c
@@ -77,10 +77,10 @@ CHEAT_TEST(cdo_local_pool_works,
 
            cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(src_cdo,
                                                             MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                            src_data));
+                                                            src_data, false));
            cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(dst_cdo,
                                                             MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                            dst_data));
+                                                            dst_data, false));
            cheat_yield();
            cheat_assert(MSTRO_OK == mstro_cdo_require(dst_cdo));
            cheat_yield();
diff --git a/tests/check_pool_mamba.c b/tests/check_pool_mamba.c
index d543c6d4fa7be15d08316e46f5bc5a5132935dd8..b7f9ec4c0e1ea630cd83fdc8cbe685fa334be572 100644
--- a/tests/check_pool_mamba.c
+++ b/tests/check_pool_mamba.c
@@ -1,6 +1,6 @@
 /* -*- mode:c -*- */
 /** @file
- ** @brief simple mamba use through maestro check 
+ ** @brief simple mamba use through maestro check
  **/
 
 /*
@@ -50,31 +50,31 @@ CHEAT_TEST(cdo_local_pool_mamba_works,
            char name[] = "Test CDO name üÜöÖäÄ";
            mstro_cdo cdo=NULL;
            cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo));
-           uint64_t size = 4096;
+           int64_t size = 4096;
            enum mstro_cdo_attr_value_type type;
            float* buf;
            void *rec;
            buf = malloc(sizeof(float)*size);
            fprintf(stderr, "buf: %p\n", buf);
            rec = NULL;
-           
+
 // TODO declare/set a Mamba array
-           mmbArray* ma_ptr = NULL; 
+           mmbArray* ma_ptr = NULL;
 // TODO associate (cdo, mmb_array_ptr)
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_set(
-                            cdo, ".maestro.core.cdo.raw-ptr", (void*)buf));
+                            cdo, ".maestro.core.cdo.raw-ptr", (void*)buf, false));
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_set(
-                            cdo, ".maestro.core.cdo.scope.local-size", &size));
-           
+                            cdo, ".maestro.core.cdo.scope.local-size", &size, true));
+
            mmbArray *mamba_ptr=NULL;
            /* mamba array not automatically available */
            mstro_status s1 = mstro_cdo_attribute_get(
                cdo, ".maestro.core.cdo.mamba-array", &type, (const void**)&mamba_ptr);
-           
+
            fprintf(stderr,"Mamba array fetch stat 1: %d, val %p\n", s1, mamba_ptr);
-           
+
            cheat_assert(MSTRO_OK==s1);
            cheat_assert(mamba_ptr==NULL);
 
@@ -82,10 +82,10 @@ CHEAT_TEST(cdo_local_pool_mamba_works,
            mmbArray *tmp=NULL;
            cheat_assert(MSTRO_OK==mstro_cdo_access_mamba_array(cdo,&tmp));
            cheat_assert(tmp==NULL);
-           
+
            /* now seal */
            cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo));
-           
+
 
            cheat_assert(MSTRO_OK
                         == mstro_cdo_access_ptr(
@@ -93,7 +93,7 @@ CHEAT_TEST(cdo_local_pool_mamba_works,
            fprintf(stderr, "rec: %p\n", (float*)rec);
            cheat_assert(rec==buf);
 
-           /* afterwards we can also get mamba array as an attribute */                        
+           /* afterwards we can also get mamba array as an attribute */
            cheat_assert(MSTRO_OK
                         == mstro_cdo_attribute_get(
                             cdo, ".maestro.core.cdo.mamba-array", &type, (const void**)&mamba_ptr));
@@ -114,6 +114,3 @@ CHEAT_TEST(cdo_local_pool_mamba_works,
            cheat_assert(MSTRO_OK == mstro_finalize());
            free(buf);
            )
-
-
-
diff --git a/tests/check_transport_gfs.c b/tests/check_transport_gfs.c
index 749a03415b7de2c3596240b5b1c03850fe4875e7..05e651307c9f482b65c9d59ec8f5840ce54595d5 100644
--- a/tests/check_transport_gfs.c
+++ b/tests/check_transport_gfs.c
@@ -87,10 +87,10 @@ CHEAT_TEST(transport_gfs_works,
   cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo_src));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                    MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                   src_data));
+                                                   src_data, false));
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                                   (void**)&bytes));
+                                                   (void**)&bytes, true));
   cheat_yield();
   
   enum mstro_cdo_attr_value_type ttype;
@@ -141,7 +141,7 @@ CHEAT_TEST(transport_gfs_works,
 
   cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_dst,
                                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                                   (void**)&bytes));
+                                                   (void**)&bytes, true));
   cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_dst));
 
   cheat_assert(MSTRO_OK == mstro_transport_gfs_dst_execute(cdo_dst, &ticket)); 
diff --git a/tests/check_transport_mio.c b/tests/check_transport_mio.c
index a2e8a0a187da9cc750ceb891bab4bee0bddbb3f7..fd7116c8034e742b92be20b87a7f03cf0919879d 100644
--- a/tests/check_transport_mio.c
+++ b/tests/check_transport_mio.c
@@ -58,26 +58,26 @@ CHEAT_DECLARE (
     {
       unsigned char x;
       int i;
-      
-      for (i=0,x=0;i<size; i++) 
-        x ^= ((unsigned char*)rawptr)[i]; 
-      
+
+      for (i=0,x=0;i<size; i++)
+        x ^= ((unsigned char*)rawptr)[i];
+
       fprintf(stderr, "Checksum for cdo \"%s\":\t%d\n", name, x);
       return x;
     }
                )
-    
+
     /* this tests MIO transport by using this single thread as both Producer and Consumer  */
     CHEAT_TEST(transport_mio_works,
                size_t data_count = 1031;
-               size_t bytes = data_count*sizeof(double);
+               int64_t bytes = data_count*sizeof(double);
                size_t pad = 0;
                double src_data[data_count];
                for(size_t i=0; i<data_count; i++) {
                  src_data[i]=random();
                }
                unsigned char sender_checksum = transport_checksum("pioneer departure", src_data, data_count);
-               
+
                cheat_assert(MSTRO_OK == mstro_init("Tests","TRANSPORT",0));
                char name[] = "transport_pioneer";
                char filename_dst[] = "./CDOs/__CDO_mio_transport_pioneer";
@@ -85,17 +85,17 @@ CHEAT_DECLARE (
                mstro_cdo cdo_dst=NULL;
                int s = mkdir("./CDOs", S_IRWXU);
                cheat_assert(s==0 || errno==EEXIST);
-               
+
                cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo_src));
                cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                                 MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                                                src_data));
+                                                                src_data, false));
                cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                                 ".maestro.core.cdo.scope.local-size",
-                                                                &bytes));
+                                                                &bytes, true));
                cheat_assert(MSTRO_OK == mstro_cdo_attribute_set(cdo_src,
                                                                 ".maestro.core.cdo.layout.pre-pad",
-                                                                &pad));
+                                                                &pad, true));
 
                const int64_t* tval;
 
@@ -110,13 +110,13 @@ CHEAT_DECLARE (
                                                                 NULL,
                                                                 (const void**)&tval));
                cheat_assert(*tval==bytes);
-               
+
                cheat_assert(MSTRO_OK == mstro_cdo_declare(name, MSTRO_ATTR_DEFAULT, &cdo_dst));
-               
+
                /* Particular case where the offerer is also the src, so let us execute
                 * the src transport before offering, otherwise the raw-ptr won't be
                 * accessible anymore */
-               
+
                /* Have a ticket issued for src */
 		  Mstro__Pool__UUID id = MSTRO__POOL__UUID__INIT;
 		size_t i;
@@ -147,10 +147,10 @@ CHEAT_DECLARE (
 		ticket.ticket_case = MSTRO__POOL__TRANSFER_TICKET__TICKET_MIO;
 		Mstro__Pool__TransferTicketMIO mio = MSTRO__POOL__TRANSFER_TICKET_MIO__INIT;
 		ticket.mio = &mio;
-		struct mstro_cdo_id* semid; 
+		struct mstro_cdo_id* semid;
 		semid = malloc(sizeof(struct mio_obj_id));
 		cheat_assert(semid != NULL);
-		struct mstro_cdo_id* objid; 
+		struct mstro_cdo_id* objid;
 		objid = malloc(sizeof(struct mio_obj_id));
 		cheat_assert (objid != NULL);
 		char* semname = NULL;
@@ -170,53 +170,50 @@ CHEAT_DECLARE (
 */
 		cheat_assert(sizeof(struct mstro_cdo_id) == 2*sizeof(uint64_t));
 		cheat_assert(sizeof(struct mstro_cdo_id) == sizeof(struct mio_obj_id));
-	
+
 		ticket.mio->semid.len = sizeof(struct mstro_cdo_id);
 		ticket.mio->semid.data = (uint8_t*)semid;
 		ticket.mio->objid.len = sizeof(struct mstro_cdo_id);
 		objid->qw[0] = ticket.cdoid->qw0;
 		objid->qw[1] = ticket.cdoid->qw1;
 		ticket.mio->objid.data = (uint8_t*)objid;
-		
+
 		ticket.mio->keep_obj = 0;
 
 	/* No clean issuing, have to duplicate code above from maestro/ofi.c*/
 //		cheat_assert(MSTRO_OK == mstro_transport_ticket_issue(cdo_src, &ticket));
-		
- 		cheat_assert(MSTRO_OK == mstro_transport_mio_src_execute(cdo_src, &ticket)); 
-               
+
+ 		cheat_assert(MSTRO_OK == mstro_transport_mio_src_execute(cdo_src, &ticket));
+
 		cheat_assert(MSTRO_OK == mstro_cdo_offer(cdo_src));
 	       /* ticket is supposed to be sent to dst (4-step protocol), so here let
                 * us just pretend it was, and use ticket directly for dst transport
                 * execute, while it is being implemented */
-               
+
 		cheat_assert(MSTRO_OK == mstro_cdo_declaration_seal(cdo_dst));
-               cheat_assert(MSTRO_OK == mstro_transport_mio_dst_execute(cdo_dst, &ticket)); 
+               cheat_assert(MSTRO_OK == mstro_transport_mio_dst_execute(cdo_dst, &ticket));
 
                cheat_assert(MSTRO_OK == mstro_cdo_require(cdo_dst));
                cheat_assert(MSTRO_OK == mstro_cdo_demand(cdo_dst));
-               
-              
+
+
                double* data;
                size_t len;
                enum mstro_cdo_attr_value_type type;
                const void* val;
-               
+
                cheat_assert(MSTRO_OK == mstro_cdo_access_ptr(cdo_dst, (void**)&data, NULL));
-               cheat_assert(MSTRO_OK == mstro_cdo_attribute_get(cdo_dst, "scope.local-size", &type, &val));                  
+               cheat_assert(MSTRO_OK == mstro_cdo_attribute_get(cdo_dst, "scope.local-size", &type, &val));
                len = *(size_t*)val;
                len /= sizeof(double);
 	       cheat_assert(len > 0 && data != NULL);
 
                unsigned char receiver_checksum = transport_checksum("pioneer arrival", data, len);
                cheat_assert(sender_checksum == receiver_checksum);
-               
+
                cheat_assert(MSTRO_OK == mstro_cdo_dispose(cdo_dst));
                cheat_assert(MSTRO_OK == mstro_cdo_withdraw(cdo_src));
                cheat_assert(MSTRO_OK == mstro_cdo_dispose(cdo_src));
                cheat_assert(MSTRO_OK == mstro_finalize());
-               
-               )
-
-
 
+               )
diff --git a/tests/demo_mvp_d3_2.c b/tests/demo_mvp_d3_2.c
index 95082a69e222054b950c07c59874a1aed003c9c8..0855944df6759324aa0314465b09192a4558d073 100644
--- a/tests/demo_mvp_d3_2.c
+++ b/tests/demo_mvp_d3_2.c
@@ -208,7 +208,7 @@ static const cyaml_config_t config = {
 int
 mvp_config_parse(mvp_config* handle, const char* filename)
 {
-  if (handle == NULL) return MSTRO_INVARG; 
+  if (handle == NULL) return MSTRO_INVARG;
 
   cyaml_err_t err;
   err = cyaml_load_file(filename, &config,
@@ -237,7 +237,7 @@ wait_for_announcement(struct cdo_announcement *announcement)
   }
   s = mstro_cdo_attribute_set(announcement_cdo,
                               MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                              announcement);
+                              announcement, false);
   if(s!=MSTRO_OK) {
     ERR("Failed to set raw-ptr attribute on announcement CDO\n");
     abort();
@@ -255,7 +255,7 @@ wait_for_announcement(struct cdo_announcement *announcement)
     ERR("Failed to require announcement CDO\n");
     abort();
   }
-  
+
   s = mstro_cdo_demand(announcement_cdo);
   if(s!=MSTRO_OK) {
     ERR("Failed to withdraw announcement CDO\n");
@@ -286,7 +286,7 @@ do_announce(struct cdo_announcement *announcement, mstro_cdo *result)
   }
   s = mstro_cdo_attribute_set(announcement_cdo,
                               MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                              announcement);
+                              announcement, false);
 
   if(s!=MSTRO_OK) {
     ERR("Failed to set raw-ptr attribute on  announcement CDO\n");
@@ -450,7 +450,7 @@ archiver_flush_to_disk(const char *name, mmbArray *a)
     abort();
   }
 
-  size_t nt; 
+  size_t nt;
   stat = mmb_tile_iterator_count(it, &nt);
   if(stat != MMB_OK) {
     ERR("Failed to get tile iterator count\n");
@@ -550,10 +550,10 @@ archiver_thread_fun(void *closure)
       }
       s = mstro_cdo_attribute_set(incoming[i],
                                   MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                  incoming_buffers[i]);
+                                  incoming_buffers[i], false);
       s |= mstro_cdo_attribute_set(incoming[i],
                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                   &announcement->cdo_size);
+                                   &announcement->cdo_size, true);
       //      INFO("archiver cdo %d incoming buffer %p\n", i, incoming_buffers[i]);
 
       if(s!=MSTRO_OK) {
@@ -671,10 +671,10 @@ producer_thread_fun(void *closure)
 
       s = mstro_cdo_attribute_set(outgoing[i],
                                   MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                  outgoing_buffers[i]);
+                                  outgoing_buffers[i], false);
       s |= mstro_cdo_attribute_set(outgoing[i],
                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                   &announcement->cdo_size);
+                                   &announcement->cdo_size, true);
 
       if(s!=MSTRO_OK) {
         ERR("Failed to add outgoing buffer to CDO %s\n",
@@ -721,8 +721,8 @@ mvp_checksum(const char* name, void* rawptr, uint64_t size)
   unsigned char x;
   uint64_t i;
 
-  for (i=0,x=0;i<size; i++) 
-    x ^= ((unsigned char*)rawptr)[i]; 
+  for (i=0,x=0;i<size; i++)
+    x ^= ((unsigned char*)rawptr)[i];
 
   INFO("Checksum for cdo \"%s\": %d\n", name, x);
 }
@@ -731,7 +731,7 @@ void
 consumer_flush_to_disk(const char *name, void *a, uint64_t size)
 {
   char file_name [256];
-  sprintf(file_name, "consumer_%s", name); 
+  sprintf(file_name, "consumer_%s", name);
 
   FILE *dst = fopen((const char*)file_name, "w");
   if(dst==NULL) {
@@ -769,7 +769,7 @@ consumer_thread_fun(void *closure)
   mstro_status s;
   size_t my_idx = * (size_t*)closure;
   INFO("Consumer %zu starting\n", my_idx);
-  
+
   struct cdo_announcement *announcement
       = malloc(sizeof(struct cdo_announcement));
   if(announcement==NULL) {
@@ -803,10 +803,10 @@ consumer_thread_fun(void *closure)
       }
       s = mstro_cdo_attribute_set(incoming[i],
                                   MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                                  incoming_buffers[i]);
+                                  incoming_buffers[i], false);
       s |= mstro_cdo_attribute_set(incoming[i],
                                    MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                   &announcement->cdo_size);
+                                   &announcement->cdo_size, true);
       //      INFO("consumer cdo %d incoming buffer %p\n", i, incoming_buffers[i]);
 
       if(s!=MSTRO_OK) {
@@ -826,7 +826,7 @@ consumer_thread_fun(void *closure)
 
   /* send ACK that we're ready */
   mstro_cdo ready_cdo;
-  
+
   INFO("Declaring Consumer %zu ready\n", my_idx);
   declare_consumer_ready(my_idx,&ready_cdo);
 
@@ -853,14 +853,14 @@ consumer_thread_fun(void *closure)
       /* query the size */
       s = mstro_cdo_attribute_get(incoming[i],
                                   MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                                  &type, (const void**)&size); 
+                                  &type, (const void**)&size);
       if(s!=MSTRO_OK) {
         ERR("Failed to extract local size from CDO %s for archiving\n",
             announcement->cdo_names[i]);
         abort();
       }
 
-      /* do some work */ 
+      /* do some work */
       mvp_checksum(announcement->cdo_names[i], rawptr, *size);
 
       /* write-out */
@@ -878,7 +878,7 @@ consumer_thread_fun(void *closure)
   }
 
   /* demand data and process */
-  
+
 
   cdo_sem_post_cleanup(ready_cdo);
   free(announcement);
@@ -893,7 +893,15 @@ consumer_thread_fun(void *closure)
 #define DEFAULT_CDO_SIZE      1024
 #define DEFAULT_CDO_COUNT     42
 
+#ifdef  TOPSRCDIR
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define CONFIG_FILE_PATH TOSTRING(TOPSRCDIR) "/tests/demo_mvp_d3_2_config.yaml"
+#else
 #define CONFIG_FILE_PATH "./demo_mvp_d3_2_config.yaml"
+#endif
+
 
 /*
  * main thread:
@@ -933,7 +941,7 @@ int main(int argc, char **argv)
       WARN("Command line arguments ignored\n");
       /* fallthrough: */
     case 1:
-      
+
       err = mvp_config_parse(&config_handle, CONFIG_FILE_PATH);
       if (err != 0) {
         WARN("Failed to parse a config file. Using default config.\n");
@@ -949,12 +957,12 @@ int main(int argc, char **argv)
         announcement.num_archivers = config_handle->num_archivers;
         announcement.cdo_size = config_handle->cdo_size;
         announcement.num_entries = config_handle->cdo_count;
-      }  
+      }
 
       break;
   }
   assert(announcement.num_entries<=CDO_COUNT_MAX);
-  
+
   pthread_t producers[announcement.num_producers];
   pthread_t consumers[announcement.num_consumers];
   pthread_t archivers[announcement.num_archivers];
@@ -1035,7 +1043,7 @@ int main(int argc, char **argv)
   for(size_t i=0; i<announcement.num_archivers; i++) {
     pthread_join(archivers[i], NULL);
   }
-  
+
   /* withdraw announcement */
   withdraw_announcement(announcement_cdo);
 
@@ -1050,11 +1058,11 @@ int main(int argc, char **argv)
           announcement.num_producers,
           announcement.num_consumers,
           announcement.num_archivers);
-          
+
 
 
 BAILOUT:
   if(config_handle) free(config_handle);
-  
+
   return s;
 }
diff --git a/tests/simple_archiver.c b/tests/simple_archiver.c
index 1bf74c66d8330495b876358186c10691e265f87f..85c24ada8e1355ca775da8c0094511f50de9afa8 100644
--- a/tests/simple_archiver.c
+++ b/tests/simple_archiver.c
@@ -144,24 +144,43 @@ CHEAT_TEST(simple_archiver,
                  mstro_pool_event tmp=e;
                  /* handle all */
                  while(tmp) {
+                   const char *event_name=NULL;
+                   const char *cdo_name=NULL;
+                   event_name = mstro_pool_event_description(tmp->kind);
+                   
                    switch(tmp->kind) {
                      case MSTRO_POOL_EVENT_OFFER:
                        /* FIXME: Immediately post a REQUIRE for it */
-                       fprintf(stderr, "Spotted an OFFER for CDO (`%s`)\n", e->offer.cdo_name);
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
 
                      case MSTRO_POOL_EVENT_DECLARE:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_DISPOSE:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_SEAL:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_DEMAND:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_REQUIRE:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_RETRACT:
+                       cdo_name = tmp->offer.cdo_name;
+                       break;
                      case MSTRO_POOL_EVENT_WITHDRAW:
-                       fprintf(stdout, "CDO event %s\n",
-                               mstro_pool_event_description(tmp->kind));
+                       cdo_name = tmp->offer.cdo_name;
                        break;
                      default:
                        fprintf(stderr, "Unexpected CDO event %d\n", tmp->kind);
                    }
+                   fprintf(stdout, "CDO event %s for CDO |%s|\n",
+                           event_name, cdo_name ? cdo_name : "??");
+
                    tmp=tmp->next;
                  }
                  /* acknowledge all */
diff --git a/tests/simple_interlock_client.c b/tests/simple_interlock_client.c
index c71f47a64a6b97c969e1795fb7a923d7aae7ac03..5b4e4fde9a66581505adde34535c116bd54f8e95 100644
--- a/tests/simple_interlock_client.c
+++ b/tests/simple_interlock_client.c
@@ -194,11 +194,11 @@ CHEAT_DECLARE(
             cheat_assert(MSTRO_OK==mstro_cdo_attribute_set(
                 e->cdo,
                 MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
-                &s->size));
+                &s->size, true));
             cheat_assert(MSTRO_OK==mstro_cdo_attribute_set(
                 e->cdo,
                 MSTRO_ATTR_CORE_CDO_RAW_PTR,
-                e->buf));
+                e->buf, false));
             fprintf(stderr, "declared %s, handle %x\n",
                     e->name, e->cdo);
             break;            
diff --git a/tests/simple_telemetry_listener.c b/tests/simple_telemetry_listener.c
index 3a3b7e70513fb17e5bbb466383a48a9bb4c800ab..7ae3372cd2b012329e24bae70525daa26a95369d 100644
--- a/tests/simple_telemetry_listener.c
+++ b/tests/simple_telemetry_listener.c
@@ -212,7 +212,7 @@ parse_arguments(int argc, char **argv)
   }
 
   if(g_verbose) {
-    fprintf(stderr, "Configuration: %s/%s/%s/%llu/%d\n",
+    fprintf(stderr, "Configuration: %s/%s/%s/%" PRIu64 "/%d\n",
             g_conf_workflow_name, g_conf_component_name,
             g_conf_terminate_after, g_conf_max_wait, g_conf_logdst);
   }
diff --git a/tests/test_attributes.yaml b/tests/test_attributes.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..68008c9aa1da68dd0df39002d20a627537a127f4
--- /dev/null
+++ b/tests/test_attributes.yaml
@@ -0,0 +1,16 @@
+# A user-defined schema. The minimum is name and version
+schema-name: Test Attributes
+schema-version: 0
+
+schema-namespace: ".maestro.test."
+
+# attributes section is optional; if given it needs to have a sequence value
+maestro-attributes:
+
+# Top-level attributes
+
+  - key: "t_1"
+    type: str()
+    required: False
+    default: ""
+    documentation: Value length is the attribute size
diff --git a/transport/gfs.c b/transport/gfs.c
index 7d3090c46c3f17152c5609ec7b6cc64b1b17a36a..c3b12490ba3e9d800654b5902b664bc273ab8f70 100644
--- a/transport/gfs.c
+++ b/transport/gfs.c
@@ -69,7 +69,7 @@ mstro_transport_gfs_src_execute(mstro_cdo src, Mstro__Pool__TransferTicket* tick
 
 	FILE *f = NULL;
 	char* path = ticket->gfs->path;
-	INFO("Path src app will use for transport: %s\n", path);
+	DEBUG("Path src app will use for transport: %s\n", path);
 	if (path == NULL) {
 		ERR("Ticket for GFS transport does not contain a path\n");
 		return MSTRO_INVARG;
@@ -77,21 +77,29 @@ mstro_transport_gfs_src_execute(mstro_cdo src, Mstro__Pool__TransferTicket* tick
 
   	f = fopen(path,"w");
   	if (f == NULL) {
-		ERR("Failed to open %s for GFS transport\n", path);
+		ERR("Failed to open %s for GFS transport (errno: %d -- %s)\n",
+                    path, errno, strerror(errno));
 	    return MSTRO_FAIL;
 	}
 
-	if (fwrite(dl.data, sizeof(char), dl.len/sizeof(char), f) != dl.len) {
-		ERR("Partial write on %s for GFS transport\n", path);
-		return MSTRO_FAIL;
+RETRY_GFS_TRANSPORT_WRITE: ;
+    size_t bytes_written = fwrite(dl.data, sizeof(char), dl.len, f);
+	if (bytes_written != dl.len) {
+      if (errno == EAGAIN)
+	    goto RETRY_GFS_TRANSPORT_WRITE;
+	  ERR("Partial write on %s (buf: %p) for GFS transport (errno: %d -- %s)\n",
+                    path, dl.data, errno, strerror(errno));
+	  return MSTRO_FAIL;
 	}
 
 	if (fclose(f) != 0) {
-		ERR("Failed to close %s for GFS transport\n", path);
+		ERR("Failed to close %s for GFS transport (errno: %d -- %s)\n",
+                    path, errno, strerror(errno));
 		return MSTRO_FAIL;
 	}
 
-	INFO("src app successfully executed transport CDO %s\n", src->name);
+	DEBUG("src app successfully executed transport CDO %s\n",
+             src->name);
 	return MSTRO_OK;
 }
 
@@ -109,10 +117,10 @@ mstro_transport_gfs_dst_execute(mstro_cdo dst,
   INFO("Executing gfs transport dst side for CDO %s\n", dst->name);
   
   mstro_status status;
-  int64_t len = ticket->data_size;
+  size_t len = (size_t)ticket->data_size;
   char* data;
 
-  DEBUG("Incoming CDO, size %" PRIi64 "\n", len);
+  DEBUG("Incoming CDO, size %zu\n", len);
 
   status = mstro_transport_get_dst_buffer(dst, (void*)&data);
   if(status!=MSTRO_OK) {
@@ -125,25 +133,30 @@ mstro_transport_gfs_dst_execute(mstro_cdo dst,
   /* data is the raw-ptr or freshly allocated, CDO attribute is correct */
   FILE *f = NULL;
   char* path = ticket->gfs->path;
-  INFO("Path src app will use for transport: %s\n", path);
   if (path == NULL) {
     ERR("Ticket for GFS transport does not contain a path\n");
     return MSTRO_INVARG;
   }
+  DEBUG("Path dst app will use for transport: %s\n", path);
   f = fopen(path,"rb");
   if (f == NULL) {
     ERR("Failed to open %s for GFS transport: %d (%s)\n",
         path, errno, strerror(errno));
     return MSTRO_FAIL;
   }
-
-  if (fread(data, sizeof(char), len, f) != (size_t)len) {
-    ERR("Partial read on %s for GFS transport\n", path);
+RETRY_GFS_TRANSPORT_READ: ;
+  size_t bytes_read = fread(data, sizeof(char), len, f);
+  if (bytes_read != (size_t)len) {
+    if (errno == EAGAIN)
+	  goto RETRY_GFS_TRANSPORT_READ;
+    ERR("Partial read (%d out of %d bytes) on %s for GFS transport (errno: %d -- %s)\n",
+        bytes_read, len, path, errno, strerror(errno));
     return MSTRO_FAIL;
   }
 
   if (fclose(f) != 0) {
-    ERR("Failed to close %s for GFS transport\n", path);
+    ERR("Failed to close %s for GFS transport (errno: %d -- %s)\n",
+        path, errno, strerror(errno));
     return MSTRO_FAIL;
   }
   
@@ -170,7 +183,7 @@ mstro_transport_gfs_dst_execute(mstro_cdo dst,
   /*   abort(); */
   /* }     */
   
-  INFO("dst app successfully executed transport CDO %s\n", dst->name);
+  DEBUG("dst app successfully executed transport CDO %s\n", dst->name);
 
   if (! (ticket->gfs->keep_file)) {
     int s = unlink(ticket->gfs->path);
@@ -178,7 +191,7 @@ mstro_transport_gfs_dst_execute(mstro_cdo dst,
       WARN("Couldn't unlink file used for gfs transfer (%s): %d (%s)\n",
            ticket->gfs->path, s, strerror(s));
     } else {
-      INFO("Removed the transfer file from GFS\n");
+      DEBUG("Removed the transfer file\n");
     }
   }
 
diff --git a/transport/transport.c b/transport/transport.c
index 230260565a21e2c389e83f271cb4e4a8e69cd571..0690af7edff5ead202d06172de6cb67b7c0c930f 100644
--- a/transport/transport.c
+++ b/transport/transport.c
@@ -43,6 +43,8 @@
 #include "maestro/i_cdo.h"
 #include <inttypes.h>
 
+#include <sys/stat.h>
+
 #include "transport/transport_gfs.h"
 
 #ifdef HAVE_MIO
@@ -170,11 +172,63 @@ mstro_transport__src_datalen_get(mstro_cdo src,
   return MSTRO_OK;
 }
 
+/** find next separator in PATH. Return the start of that segment */
+static inline
+const char * next_sep (const char *path)
+{
+  while (*path)
+    if (*path == '/' || *path == '\\')
+      return path;
+    else
+      path++;
+  return NULL;
+}
+
+/** ensure all directories in DIRNAME exist. If not, try creating them  */
+static inline
+mstro_status
+mkdirhier(char *dirname)
+{
+  char buf[PATH_MAX];
+  const char *prev = dirname;
+  const char *next;
+  struct stat sb;
+  
+  while(NULL!=(next = next_sep(prev))) {
+    strncpy(buf, dirname, next - dirname) ;
+    buf[next - dirname] = '\0';
+    if(stat(buf,&sb)) {
+      mkdir(buf, 0777); /* umask taken into account by system */
+      /* we ignore errors, as we'll see an error on the last part */
+    }
+    prev=next+1;
+  }
+  /* handle last part */
+  if(stat(dirname, &sb)) {
+    int s = mkdir(dirname, 0777);
+    if(s!=0) {
+      ERR("Failed to create GFS transport directory %s: %d (%s)\n",
+          dirname, errno, strerror(errno));
+      return MSTRO_FAIL;
+    }
+  }
+  return MSTRO_OK;
+}
+
 mstro_status
 mstro_transport_init()
 {
   srand(time(NULL)); /* We'll need that to generate random IDs */
+
+  /* ensure GFS dir is set up */
   
+  mstro_status s = mkdirhier(g_mstro_transport_gfs_dir);
+  if(s!=MSTRO_OK) {
+    ERR("Failed to initialize GFS transport. Check your setting of %s\n",
+        MSTRO_ENV_TRANSPORT_GFS_DIR);
+    return s;
+  }
+
 #ifdef HAVE_MIO
   /* MIO */
   if(g_mio_available) {
@@ -317,9 +371,32 @@ mstro_transport_execute(
   if (cdo->state & MSTRO_CDO_STATE_OFFERED) {
     /* we are called on the source side of transport */
     {
-      /* FIXME: should check this for robustness: */
-      DEBUG("Not checking that SRC cdo has expected size (%" PRIi64 ")\n",
-            ticket->data_size);
+	  /* let's check ticket size matches local CDO size */
+       enum mstro_cdo_attr_value_type type;
+       const void *sizep=NULL;
+       int64_t src_size;
+       mstro_status s = mstro_attribute_dict_get(cdo->attributes,
+                                            MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE,
+                                            &type, &sizep, NULL, false);
+       if(s==MSTRO_OK) {
+         src_size=*(int64_t*)sizep;
+       } else if(s==MSTRO_NOENT && type==MSTRO_CDO_ATTR_VALUE_INVALID) {
+	     ERR("Attribute not found or value invalid\n");
+         src_size=-1;
+       } else {
+         ERR("Failed to look up %s (%d: %s)\n",
+             MSTRO_ATTR_CORE_CDO_SCOPE_LOCAL_SIZE, s, mstro_status_description(s));
+         return MSTRO_FAIL;
+       }
+
+       if (ticket->data_size == 0) {
+         DEBUG("0-transport\n");
+	   }
+       else if (src_size != ticket->data_size) {
+         ERR("SRC cdo has a size (%" PRIi64 ") that doesn't match ticket-specified size (%" PRIi64 ")\n",
+            src_size, ticket->data_size);
+			return MSTRO_FAIL;
+       }
     }
     mstro_status (*f)(mstro_cdo src, Mstro__Pool__TransferTicket *t)
         = g_transport_registry[ticket->method].src_func;