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/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..14faf1158dcd005478ecb50df51853ce54e6854d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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/attributes/maestro-schema.c b/attributes/maestro-schema.c
index eb7db7b9ffd700ff7178db840caba1575ab50269..d08fe67b7095cf03bfb267215d1e911b40864504 100644
--- a/attributes/maestro-schema.c
+++ b/attributes/maestro-schema.c
@@ -2516,7 +2516,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 +2710,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..d0243cd30d83fc04733da09bc838fab245f6490c 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],
@@ -536,6 +544,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/include/maestro/attributes.h b/include/maestro/attributes.h
index ecd390a637ccf41a8f81b082e0304f85cc8a6d21..0788c5d2b6f124f4d313a6694c2971dda7dcc31f 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
diff --git a/include/maestro/env.h b/include/maestro/env.h
index a6714bec111bbf7300a146b46cb7ade958808fe5..8129cba1f79033118804603a93a4708ec8065e14 100644
--- a/include/maestro/env.h
+++ b/include/maestro/env.h
@@ -206,7 +206,18 @@
 
 
 /**
- ** @brief Path to the MIO transport config file
+ ** @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
  **
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..62dfea09f968a521ffee23ea8c182b05a1cc48bc 100644
--- a/include/maestro/i_globals.h
+++ b/include/maestro/i_globals.h
@@ -111,6 +111,8 @@ 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;
@@ -159,4 +161,12 @@ extern union mstro_component_descriptor g_component_descriptor;
          | (MSTRO_POOL_PROTOCOL_VERSION_MINOR << 8)    \
          | (MSTRO_POOL_PROTOCOL_VERSION_PATCH << 0)))
 
+
+
+/** 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..8a3250ad128fbd37cab2f289edaf82a01de5ae32 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";
 
diff --git a/maestro/cdo.c b/maestro/cdo.c
index 37f25ccaacc2b9e92ac52866c76956aded77c3ec..15c2d4990328c3a8ac3b9f27b54c24ba746d8971 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);
@@ -639,6 +643,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 */
@@ -1592,29 +1637,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;
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 9d3d5477bf776aa40bdee1d5ad4932e3e64fb96d..accbf882ce442cf87ea08552cf5c417b5994b4c0 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
@@ -307,7 +331,53 @@ 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());
 
-
+  /* 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);
+    }
+  }
+  
+  /*Reading and integrating attribute schemas*/
   status = mstro_core_init__setup_schemata();
   if(status!=MSTRO_OK) {
     goto BAILOUT;
@@ -328,6 +398,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/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/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..87a15d014032296c3db66ad2f5bfbe5eb0c8681b 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)
@@ -398,7 +427,12 @@ 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;
+      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 = 0; // Arbitrarily rm the transport file on dst
       break;
     case MSTRO__POOL__TRANSFER_TICKET__TICKET_MIO:
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/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/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_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/transport/gfs.c b/transport/gfs.c
index 7d3090c46c3f17152c5609ec7b6cc64b1b17a36a..a8844e5d345b9fc50130e45281a3c97888f74e87 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,25 @@ 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);
+		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;
 }
 
@@ -125,11 +129,11 @@ 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",
@@ -138,12 +142,14 @@ mstro_transport_gfs_dst_execute(mstro_cdo dst,
   }
 
   if (fread(data, sizeof(char), len, f) != (size_t)len) {
-    ERR("Partial read on %s for GFS transport\n", path);
+    ERR("Partial read on %s for GFS transport (errno: %d -- %s)\n",
+        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 +176,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 +184,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..4c3f6d73c038e50792a953809daa591e908582ca 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) {