resolve merge conflicts
diff --git a/containers/onboarding_synchronizer/Dockerfile b/containers/onboarding_synchronizer/Dockerfile
new file mode 100644
index 0000000..967e234
--- /dev/null
+++ b/containers/onboarding_synchronizer/Dockerfile
@@ -0,0 +1,40 @@
+FROM       xosproject/xos-synchronizer-openstack
+
+# Install docker-in-docker (dind). See https://hub.docker.com/_/docker/. The docker git repo
+# currently only has 1.10 and 1.11, but it's possible to get the dockerfiles for earlier
+# versions by using:
+#        docker pull centurylink/dockerfile-from-image
+#        alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm centurylink/dockerfile-from-image"
+#        dgimage <name of image>
+
+# This container must be started in privileged mode. 
+
+RUN apt-get install -y curl
+# iptables 
+ENV DOCKER_BUCKET=get.docker.com
+ENV DOCKER_VERSION=1.8.3
+ENV DOCKER_SHA256=f024bc65c45a3778cf07213d26016075e8172de8f6e4b5702bedde06c241650f
+RUN curl -fSL "https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-$DOCKER_VERSION" -o /usr/local/bin/docker && echo "${DOCKER_SHA256} /usr/local/bin/docker" | sha256sum -c - && chmod +x /usr/local/bin/docker
+
+# XXX uncomment the following 6 lines to run docker-in-docker
+#     comment them out if using the docker socket in a volume instead
+#ENV DIND_COMMIT=3b5fac462d21ca164b3778647420016315289034
+#RUN wget "https://raw.githubusercontent.com/docker/docker/${DIND_COMMIT}/hack/dind" -O /usr/local/bin/dind && chmod +x /usr/local/bin/dind
+#COPY start-dockerd.sh /usr/local/bin/
+#VOLUME /var/lib/docker
+#EXPOSE 2375
+#ENTRYPOINT ["start-dockerd.sh"]
+
+# Instead of using docker-in-docker, we can just attach ourselves
+# to the docker socket via a volume in the docker-compose:
+#     - /var/run/docker.sock:/var/run/docker.sock
+# This is more convenient, allowing us to build directly into our
+# parent's docker build system, making the images available for
+# instantiation on the parent. 
+
+# Now install docker-compose
+
+RUN bash -c "curl -L https://github.com/docker/compose/releases/download/1.5.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose"
+RUN chmod +x /usr/local/bin/docker-compose
+
+CMD update-ca-certificates && /usr/bin/supervisord -c /etc/supervisor/conf.d/synchronizer.conf
diff --git a/containers/onboarding_synchronizer/Makefile b/containers/onboarding_synchronizer/Makefile
new file mode 100644
index 0000000..6532196
--- /dev/null
+++ b/containers/onboarding_synchronizer/Makefile
@@ -0,0 +1,15 @@
+IMAGE_NAME:=xosproject/xos-synchronizer-onboarding
+CONTAINER_NAME:=xos-synchronizer
+NO_DOCKER_CACHE?=false
+
+.PHONY: build
+build: ; sudo docker build --no-cache=${NO_DOCKER_CACHE} --rm -t ${IMAGE_NAME} .
+
+.PHONY: run
+run: ; sudo docker run -d --name ${CONTAINER_NAME} -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro ${IMAGE_NAME}
+
+.PHONY: stop
+stop: ; sudo docker stop ${CONTAINER_NAME}
+
+.PHONY: rm
+rm: ; sudo docker rm ${CONTAINER_NAME}
diff --git a/containers/onboarding_synchronizer/start-dockerd.sh b/containers/onboarding_synchronizer/start-dockerd.sh
new file mode 100755
index 0000000..bb97341
--- /dev/null
+++ b/containers/onboarding_synchronizer/start-dockerd.sh
@@ -0,0 +1,3 @@
+#! /bin/bash
+
+docker daemon --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2375 --storage-driver=aufs
diff --git a/xos/api/utility/onboarding.py b/xos/api/utility/onboarding.py
new file mode 100644
index 0000000..dd66d6d
--- /dev/null
+++ b/xos/api/utility/onboarding.py
@@ -0,0 +1,97 @@
+import json
+from django.http import HttpResponse
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from core.models import *
+from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
+from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+
+class OnboardingViewSet(XOSViewSet):
+    base_name = "onboarding"
+    method_name = "onboarding"
+    method_kind = "viewset"
+
+    @classmethod
+    def get_urlpatterns(self, api_path="^"):
+        patterns = [] #super(CordSubscriberViewSet, self).get_urlpatterns(api_path=api_path)
+
+        patterns.append( self.list_url("xos/ready/$", {"get": "get_xos_ready"}, "xos_ready") )
+
+        patterns.append( self.list_url("summary/$", {"get": "get_summary"}, "summary") )
+
+        patterns.append( self.list_url("services/$", {"get": "get_service_list"}, "service_list") )
+        patterns.append( self.list_url("services/(?P<service>[a-zA-Z0-9\-_]+)/ready/$", {"get": "get_service_ready"}, "service_ready") )
+
+
+        return patterns
+
+    def is_ready(self, obj):
+        return (obj.enacted is not None) and (obj.updated is not None) and (obj.enacted>=obj.updated) and (obj.backend_status.startswith("1"))
+
+    def get_xos_ready(self, request):
+        xos = XOS.objects.all()
+        if not xos:
+            return Response(false)
+
+        xos=xos[0]
+
+        result = (xos.enacted is not None) and (xos.updated is not None) and (xos.enacted>=xos.updated) and (xos.backend_status.startswith("1"))
+        return HttpResponse( json.dumps(result), content_type="application/javascript" )
+
+    def get_summary(self, request):
+        result = []
+
+        xos = XOS.objects.all()
+        if not xos:
+            result.append( ("XOS", false) )
+        else:
+            xos=xos[0]
+
+            result.append( ("XOS", self.is_ready(xos)) )
+
+            for sc in xos.service_controllers.all():
+                result.append( (sc.name, self.is_ready(sc)) )
+
+        result = "\n".join( ["%s: %s" % (x[0], x[1]) for x in result] )
+        if result:
+            result = result + "\n"
+
+        return HttpResponse( result, content_type="text/ascii" )
+
+    def get_service_list(self, request):
+        xos = XOS.objects.all()
+        if not xos:
+            return Response([])
+
+        xos=xos[0]
+
+        result = []
+        for sc in xos.service_controllers.all():
+            result.append(sc.name)
+
+        return HttpResponse( json.dumps(result), content_type="application/javascript")
+
+    def get_service_ready(self, request, service):
+        xos = XOS.objects.all()
+        if not xos:
+            return Response([])
+
+        xos=xos[0]
+
+        sc=xos.service_controllers.filter(name=service)
+        if not sc:
+            return HttpResponse("Not Found", status_code=404)
+
+        sc=sc[0]
+        result = self.is_ready(sc)
+
+        return HttpResponse( json.dumps(result), content_type="application/javascript")
+
+
+
+
+
diff --git a/xos/configurations/common/fixtures.yaml b/xos/configurations/common/fixtures.yaml
index e28b03c..6b3234e 100644
--- a/xos/configurations/common/fixtures.yaml
+++ b/xos/configurations/common/fixtures.yaml
@@ -8,6 +8,10 @@
 topology_template:
   node_templates:
 
+    xos:
+      type: tosca.nodes.XOS
+
+
 # -----------------------------------------------------------------------------
 # Network Parameter Types
 # -----------------------------------------------------------------------------
diff --git a/xos/configurations/common/wait_for_onboarding_ready.sh b/xos/configurations/common/wait_for_onboarding_ready.sh
new file mode 100755
index 0000000..9606dbb
--- /dev/null
+++ b/xos/configurations/common/wait_for_onboarding_ready.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+
+display_usage() { 
+    echo -e "\nUsage:\n$0 [xos-listen-port] [name] \n" 
+} 
+
+if [  $# -lt 2 ] 
+then 
+    display_usage
+    exit 1
+fi 
+
+echo "Waiting for $2 to be onboarded"
+while [[ 1 ]]; do
+    STATUS=`curl 0.0.0.0:$1/api/utility/onboarding/$2/ready/ 2> /dev/null`
+    if [[ "$STATUS" == "true" ]]; then
+        echo "$2 is onboarded"
+        exit 0
+    fi
+    sleep 1
+#    RUNNING_CONTAINER=`sudo docker ps|grep "xos"|awk '{print $$NF}'`
+#    if [[ $RUNNING_CONTAINER == "" ]]; then
+#        echo Container may have failed. check with \"make showlogs\'
+#        exit 1
+#    fi
+done
+
diff --git a/xos/configurations/common/wait_for_xos_file.sh b/xos/configurations/common/wait_for_xos_file.sh
new file mode 100755
index 0000000..1214dc4
--- /dev/null
+++ b/xos/configurations/common/wait_for_xos_file.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+
+display_usage() { 
+    echo -e "\nUsage:\n$0 [fn] \n" 
+} 
+
+if [  $# -lt 1 ] 
+then 
+    display_usage
+    exit 1
+fi 
+
+echo "Waiting for XOS to create file $1"
+
+until find $1 &> /dev/null
+do
+    sleep 1
+    RUNNING_CONTAINER=`sudo docker ps|grep "xos"|awk '{print $$NF}'`
+    if [[ $RUNNING_CONTAINER == "" ]]; then
+        echo Container may have failed. check with \"make showlogs\'
+        exit 1
+    fi
+done
+echo "XOS is ready"
diff --git a/xos/configurations/common/wait_for_xos_port.sh b/xos/configurations/common/wait_for_xos_port.sh
index 3ed5833..9c9e041 100755
--- a/xos/configurations/common/wait_for_xos_port.sh
+++ b/xos/configurations/common/wait_for_xos_port.sh
@@ -10,7 +10,7 @@
     exit 1
 fi 
 
-echo "Waiting for XOS to come up"
+echo "Waiting for XOS to start listening on port $1"
 until curl 0.0.0.0:$1 &> /dev/null
 do
     sleep 1
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
index fb5b26a..f0b3c3c 100644
--- a/xos/configurations/cord-pod/Makefile
+++ b/xos/configurations/cord-pod/Makefile
@@ -1,24 +1,42 @@
+CONFIG_DIR:=$(shell pwd)
+DOCKER_COMPOSE_YML=./onboarding-docker-compose/docker-compose.yml
+BOOTSTRAP_YML=./docker-compose-bootstrap.yml
+DOCKER_PROJECT=cordpod
+
 .PHONY: xos
-xos: up bootstrap
+xos: prereqs bootstrap onboarding podconfig
 
-up:
-	sudo docker-compose up -d
-	../common/wait_for_xos_port.sh 80
+prereqs:
+	sudo make -f ../common/Makefile.prereqs
 
-bootstrap: nodes.yaml images.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/fixtures.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/mydeployment.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/setup.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/images.yaml
+bootstrap:
+	echo "[BOOTSTRAP]"
+	sudo rm -f onboarding-docker-compose/docker-compose.yml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f docker-compose-bootstrap.yml up -d
+	bash ../common/wait_for_xos_port.sh 81
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(BOOTSTRAP_YML) run -e CONFIG_DIR=$(CONFIG_DIR) xos_bootstrap_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/cord-pod/xos.yaml
+
+onboarding:
+	echo "[ONBOARDING]"
+	# on-board any services here
+	bash ../common/wait_for_onboarding_ready.sh 81 xos
+	bash ../common/wait_for_xos_port.sh 80
+
+podconfig: nodes.yaml images.yaml
+	echo "[PODCONFIG]"
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/common/fixtures.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/common/mydeployment.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/setup.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/images.yaml
 
 vtn: vtn-external.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/vtn-external.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/vtn-external.yaml
 
 cord: vsg_custom_images
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/mgmt-net.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-volt-devices.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/mgmt-net.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-volt-devices.yaml
 
 exampleservice:
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/pod-exampleservice.yaml
@@ -42,10 +60,12 @@
 	export SETUPDIR=.; bash ./make-vtn-networkconfig-json.sh
 
 stop:
-	sudo MYIP=$(MYIP) docker-compose stop
+	test ! -s $(DOCKER_COMPOSE_YML) || sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) stop
+	sudo docker-compose -f $(BOOTSTRAP_YML) stop
 
 rm:
-	sudo MYIP=$(MYIP) docker-compose rm
+	test ! -s $(DOCKER_COMPOSE_YML) || sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) rm
+	sudo docker-compose -f $(BOOTSTRAP_YML) rm
 
 showlogs:
 	sudo MYIP=$(MYIP) docker-compose logs
@@ -80,4 +100,4 @@
 	done
 	cd ../../../containers/xos; make devel
 	cd ../../../containers/synchronizer; make
-
+	cd ../../../containers/onboarding_synchronizer; make
diff --git a/xos/configurations/cord-pod/ceilometer.yaml b/xos/configurations/cord-pod/ceilometer.yaml
index d07f2e9..07b163e 100644
--- a/xos/configurations/cord-pod/ceilometer.yaml
+++ b/xos/configurations/cord-pod/ceilometer.yaml
@@ -124,6 +124,7 @@
           kind: ceilometer
           ceilometer_pub_sub_url: http://10.11.10.1:4455/
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          private_key_fn: /opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key
       artifacts:
           pubkey: /opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key
 
diff --git a/xos/configurations/cord-pod/docker-compose.yml b/xos/configurations/cord-pod/docker-compose-bootstrap.yml
similarity index 81%
rename from xos/configurations/cord-pod/docker-compose.yml
rename to xos/configurations/cord-pod/docker-compose-bootstrap.yml
index fa8660a..d481767 100644
--- a/xos/configurations/cord-pod/docker-compose.yml
+++ b/xos/configurations/cord-pod/docker-compose-bootstrap.yml
@@ -3,6 +3,24 @@
     expose:
         - "5432"
 
+xos_synchronizer_onboarding:
+    image: xosproject/xos-synchronizer-onboarding
+    command: bash -c "cd /opt/xos/synchronizers/onboarding; ./run.sh"
+    #command: sleep 86400
+    labels:
+        org.xosproject.kind: synchronizer
+        org.xosproject.target: onboarding
+    links:
+        - xos_db
+    volumes:
+        - /var/run/docker.sock:/var/run/docker.sock
+        - ./key_import:/opt/xos/key_import:ro
+        - ./onboarding-docker-compose:/opt/xos/synchronizers/onboarding/docker-compose
+    log_driver: "json-file"
+    log_opt:
+            max-size: "100k"
+            max-file: "5"
+
 xos_synchronizer_openstack:
     command: bash -c "sleep 120; python /opt/xos/synchronizers/openstack/xos-synchronizer.py"
     image: xosproject/xos-synchronizer-openstack
@@ -101,21 +119,21 @@
             max-size: "100k"
             max-file: "5"
 
-xos:
-    command: python /opt/xos/manage.py runserver 0.0.0.0:80 --insecure --makemigrations
+xos_bootstrap_ui:
+    command: python /opt/xos/manage.py runserver 0.0.0.0:81 --insecure --makemigrations
     image: xosproject/xos
     links:
         - xos_db
     ports:
-        - "80:80"
+        - "81:81"
     volumes:
-        - .:/root/setup:ro
+#        - .:/root/setup:ro
         - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
         - ./xos_cord_config:/opt/xos/xos_configuration/xos_cord_config:ro
         - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
-        - ./id_rsa.pub:/opt/xos/synchronizers/onos/onos_key.pub:ro
-        - ./id_rsa.pub:/opt/xos/synchronizers/vcpe/vcpe_public_key:ro
-        - ./id_rsa.pub:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key:ro
+#        - ./id_rsa.pub:/opt/xos/synchronizers/onos/onos_key.pub:ro
+#        - ./id_rsa.pub:/opt/xos/synchronizers/vcpe/vcpe_public_key:ro
+#        - ./id_rsa.pub:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key:ro
     log_driver: "json-file"
     log_opt:
             max-size: "100k"
diff --git a/xos/configurations/cord-pod/pod-exampleservice.yaml b/xos/configurations/cord-pod/pod-exampleservice.yaml
index 677e889..0182a59 100644
--- a/xos/configurations/cord-pod/pod-exampleservice.yaml
+++ b/xos/configurations/cord-pod/pod-exampleservice.yaml
@@ -79,10 +79,10 @@
           view_url: /admin/exampleservice/exampleservice/$id$/
           kind: exampleservice
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
-          private_key_fn: /opt/xos/synchronizers/exampleservice/exampleservice_private_key
+          private_key_fn: /opt/xos/services/exampleservice/keys/exampleservice_rsa
           service_message: hello
       artifacts:
-          pubkey: /opt/xos/synchronizers/exampleservice/exampleservice_public_key
+          pubkey: /opt/xos/services/exampleservice/keys/exampleservice_rsa.pub
 
     tenant#exampletenant1:
         type: tosca.nodes.ExampleTenant
diff --git a/xos/configurations/cord-pod/xos.yaml b/xos/configurations/cord-pod/xos.yaml
new file mode 100644
index 0000000..06b5eb5
--- /dev/null
+++ b/xos/configurations/cord-pod/xos.yaml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    xos:
+      type: tosca.nodes.XOS
+      properties:
+        ui_port: 80
+        bootstrap_ui_port: 81
+        docker_project_name: cordpod
+
+    /opt/xos/xos_configuration/xos_common_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, ../common/xos_common_config, ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/xos_configuration/xos_cord_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, xos_cord_config, ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/xos_configuration/xos_vtn_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, ../vtn/files/xos_vtn_config, ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+              node: xos
+              relationship: tosca.relationships.UsedByXOS
+
+    /root/setup:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, ., ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/synchronizers/onos/onos_key.pub:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, id_rsa.pub, ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/synchronizers/vcpe/vcpe_public_key:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, id_rsa.pub, ENV_VAR ] }                                                      
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, id_rsa.pub, ENV_VAR ] }                                                      
+          read_only: true
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 9b1ec86..46f39bf 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -1,24 +1,41 @@
 MYIP:=$(shell hostname -i)
+CONFIG_DIR:=$(shell pwd)
+DOCKER_COMPOSE_YML=./onboarding-docker-compose/docker-compose.yml
+BOOTSTRAP_YML=./docker-compose-bootstrap.yml
+DOCKER_PROJECT=frontend
 
-frontend:
+frontend: prereqs bootstrap
+	bash ../common/wait_for_xos_port.sh 9999
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/common/fixtures.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/common/mydeployment.yaml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/sample.yaml
+
+prereqs:
 	sudo make -f ../common/Makefile.prereqs
-	sudo docker-compose up -d
-	bash ../common/wait_for_xos.sh
-	sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/fixtures.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/mydeployment.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/sample.yaml
+
+bootstrap:
+	sudo rm -f onboarding-docker-compose/docker-compose.yml
+	sudo rm -f docker-compose.yml
+	sudo docker-compose -p $(DOCKER_PROJECT) -f docker-compose-bootstrap.yml up -d
+	bash ../common/wait_for_xos_port.sh 9998
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(BOOTSTRAP_YML) run -e CONFIG_DIR=$(CONFIG_DIR) xos_bootstrap_ui python /opt/xos/tosca/run.py none /opt/xos/configurations/frontend/xos.yaml
 
 containers:
 	cd ../../../containers/xos; make devel
+	cd ../../../containers/synchronizer; make
+	cd ../../../containers/onboarding_synchronizer; make
+	#cd ../../../containers/xos; make devel
 
 stop:
-	sudo docker-compose stop
+	test ! -s $(DOCKER_COMPOSE_YML) || sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) stop
+	sudo docker-compose -f $(BOOTSTRAP_YML) stop
 
 showlogs:
 	sudo docker-compose logs
 
 rm: stop
-	sudo docker-compose rm
+	test ! -s $(DOCKER_COMPOSE_YML) || sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) rm
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(BOOTSTRAP_YML) rm
 
 ps:
 	sudo docker-compose ps
@@ -50,3 +67,11 @@
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/mocks/mcord.yaml
 	sudo docker exec frontend_xos_1 cp /opt/xos/configurations/mcord/xos_mcord_config /opt/xos/xos_configuration/
 	sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
+
+exampleservice:
+	mkdir -p key_import
+	# fake keys are fine
+	sudo bash -c "echo somekey > key_import/exampleservice_rsa"
+	sudo bash -c "echo somekey > key_import/exampleservice_rsa.pub"
+	sudo docker-compose -p $(DOCKER_PROJECT) -f $(DOCKER_COMPOSE_YML) run xos_ui python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/onboard/exampleservice/exampleservice-onboard.yaml
+	bash ../common/wait_for_onboarding_ready.sh 9998 xos
diff --git a/xos/configurations/frontend/docker-compose-bootstrap.yml b/xos/configurations/frontend/docker-compose-bootstrap.yml
new file mode 100644
index 0000000..756399c
--- /dev/null
+++ b/xos/configurations/frontend/docker-compose-bootstrap.yml
@@ -0,0 +1,34 @@
+xos_db:
+    image: xosproject/xos-postgres
+    expose:
+        - "5432"
+
+xos_bootstrap_ui:
+    image: xosproject/xos
+    command: python /opt/xos/manage.py runserver 0.0.0.0:9998 --insecure --makemigrations
+    ports:
+        - "9998:9998"
+    links:
+        - xos_db
+    volumes:
+      - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config
+      - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
+
+xos_synchronizer_onboarding:
+    image: xosproject/xos-synchronizer-onboarding
+    #command: bash -c "sleep 120; python /opt/xos/synchronizers/onboarding/onboarding-synchronizer.py -C /root/setup/files/onboarding_synchronizer_config"
+    command: sleep 86400
+    labels:
+        org.xosproject.kind: synchronizer
+        org.xosproject.target: onboarding
+    links:
+        - xos_db
+    volumes:
+#        - .:/root/setup:ro
+        - /var/run/docker.sock:/var/run/docker.sock
+        - ./key_import:/opt/xos/key_import:ro
+        - ./onboarding-docker-compose:/opt/xos/synchronizers/onboarding/docker-compose
+    log_driver: "json-file"
+    log_opt:
+            max-size: "100k"
+            max-file: "5"
diff --git a/xos/configurations/frontend/docker-compose.yml b/xos/configurations/frontend/docker-compose.yml
deleted file mode 100644
index 835eb83..0000000
--- a/xos/configurations/frontend/docker-compose.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-xos_db:
-    image: xosproject/xos-postgres
-    expose:
-        - "5432"
-
-# FUTURE
-#xos_swarm_synchronizer:
-#    image: xosproject/xos-swarm-synchronizer
-#    labels:
-#        org.xosproject.kind: synchronizer
-#        org.xosproject.target: swarm
-
-xos:
-    image: xosproject/xos
-    command: python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure --makemigrations
-    #command: sleep 86400    # For interactive session
-    ports:
-        - "9999:8000"
-    links:
-        - xos_db
-    volumes:
-      - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config
-      - ../../tosca:/opt/xos/tosca
-      - ../../core/xoslib:/opt/xos/core/xoslib
-      - ../../core/static:/opt/xos/core/static
-      - ../../core/dashboard:/opt/xos/core/dashboard
-      - ../../core/templatetags:/opt/xos/core/templatetags
-      - ../../core/views:/opt/xos/core/views
-      - ../../templates:/opt/xos/templates
-      - ../../configurations:/opt/xos/configurations
-      - ../../xos:/opt/xos/xos
-      - ../../api:/opt/xos/api
-      - ../../services:/opt/xos/services
-
diff --git a/xos/configurations/frontend/xos.yaml b/xos/configurations/frontend/xos.yaml
new file mode 100644
index 0000000..8b286cd
--- /dev/null
+++ b/xos/configurations/frontend/xos.yaml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    xos:
+      type: tosca.nodes.XOS
+      properties:
+        ui_port: 9999
+        bootstrap_ui_port: 9998
+        docker_project_name: frontend
+
+    /opt/xos/xos_configuration/xos_common_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, ../common/xos_common_config, ENV_VAR ] }
+          read_only: false
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/xos/xos_configuration/xos_vtn_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: { path_join: [ SELF, CONFIG_DIR, ../vtn/files/xos_vtn_config, ENV_VAR ] }
+          read_only: true
+      requirements:
+          - xos:
+              node: xos
+              relationship: tosca.relationships.UsedByXOS
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 200bb5c..c5e36be 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1034,7 +1034,7 @@
     list_display = ("backend_status_icon", "name", "kind",
                     "versionNumber", "enabled", "published")
     list_display_links = ('backend_status_icon', 'name', )
-    fieldList = ["backend_status_text", "name", "kind", "description", "versionNumber", "enabled", "published",
+    fieldList = ["backend_status_text", "name", "kind", "description", "controller", "versionNumber", "enabled", "published",
                  "view_url", "icon_url", "public_key", "private_key_fn", "service_specific_attribute", "service_specific_id"]
     fieldsets = [
         (None, {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
@@ -1051,6 +1051,27 @@
                       ('serviceprivileges', 'Privileges')
                       )
 
+class ServiceControllerResourceInline(XOSTabularInline):
+    model = ServiceControllerResource
+    fields = ['name', 'kind', 'format', 'url']
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-resources'
+
+class ServiceControllerAdmin(XOSBaseAdmin):
+    list_display = ("backend_status_icon", "name",)
+    list_display_links = ('backend_status_icon', 'name',)
+    fieldList = ["backend_status_text", "name", "xos", "base_url"]
+    fieldsets = [
+        (None, {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
+    inlines = [ServiceControllerResourceInline]
+    readonly_fields = ('backend_status_text', )
+
+    user_readonly_fields = fieldList
+
+    suit_form_tabs = (('general', 'Service Details'),
+                      ('resources', 'Resources'),
+                      )
+
 
 class SiteNodeInline(XOSTabularInline):
     model = Node
@@ -2416,6 +2437,7 @@
 admin.site.register(Site, SiteAdmin)
 admin.site.register(Slice, SliceAdmin)
 admin.site.register(Service, ServiceAdmin)
+admin.site.register(ServiceController, ServiceControllerAdmin)
 #admin.site.register(Reservation, ReservationAdmin)
 admin.site.register(Network, NetworkAdmin)
 admin.site.register(Port, PortAdmin)
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index 5b0ad4b..41e6b3b 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -1,8 +1,10 @@
 from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn
 from .project import Project
 from .singletonmodel import SingletonModel
+from .xosmodel import XOS, XOSVolume
 from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, TenantPrivilege, TenantRole, Subscriber, Provider
 from .service import ServiceAttribute, TenantAttribute, ServiceRole
+from .service import ServiceController, ServiceControllerResource
 from .tag import Tag
 from .role import Role
 from .site import Site, Deployment, DeploymentRole, DeploymentPrivilege, Controller, ControllerRole, ControllerSite, SiteDeployment,Diag
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 5196336..c871c7e 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -1,13 +1,22 @@
 import json
 from operator import attrgetter
 
-from core.models import PlCoreBase, PlCoreBaseManager, SingletonModel
+from core.models import PlCoreBase, PlCoreBaseManager, SingletonModel, XOS
 from core.models.plcorebase import StrippedCharField
 from django.db import models
 from xos.exceptions import *
+import urlparse
 
 COARSE_KIND = "coarse"
 
+def get_xos():
+    xos = XOS.objects.all()
+
+    if xos:
+       return xos[0]
+    else:
+       return None
+

 
 class AttributeMixin(object):
     # helper for extracting things from a json-encoded
@@ -56,6 +65,55 @@
                                             None,
                                             attrname))
 
+class ServiceController(PlCoreBase):
+    xos = models.ForeignKey(XOS, related_name='service_controllers', help_text="Pointer to XOS", default=get_xos)
+    name = StrippedCharField(max_length=30, help_text="Service Name")
+    base_url = StrippedCharField(max_length=1024, help_text="Base URL, allows use of relative URLs for resources", null=True, blank=True)
+
+    def __unicode__(self): return u'%s' % (self.name)
+
+    def save(self, *args, **kwargs):
+       super(ServiceController, self).save(*args, **kwargs)
+
+       if self.xos:
+           # force XOS to rebuild
+           # XXX somewhat hackish XXX
+           self.xos.save(update_fields=["updated"])
+
+class ServiceControllerResource(PlCoreBase):
+    KIND_CHOICES = (('models', 'Models'),
+                    ('admin', 'Admin'),
+                    ('django_library', 'Django Library'),
+                    ('synchronizer', 'Synchronizer'),
+                    ('rest_service', 'REST API (service)'),
+                    ('rest_tenant', 'REST API (tenant)'),
+                    ('tosca_custom_types', 'Tosca Custom Types'),
+                    ('tosca_resource', 'Tosca Resource'),
+                    ('private_key', 'Private Key'),
+                    ('public_key', 'Public Key'))
+
+    FORMAT_CHOICES = (('python', 'Python'),
+                      ('manifest', 'Manifest'),
+                      ('docker', 'Docker Container'),
+                      ('yaml', 'YAML'),
+                      ('raw', 'raw'))
+
+    service_controller = models.ForeignKey(ServiceController, related_name='service_controller_resources',
+                                help_text="The Service Controller this resource is associated with")
+
+    name = StrippedCharField(max_length=30, help_text="Object Name")
+    kind = StrippedCharField(choices=KIND_CHOICES, max_length=30)
+    format = StrippedCharField(choices=FORMAT_CHOICES, max_length=30)
+    url = StrippedCharField(max_length=1024, help_text="URL of resource", null=True, blank=True)
+
+    def __unicode__(self): return u'%s' % (self.name)
+
+    @property
+    def full_url(self):
+        if self.service_controller and self.service_controller.base_url:
+            return urlparse.urljoin(self.service_controller.base_url, self.url)
+        else:
+            return self.url
 
 class Service(PlCoreBase, AttributeMixin):
     # when subclassing a service, redefine KIND to describe the new service
@@ -81,6 +139,10 @@
         max_length=30, blank=True, null=True)
     service_specific_attribute = models.TextField(blank=True, null=True)
 
+    controller = models.ForeignKey(ServiceController, related_name='services',
+                                help_text="The Service Controller this Service uses",
+                                null=True, blank=True)
+
     def __init__(self, *args, **kwargs):
         # for subclasses, set the default kind appropriately
         self._meta.get_field("kind").default = self.KIND
diff --git a/xos/core/models/xosmodel.py b/xos/core/models/xosmodel.py
new file mode 100644
index 0000000..4942241
--- /dev/null
+++ b/xos/core/models/xosmodel.py
@@ -0,0 +1,44 @@
+import os
+from django.db import models
+from core.models import PlCoreBase
+from core.models.plcorebase import StrippedCharField
+
+# XOS: Serves as the root of the build system
+
+
+
+class XOS(PlCoreBase):
+    name = StrippedCharField(max_length=200, unique=True, help_text="Name of XOS", default="XOS")
+    ui_port = models.IntegerField(help_text="Port for XOS UI", default=80)
+    bootstrap_ui_port = models.IntegerField(help_text="Port for XOS UI", default=81)
+    db_container_name = StrippedCharField(max_length=200, help_text="name of XOS db container", default="xos_db")
+    docker_project_name = StrippedCharField(max_length=200, help_text="docker project name")
+    enable_build = models.BooleanField(help_text="True if Onboarding Synchronizer should build XOS as necessary", default=True)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+    def __init__(self, *args, **kwargs):
+        super(XOS, self).__init__(*args, **kwargs)
+
+    def save(self, *args, **kwds):
+        super(XOS, self).save(*args, **kwds)
+
+#    def can_update(self, user):
+#        return user.can_update_site(self.site, allow=['tech'])
+
+    def rebuild(self):
+        for service_controller in self.service_controllers.all():
+            for scr in service_controller.service_controller_resources.all():
+               scr.save()
+            service_controller.save()
+        self.save()
+
+class XOSVolume(PlCoreBase):
+    xos = models.ForeignKey(XOS, related_name='volumes', help_text="The XOS object for this Volume")
+    container_path=StrippedCharField(max_length=1024, unique=True, help_text="Path of Volume in Container")
+    host_path=StrippedCharField(max_length=1024, help_text="Path of Volume in Host")
+    read_only=models.BooleanField(default=False, help_text="True if mount read-only")
+
+    def __unicode__(self): return u'%s' % (self.container_path)
+
+
diff --git a/xos/onboard/README.md b/xos/onboard/README.md
new file mode 100644
index 0000000..2030708
--- /dev/null
+++ b/xos/onboard/README.md
@@ -0,0 +1,3 @@
+This directory is a temporary placeholder for services that can be on-boarded. 
+
+Once we move to Gerritt and service-per-repo, this directory will be removed.
diff --git a/xos/services/exampleservice/admin.py b/xos/onboard/exampleservice/admin.py
similarity index 100%
rename from xos/services/exampleservice/admin.py
rename to xos/onboard/exampleservice/admin.py
diff --git a/xos/api/service/exampleservice.py b/xos/onboard/exampleservice/api/service/exampleservice.py
similarity index 100%
rename from xos/api/service/exampleservice.py
rename to xos/onboard/exampleservice/api/service/exampleservice.py
diff --git a/xos/api/tenant/exampletenant.py b/xos/onboard/exampleservice/api/tenant/exampletenant.py
similarity index 100%
rename from xos/api/tenant/exampletenant.py
rename to xos/onboard/exampleservice/api/tenant/exampletenant.py
diff --git a/xos/onboard/exampleservice/exampleservice-onboard-longform.yaml b/xos/onboard/exampleservice/exampleservice-onboard-longform.yaml
new file mode 100644
index 0000000..0eddd51
--- /dev/null
+++ b/xos/onboard/exampleservice/exampleservice-onboard-longform.yaml
@@ -0,0 +1,57 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    exampleservice:
+      type: tosca.nodes.ServiceController
+      properties:
+          base_url: file:/opt/xos/onboard/exampleservice/
+
+    exampleservice_models:
+      type: tosca.nodes.ServiceControllerResource
+      properties:
+          kind: models
+          format: python
+          url: models.py
+      requirements:
+          - controller:
+              node: exampleservice
+              relationship: tosca.relationships.UsedByController
+
+    exampleservice_admin:
+      type: tosca.nodes.ServiceControllerResource
+      properties:
+          kind: admin
+          format: python
+          url: admin.py
+      requirements:
+          - controller:
+              node: exampleservice
+              relationship: tosca.relationships.UsedByController
+
+    exampleservice_synchronizer:
+      type: tosca.nodes.ServiceControllerResource
+      properties:
+          kind: synchronizer
+          format: manifest
+          url: synchronizer/manifest
+      requirements:
+          - controller:
+              node: exampleservice
+              relationship: tosca.relationships.UsedByController
+
+    exampleservice_tosca_types:
+      type: tosca.nodes.ServiceControllerResource
+      properties:
+          kind: tosca_custom_types
+          format: yaml
+          url: exampleservice.yaml
+      requirements:
+          - controller:
+              node: exampleservice
+              relationship: tosca.relationships.UsedByController
diff --git a/xos/onboard/exampleservice/exampleservice-onboard.yaml b/xos/onboard/exampleservice/exampleservice-onboard.yaml
new file mode 100644
index 0000000..91dbb2e
--- /dev/null
+++ b/xos/onboard/exampleservice/exampleservice-onboard.yaml
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    exampleservice:
+      type: tosca.nodes.ServiceController
+      properties:
+          base_url: file:///opt/xos/onboard/exampleservice/
+          # The following will concatenate with base_url automatically, if
+          # base_url is non-null.
+          models: models.py
+          admin: admin.py
+          synchronizer: synchronizer/manifest
+          tosca_custom_types: exampleservice.yaml
+          rest_service: api/service/exampleservice.py
+          rest_tenant: api/tenant/exampletenant.py
+          private_key: file:///opt/xos/key_import/exampleservice_rsa
+          public_key: file:///opt/xos/key_import/exampleservice_rsa.pub
+
diff --git a/xos/tosca/custom_types/exampleservice.m4 b/xos/onboard/exampleservice/exampleservice.m4
similarity index 100%
rename from xos/tosca/custom_types/exampleservice.m4
rename to xos/onboard/exampleservice/exampleservice.m4
diff --git a/xos/tosca/custom_types/exampleservice.yaml b/xos/onboard/exampleservice/exampleservice.yaml
similarity index 100%
rename from xos/tosca/custom_types/exampleservice.yaml
rename to xos/onboard/exampleservice/exampleservice.yaml
diff --git a/xos/onboard/exampleservice/macros.m4 b/xos/onboard/exampleservice/macros.m4
new file mode 100644
index 0000000..1f48f10
--- /dev/null
+++ b/xos/onboard/exampleservice/macros.m4
@@ -0,0 +1,84 @@
+# Note: Tosca derived_from isn't working the way I think it should, it's not
+#    inheriting from the parent template. Until we get that figured out, use
+#    m4 macros do our inheritance
+
+define(xos_base_props,
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object)
+# Service
+define(xos_base_service_caps,
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service)
+define(xos_base_service_props,
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.)
+# Subscriber
+define(xos_base_subscriber_caps,
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber)
+define(xos_base_subscriber_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of subscriber
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+define(xos_base_tenant_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+
+# end m4 macros
+
diff --git a/xos/onboard/exampleservice/make_synchronizer_manifest.sh b/xos/onboard/exampleservice/make_synchronizer_manifest.sh
new file mode 100644
index 0000000..4058982
--- /dev/null
+++ b/xos/onboard/exampleservice/make_synchronizer_manifest.sh
@@ -0,0 +1,2 @@
+#! /bin/bash
+find synchronizer -type f | cut -b 14- > synchronizer/manifest 
diff --git a/xos/services/exampleservice/models.py b/xos/onboard/exampleservice/models.py
similarity index 100%
rename from xos/services/exampleservice/models.py
rename to xos/onboard/exampleservice/models.py
diff --git a/xos/synchronizers/exampleservice/exampleservice-synchronizer.py b/xos/onboard/exampleservice/synchronizer/exampleservice-synchronizer.py
similarity index 100%
copy from xos/synchronizers/exampleservice/exampleservice-synchronizer.py
copy to xos/onboard/exampleservice/synchronizer/exampleservice-synchronizer.py
diff --git a/xos/synchronizers/exampleservice/exampleservice_config b/xos/onboard/exampleservice/synchronizer/exampleservice_config
similarity index 100%
copy from xos/synchronizers/exampleservice/exampleservice_config
copy to xos/onboard/exampleservice/synchronizer/exampleservice_config
diff --git a/xos/onboard/exampleservice/synchronizer/manifest b/xos/onboard/exampleservice/synchronizer/manifest
new file mode 100644
index 0000000..8f43610
--- /dev/null
+++ b/xos/onboard/exampleservice/synchronizer/manifest
@@ -0,0 +1,10 @@
+manifest
+steps/sync_exampletenant.py
+steps/roles/install_apache/tasks/main.yml
+steps/roles/create_index/templates/index.html.j2
+steps/roles/create_index/tasks/main.yml
+steps/exampletenant_playbook.yaml
+exampleservice-synchronizer.py
+model-deps
+run.sh
+exampleservice_config
diff --git a/xos/synchronizers/exampleservice/model-deps b/xos/onboard/exampleservice/synchronizer/model-deps
similarity index 100%
copy from xos/synchronizers/exampleservice/model-deps
copy to xos/onboard/exampleservice/synchronizer/model-deps
diff --git a/xos/onboard/exampleservice/synchronizer/run.sh b/xos/onboard/exampleservice/synchronizer/run.sh
new file mode 100755
index 0000000..e6da8f6
--- /dev/null
+++ b/xos/onboard/exampleservice/synchronizer/run.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+python exampleservice-synchronizer.py  -C $XOS_DIR/synchronizers/exampleservice/exampleservice_config
diff --git a/xos/synchronizers/exampleservice/steps/exampletenant_playbook.yaml b/xos/onboard/exampleservice/synchronizer/steps/exampletenant_playbook.yaml
similarity index 100%
copy from xos/synchronizers/exampleservice/steps/exampletenant_playbook.yaml
copy to xos/onboard/exampleservice/synchronizer/steps/exampletenant_playbook.yaml
diff --git a/xos/synchronizers/exampleservice/steps/roles/create_index/tasks/main.yml b/xos/onboard/exampleservice/synchronizer/steps/roles/create_index/tasks/main.yml
similarity index 100%
copy from xos/synchronizers/exampleservice/steps/roles/create_index/tasks/main.yml
copy to xos/onboard/exampleservice/synchronizer/steps/roles/create_index/tasks/main.yml
diff --git a/xos/synchronizers/exampleservice/steps/roles/create_index/templates/index.html.j2 b/xos/onboard/exampleservice/synchronizer/steps/roles/create_index/templates/index.html.j2
similarity index 100%
copy from xos/synchronizers/exampleservice/steps/roles/create_index/templates/index.html.j2
copy to xos/onboard/exampleservice/synchronizer/steps/roles/create_index/templates/index.html.j2
diff --git a/xos/synchronizers/exampleservice/steps/roles/install_apache/tasks/main.yml b/xos/onboard/exampleservice/synchronizer/steps/roles/install_apache/tasks/main.yml
similarity index 100%
copy from xos/synchronizers/exampleservice/steps/roles/install_apache/tasks/main.yml
copy to xos/onboard/exampleservice/synchronizer/steps/roles/install_apache/tasks/main.yml
diff --git a/xos/synchronizers/exampleservice/steps/sync_exampletenant.py b/xos/onboard/exampleservice/synchronizer/steps/sync_exampletenant.py
similarity index 100%
copy from xos/synchronizers/exampleservice/steps/sync_exampletenant.py
copy to xos/onboard/exampleservice/synchronizer/steps/sync_exampletenant.py
diff --git a/xos/services/exampleservice/README.md b/xos/services/exampleservice/README.md
deleted file mode 100644
index ce6210d..0000000
--- a/xos/services/exampleservice/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# ExampleService
-
-This is an example XOS service, specifically the Django Model and Admin. 
-
-The Synchronizer corresponding to this service can be found in `../../synchronizers/exampleservice`.
-
-Documentation for this is located here: [XOS Guide : DevGuide : ExampleService](http://guide.xosproject.org/devguide/exampleservice/).
-
diff --git a/xos/services/exampleservice/__init__.py b/xos/services/exampleservice/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/xos/services/exampleservice/__init__.py
+++ /dev/null
diff --git a/xos/synchronizers/base/SyncInstanceUsingAnsible.py b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
index fef8f86..49ca23b 100644
--- a/xos/synchronizers/base/SyncInstanceUsingAnsible.py
+++ b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
@@ -21,7 +21,6 @@
     # observes=VSGTenant
     # requested_interval=0
     # template_name = "sync_vcpetenant.yaml"
-    # service_key_name = "/opt/xos/observers/vcpe/vcpe_private_key"
 
     def __init__(self, **args):
         SyncStep.__init__(self, **args)
@@ -96,7 +95,10 @@
                        "username": "ubuntu",
                        "ssh_ip": instance.get_ssh_ip(),
                      }
-            key_name = self.service_key_name
+            if (instance.slice) and (instance.slice.service) and (instance.slice.service.private_key_fn):
+                key_name = instance.slice.service.private_key_fn
+            else:
+                raise Exception("Make sure to set private_key_fn in the service")
         elif (instance.isolation == "container"):
             # container on bare metal
             node = self.get_node(instance)
diff --git a/xos/synchronizers/base/event_loop.py b/xos/synchronizers/base/event_loop.py
index 4d57deb..ae97329 100644
--- a/xos/synchronizers/base/event_loop.py
+++ b/xos/synchronizers/base/event_loop.py
@@ -9,6 +9,7 @@
 import json
 import pdb
 import pprint
+import traceback
 
 
 from datetime import datetime
@@ -349,9 +350,11 @@
 				if (step_status[d] is STEP_STATUS_WORKING):
                                         logger.info("  step %s wait on dep %s" % (step.__name__, d))
 					cond.wait()
+                                        logger.info("  step %s wait on dep %s cond returned" % (step.__name__, d))
 				elif step_status[d] == STEP_STATUS_OK:
 					go = True
 				else:
+                                        logger.info("  step %s has failed dep %s" % (step.__name__, d))
 					go = False
                         		failed_dep = d
 				cond.release()
@@ -361,6 +364,7 @@
 			go = True
 
 		if (not go):
+                        logger.info("Step %s skipped" % step.__name__)
                         self.consolePrint(bcolors.FAIL + "Step %r skipped on %r" % (step,failed_dep) + bcolors.ENDC)
                         # SMBAKER: sync_step was not defined here, so I changed
                         #    this from 'sync_step' to 'step'. Verify.
@@ -416,7 +420,7 @@
 					if failed_objects:
 						self.failed_step_objects.update(failed_objects)
 
-                                        logger.info("Step %r succeeded" % sync_step.__name__)
+                                        logger.info("Step %r succeeded, deletion=%s" % (sync_step.__name__, deletion))
                                         self.consolePrint(bcolors.OKGREEN + "Step %r succeeded" % sync_step.__name__ + bcolors.ENDC)
 					my_status = STEP_STATUS_OK
 					self.update_run_time(sync_step,deletion)
@@ -462,8 +466,27 @@
 
                     self.run_once()
 
+        def check_db_connection_okay(self):
+            # django implodes if the database connection is closed by docker-compose
+            try:
+                diag = Diag.objects.filter(name="foo").first()
+            except Exception, e:
+                from django import db
+                if "connection already closed" in traceback.format_exc():
+                   logger.error("XXX connection already closed")
+                   try:
+#                       if db.connection:
+#                           db.connection.close()
+                       db.close_connection()
+                   except:
+                        logger.log_exc("XXX we failed to fix the failure")
+                else:
+                   logger.log_exc("XXX some other error")
+
         def run_once(self):
                 try:
+                        self.check_db_connection_okay()
+
                         loop_start = time.time()
                         error_map_file = getattr(Config(), "error_map_path", XOS_DIR + "/error_map.txt")
                         self.error_mapper = ErrorMapper(error_map_file)
diff --git a/xos/synchronizers/base/syncstep.py b/xos/synchronizers/base/syncstep.py
index 2fa6c38..eeb61db 100644
--- a/xos/synchronizers/base/syncstep.py
+++ b/xos/synchronizers/base/syncstep.py
@@ -141,6 +141,8 @@
 
 
     def sync_record(self, o):
+        logger.info("Sync_record called for %s %s" % (o.__class__.__name__, str(o)))
+
         try:
             controller = o.get_controller()
             controller_register = json.loads(controller.backend_register)
diff --git a/xos/synchronizers/exampleservice/exampleservice-synchronizer.py b/xos/synchronizers/exampleservice_old/exampleservice-synchronizer.py
similarity index 100%
rename from xos/synchronizers/exampleservice/exampleservice-synchronizer.py
rename to xos/synchronizers/exampleservice_old/exampleservice-synchronizer.py
diff --git a/xos/synchronizers/exampleservice/exampleservice_config b/xos/synchronizers/exampleservice_old/exampleservice_config
similarity index 100%
rename from xos/synchronizers/exampleservice/exampleservice_config
rename to xos/synchronizers/exampleservice_old/exampleservice_config
diff --git a/xos/synchronizers/exampleservice/model-deps b/xos/synchronizers/exampleservice_old/model-deps
similarity index 100%
rename from xos/synchronizers/exampleservice/model-deps
rename to xos/synchronizers/exampleservice_old/model-deps
diff --git a/xos/synchronizers/exampleservice/steps/exampletenant_playbook.yaml b/xos/synchronizers/exampleservice_old/steps/exampletenant_playbook.yaml
similarity index 100%
rename from xos/synchronizers/exampleservice/steps/exampletenant_playbook.yaml
rename to xos/synchronizers/exampleservice_old/steps/exampletenant_playbook.yaml
diff --git a/xos/synchronizers/exampleservice/steps/roles/create_index/tasks/main.yml b/xos/synchronizers/exampleservice_old/steps/roles/create_index/tasks/main.yml
similarity index 100%
rename from xos/synchronizers/exampleservice/steps/roles/create_index/tasks/main.yml
rename to xos/synchronizers/exampleservice_old/steps/roles/create_index/tasks/main.yml
diff --git a/xos/synchronizers/exampleservice/steps/roles/create_index/templates/index.html.j2 b/xos/synchronizers/exampleservice_old/steps/roles/create_index/templates/index.html.j2
similarity index 100%
rename from xos/synchronizers/exampleservice/steps/roles/create_index/templates/index.html.j2
rename to xos/synchronizers/exampleservice_old/steps/roles/create_index/templates/index.html.j2
diff --git a/xos/synchronizers/exampleservice/steps/roles/install_apache/tasks/main.yml b/xos/synchronizers/exampleservice_old/steps/roles/install_apache/tasks/main.yml
similarity index 100%
rename from xos/synchronizers/exampleservice/steps/roles/install_apache/tasks/main.yml
rename to xos/synchronizers/exampleservice_old/steps/roles/install_apache/tasks/main.yml
diff --git a/xos/synchronizers/exampleservice/steps/sync_exampletenant.py b/xos/synchronizers/exampleservice_old/steps/sync_exampletenant.py
similarity index 100%
rename from xos/synchronizers/exampleservice/steps/sync_exampletenant.py
rename to xos/synchronizers/exampleservice_old/steps/sync_exampletenant.py
diff --git a/xos/synchronizers/model_policy.py b/xos/synchronizers/model_policy.py
index ef31d41..aa12092 100644
--- a/xos/synchronizers/model_policy.py
+++ b/xos/synchronizers/model_policy.py
@@ -3,10 +3,11 @@
 import pdb
 from generate.dependency_walker import *
 from synchronizers.openstack import model_policies
-from xos.logger import logger
+from xos.logger import Logger, logging
 from datetime import datetime
 from django.utils import timezone
 import time
+import traceback
 from core.models import *
 from django.db import reset_queries
 from django.db.transaction import atomic
@@ -15,6 +16,8 @@
 modelPolicyEnabled = True
 bad_instances=[]
 
+logger = Logger(level=logging.INFO)
+
 def EnableModelPolicy(x):
     global modelPolicyEnabled
     modelPolicyEnabled = x
@@ -40,9 +43,10 @@
         if (save_fields):
             d.save(update_fields=save_fields)
     except AttributeError,e:
+        logger.log_exc("AttributeError in update_dep")
         raise e
     except Exception,e:
-            logger.info('Could not save %r. Exception: %r'%(d,e), extra=d.tologdict())
+        logger.log_exc("Exception in update_dep")
 
 def delete_if_inactive(d, o):
     try:
@@ -75,7 +79,7 @@
 
     try:
         policy_handler = getattr(model_policies, policy_name, None)
-        logger.error("POLICY HANDLER: %s %s" % (policy_name, policy_handler))
+        logger.info("MODEL POLICY: handler %s %s" % (policy_name, policy_handler))
         if policy_handler is not None:
             if (deleted):
                 try:
@@ -84,23 +88,45 @@
                     pass
             else:
                 policy_handler.handle(instance)
+        logger.info("MODEL POLICY: completed handler %s %s" % (policy_name, policy_handler))
     except:
-        logger.log_exc("Model Policy Error:")
+        logger.log_exc("MODEL POLICY: Exception when running handler")
 
     try:
         instance.policed=timezone.now()
         instance.save(update_fields=['policed'])
     except:
-        logging.error('Object %r is defective'%instance)
+        logger.log_exc('MODEL POLICY: Object %r is defective'%instance)
         bad_instances.append(instance)
 
 def noop(o,p):
         pass
 
+def check_db_connection_okay():
+    # django implodes if the database connection is closed by docker-compose
+    from django import db
+    try:
+        db.connection.cursor()
+        #diag = Diag.objects.filter(name="foo").first()
+    except Exception, e:
+        if "connection already closed" in traceback.format_exc():
+           logger.error("XXX connection already closed")
+           try:
+#               if db.connection:
+#                   db.connection.close()
+               db.close_connection()
+           except:
+                logger.log_exc("XXX we failed to fix the failure")
+        else:
+           logger.log_exc("XXX some other error")
+
 def run_policy():
     while (True):
         start = time.time()
-        run_policy_once()
+        try:
+            run_policy_once()
+        except:
+            logger.log_exc("MODEL_POLICY: Exception in run_policy()")
         if (time.time()-start<1):
             time.sleep(1)
 
@@ -110,6 +136,10 @@
         objects = []
         deleted_objects = []
 
+        logger.info("MODEL POLICY: run_policy_once()")
+
+        check_db_connection_okay()
+
         for m in models:
             res = m.objects.filter((Q(policed__lt=F('updated')) | Q(policed=None)) & Q(no_policy=False))
             objects.extend(res)
@@ -137,4 +167,6 @@
             reset_queries()
         except:
             # this shouldn't happen, but in case it does, catch it...
-            logger.log_exc("exception in reset_queries")
+            logger.log_exc("MODEL POLICY: exception in reset_queries")
+
+        logger.info("MODEL POLICY: finished run_policy_once()")
diff --git a/xos/synchronizers/onboarding/files/__init__.py b/xos/synchronizers/onboarding/files/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/synchronizers/onboarding/files/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/synchronizers/onboarding/model-deps b/xos/synchronizers/onboarding/model-deps
new file mode 100644
index 0000000..4aa86c7
--- /dev/null
+++ b/xos/synchronizers/onboarding/model-deps
@@ -0,0 +1,8 @@
+{
+    "XOS": [
+        "ServiceController"
+    ], 
+    "ServiceController": [
+        "ServiceControllerResource"
+    ]
+}
diff --git a/xos/synchronizers/onboarding/onboarding-synchronizer.py b/xos/synchronizers/onboarding/onboarding-synchronizer.py
new file mode 100755
index 0000000..84bec4f
--- /dev/null
+++ b/xos/synchronizers/onboarding/onboarding-synchronizer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../synchronizers/base")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-synchronizer")
+mod.main()
diff --git a/xos/synchronizers/onboarding/onboarding_synchronizer_config b/xos/synchronizers/onboarding/onboarding_synchronizer_config
new file mode 100644
index 0000000..e2b92fd
--- /dev/null
+++ b/xos/synchronizers/onboarding/onboarding_synchronizer_config
@@ -0,0 +1,38 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=onboarding
+dependency_graph=/opt/xos/synchronizers/onboarding/model-deps
+steps_dir=/opt/xos/synchronizers/onboarding/steps
+sys_dir=/opt/xos/synchronizers/onboarding/sys
+deleters_dir=/opt/xos/synchronizers/onboarding/deleters
+log_file=console
+driver=None
+backoff_disabled=True
+pretend=False
+save_ansible_output=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/synchronizers/onboarding/run.sh b/xos/synchronizers/onboarding/run.sh
new file mode 100755
index 0000000..d52d39c
--- /dev/null
+++ b/xos/synchronizers/onboarding/run.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+python onboarding-synchronizer.py  -C $XOS_DIR/synchronizers/onboarding/onboarding_synchronizer_config
diff --git a/xos/synchronizers/onboarding/steps/sync_servicecontroller.py b/xos/synchronizers/onboarding/steps/sync_servicecontroller.py
new file mode 100644
index 0000000..77d8e12
--- /dev/null
+++ b/xos/synchronizers/onboarding/steps/sync_servicecontroller.py
@@ -0,0 +1,53 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep, DeferredException
+from core.models import XOS, ServiceController
+from xos.logger import Logger, logging
+from synchronizers.base.ansible import run_template
+
+# xosbuilder will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from xosbuilder import XOSBuilder
+
+logger = Logger(level=logging.INFO)
+
+class SyncServiceController(SyncStep, XOSBuilder):
+    provides=[ServiceController]
+    observes=ServiceController
+    requested_interval=0
+    playbook = "sync_servicecontroller.yaml"
+
+    def __init__(self, **args):
+        SyncStep.__init__(self, **args)
+        XOSBuilder.__init__(self)
+
+    def sync_record(self, sc):
+        logger.info("Sync'ing ServiceController %s" % sc)
+
+        if sc.xos and (not sc.xos.enable_build):
+            raise DeferredException("XOS build is currently disabled")
+
+        unready = self.check_controller_unready(sc)
+        if unready:
+            raise Exception("Controller %s has unready resources: %s" % (str(sc), ",".join([str(x) for x in unready])))
+
+        dockerfiles = [self.create_synchronizer_dockerfile(sc)]
+        tenant_fields = {"dockerfiles": dockerfiles,
+                         "build_dir": self.build_dir,
+                         "ansible_tag": sc.__class__.__name__ + "_" + str(sc.id)}
+
+        path="servicecontroller"
+        res = run_template(self.playbook, tenant_fields, path=path)
+
+    def delete_record(self, m):
+        pass
+
+    def fetch_pending(self, deleted=False):
+        pend = super(SyncServiceController, self).fetch_pending(deleted)
+        return pend
+
diff --git a/xos/synchronizers/onboarding/steps/sync_servicecontroller.yaml b/xos/synchronizers/onboarding/steps/sync_servicecontroller.yaml
new file mode 100644
index 0000000..9431427
--- /dev/null
+++ b/xos/synchronizers/onboarding/steps/sync_servicecontroller.yaml
@@ -0,0 +1,20 @@
+---
+- hosts: 127.0.0.1
+  connection: local
+
+  vars:
+    dockerfiles:
+    {% for dockerfile in dockerfiles %}
+      - docker_image_name: {{ dockerfile.docker_image_name }}
+        dockerfile_fn: {{ dockerfile.dockerfile_fn }}
+    {% endfor %}  
+
+  tasks: 
+    {% for dockerfile in dockerfiles %}
+    - name: build_docker_{{ dockerfile.docker_image_name }}
+      shell: chdir={{ build_dir }} docker build -f {{ dockerfile.dockerfile_fn }} --rm -t {{ dockerfile.docker_image_name }} .
+    {% endfor %}
+
+#  - build_dockers:
+#    shell: docker build -f {{ '{{' }} item.dockerfile_fn {{ '}}' }} --rm -t {{ '{{' }} item.docker_image_name {{ '}}' }} .
+#    with items: "dockerfiles"
diff --git a/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py b/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py
new file mode 100644
index 0000000..59ae93f
--- /dev/null
+++ b/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py
@@ -0,0 +1,37 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service, ServiceController, ServiceControllerResource
+from xos.logger import Logger, logging
+
+# xosbuilder will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from xosbuilder import XOSBuilder
+
+logger = Logger(level=logging.INFO)
+
+class SyncServiceControllerResource(SyncStep, XOSBuilder):
+    provides=[ServiceControllerResource]
+    observes=ServiceControllerResource
+    requested_interval=0
+
+    def __init__(self, **args):
+        SyncStep.__init__(self, **args)
+        XOSBuilder.__init__(self)
+
+    def sync_record(self, scr):
+        logger.info("Sync'ing ServiceControllerResource %s" % scr)
+        self.download_resource(scr)
+
+    def delete_record(self, m):
+        pass
+
+    def fetch_pending(self, deleted=False):
+        pend = super(SyncServiceControllerResource, self).fetch_pending(deleted)
+        return pend
+
diff --git a/xos/synchronizers/onboarding/steps/sync_xos.py b/xos/synchronizers/onboarding/steps/sync_xos.py
new file mode 100644
index 0000000..dec7a34
--- /dev/null
+++ b/xos/synchronizers/onboarding/steps/sync_xos.py
@@ -0,0 +1,52 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep, DeferredException
+from core.models import XOS
+from xos.logger import Logger, logging
+from synchronizers.base.ansible import run_template
+
+# xosbuilder will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from xosbuilder import XOSBuilder
+
+logger = Logger(level=logging.INFO)
+
+class SyncXOS(SyncStep, XOSBuilder):
+    provides=[XOS]
+    observes=XOS
+    requested_interval=0
+    playbook = "sync_xos.yaml"
+
+    def __init__(self, **args):
+        SyncStep.__init__(self, **args)
+        XOSBuilder.__init__(self)
+
+    def sync_record(self, xos):
+        logger.info("Sync'ing XOS %s" % xos)
+
+        if (not xos.enable_build):
+            raise DeferredException("XOS build is currently disabled")
+
+        self.create_docker_compose()
+
+        dockerfiles = [self.create_ui_dockerfile()]
+        tenant_fields = {"dockerfiles": dockerfiles,
+                         "build_dir": self.build_dir,
+                         "docker_project_name": xos.docker_project_name,
+                         "ansible_tag": xos.__class__.__name__ + "_" + str(xos.id)}
+
+        path="XOS"
+        res = run_template(self.playbook, tenant_fields, path=path)
+
+    def delete_record(self, m):
+        pass
+
+    def fetch_pending(self, deleted=False):
+        pend = super(SyncXOS, self).fetch_pending(deleted)
+        return pend
+
diff --git a/xos/synchronizers/onboarding/steps/sync_xos.yaml b/xos/synchronizers/onboarding/steps/sync_xos.yaml
new file mode 100644
index 0000000..8a98873
--- /dev/null
+++ b/xos/synchronizers/onboarding/steps/sync_xos.yaml
@@ -0,0 +1,20 @@
+---
+- hosts: 127.0.0.1
+  connection: local
+
+  vars:
+    dockerfiles:
+    {% for dockerfile in dockerfiles %}
+      - docker_image_name: {{ dockerfile.docker_image_name }}
+        dockerfile_fn: {{ dockerfile.dockerfile_fn }}
+    {% endfor %}  
+
+  tasks: 
+    {% for dockerfile in dockerfiles %}
+    - name: build_docker_{{ dockerfile.docker_image_name }}
+      shell: chdir={{ build_dir }} docker build -f {{ dockerfile.dockerfile_fn }} --rm -t {{ dockerfile.docker_image_name }} .
+    {% endfor %}
+
+    - name: run docker-compose
+      shell: docker-compose -p {{ docker_project_name }} -f /opt/xos/synchronizers/onboarding/docker-compose/docker-compose.yml up -d
+
diff --git a/xos/synchronizers/onboarding/templates/docker-compose.yml.j2 b/xos/synchronizers/onboarding/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..faa9d02
--- /dev/null
+++ b/xos/synchronizers/onboarding/templates/docker-compose.yml.j2
@@ -0,0 +1,48 @@
+{% for container_name, container in containers.iteritems() %}
+
+{{ container_name}}:
+#  container_name: {{ container.container_base_name }}_{{ container_name }}_1
+  image: {{ container.image }}
+{%- if container.command %}
+  command: {{ container.command }}
+{%- endif %}
+{%- if container.ports %}
+  ports:
+{%- for src,dest in container.ports.iteritems() %}
+    - "{{ src }}:{{ dest }}"
+{%- endfor %}
+{%- endif %}
+{%- if container.links %}
+  links:
+{%- for link in container.links %}
+    - {{ link }}
+{%- endfor %}
+{%- endif %}
+{%- if container.external_links %}
+  external_links:
+{%- for link in container.external_links %}
+    - {{ link }}
+{%- endfor %}
+{%- endif %}
+{%- if container.volumes %}
+  volumes:
+{%- for volume in container.volumes %}
+{%- if volume.read_only %}
+    - {{ volume.host_path }}:{{ volume.container_path }}:ro
+{%- else %}
+    - {{ volume.host_path }}:{{ volume.container_path }}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- if container.expose %}
+  expose:
+{%- for expose in container.expose %}
+    - "{{ expose }}"
+{%- endfor %}
+{%- endif %}
+  log_driver: "json-file"
+  log_opt:

+    max-size: "100k"

+    max-file: "5"
+
+{%- endfor %}
diff --git a/xos/synchronizers/onboarding/xosbuilder.py b/xos/synchronizers/onboarding/xosbuilder.py
new file mode 100644
index 0000000..ffb66ee
--- /dev/null
+++ b/xos/synchronizers/onboarding/xosbuilder.py
@@ -0,0 +1,252 @@
+import os
+import base64
+import jinja2
+import string
+import sys
+import urllib2
+import urlparse
+import xmlrpclib
+
+from xos.config import Config
+from core.models import Service, ServiceController, ServiceControllerResource, XOS
+from xos.logger import Logger, logging
+
+logger = Logger(level=logging.INFO)
+
+class XOSBuilder(object):
+    UI_KINDS=["models", "admin", "django_library", "rest_service", "rest_tenant", "tosca_custom_types", "tosca_resource","public_key"]
+    SYNC_CONTROLLER_KINDS=["synchronizer", "private_key", "public_key"]
+    SYNC_ALLCONTROLLER_KINDS=["models", "django_library"]
+
+    def __init__(self):
+        self.source_ui_image = "xosproject/xos"
+        self.source_sync_image = "xosproject/xos-synchronizer-openstack"
+        self.build_dir = "/opt/xos/BUILD/"
+
+    # stuff that has to do with downloading
+
+    def get_dest_dir(self, scr):
+        xos_base = "opt/xos"
+        service_name = scr.service_controller.name
+        base_dirs = {"models": "%s/services/%s/" % (xos_base, service_name),
+                     "admin": "%s/services/%s/" % (xos_base, service_name),
+                     "django_library": "%s/services/%s/" % (xos_base, service_name),
+                     "synchronizer": "%s/synchronizers/%s/" % (xos_base, service_name),
+                     "tosca_custom_types": "%s/tosca/custom_types/" % (xos_base),
+                     "tosca_resource": "%s/tosca/resources/" % (xos_base),
+                     "rest_service": "%s/api/service/" % (xos_base),
+                     "rest_tenant": "%s/api/tenant/" % (xos_base),
+                     "private_key": "%s/services/%s/keys" % (xos_base, service_name),
+                     "public_key": "%s/services/%s/keys/" % (xos_base, service_name)}
+        return base_dirs[scr.kind]
+
+    def get_build_fn(self, scr):
+        dest_dir = self.get_dest_dir(scr)
+        dest_fn = os.path.split(urlparse.urlsplit(scr.full_url).path)[-1]
+        return os.path.join(dest_dir, dest_fn)
+
+    def get_download_fn(self, scr):
+        dest_fn = self.get_build_fn(scr)
+        return os.path.join(self.build_dir, dest_fn)
+
+    def read_manifest(self, scr, fn):
+        manifest = []
+        manifest_lines = file(fn).readlines()
+        manifest_lines = [x.strip() for x in manifest_lines]
+        manifest_lines = [x for x in manifest_lines if x]
+        for line in manifest_lines:
+            url_parts = urlparse.urlsplit(scr.full_url)
+            new_path = os.path.join(os.path.join(*os.path.split(url_parts.path)[:-1]),line)
+            url = urlparse.urlunsplit( (url_parts.scheme, url_parts.netloc, new_path, url_parts.query, url_parts.fragment) )
+
+            build_fn = os.path.join(self.get_dest_dir(scr), line)
+            download_fn = os.path.join(self.build_dir, build_fn)
+
+            manifest.append( (url, download_fn, build_fn) )
+        return manifest
+
+    def download_file(self, url, dest_fn):
+        logger.info("Download %s to %s" % (url, dest_fn))
+        if not os.path.exists(os.path.dirname(dest_fn)):
+            os.makedirs(os.path.dirname(dest_fn))
+        obj = urllib2.urlopen(url)
+        file(dest_fn,"w").write(obj.read())
+
+        # make python files executable
+        if dest_fn.endswith(".py"): # and contents.startswith("#!"):
+            os.chmod(dest_fn, 0755)
+
+    def download_resource(self, scr):
+        if scr.format == "manifest":
+            manifest_fn = self.get_download_fn(scr)
+            self.download_file(scr.full_url, manifest_fn)
+            manifest = self.read_manifest(scr, manifest_fn)
+            for (url, download_fn, build_fn) in manifest:
+                self.download_file(url, download_fn)
+        else:
+            self.download_file(scr.full_url, self.get_download_fn(scr))
+
+    def get_docker_lines(self, scr):
+        if scr.format == "manifest":
+            manifest_fn = self.get_download_fn(scr)
+            manifest = self.read_manifest(scr, manifest_fn)
+            lines = []
+            for (url, download_fn, build_fn) in manifest:
+               lines.append("ADD %s /%s" % (build_fn, build_fn))
+            return lines
+        else:
+            build_fn = self.get_build_fn(scr)
+            return ["ADD %s /%s" % (build_fn, build_fn)]
+
+    def get_controller_docker_lines(self, controller, kinds):
+        need_service_init_py = False
+        dockerfile=[]
+        for scr in controller.service_controller_resources.all():
+            if scr.kind in kinds:
+                lines = self.get_docker_lines(scr)
+                dockerfile = dockerfile + lines
+            if scr.kind in ["admin", "models"]:
+                need_service_init_py = True
+
+        if need_service_init_py:
+            file(os.path.join(self.build_dir, "opt/xos/empty__init__.py"),"w").write("")
+            dockerfile.append("ADD opt/xos/empty__init__.py /opt/xos/services/%s/__init__.py" % controller.name)
+
+        return dockerfile
+
+    def check_controller_unready(self, controller):
+        unready_resources=[]
+        for scr in controller.service_controller_resources.all():
+            if (not scr.backend_status) or (not scr.backend_status.startswith("1")):
+                unready_resources.append(scr)
+
+        return unready_resources
+
+    # stuff that has to do with building
+
+    def create_xos_app_data(self, name, dockerfile, app_list, migration_list):
+        if not os.path.exists(os.path.join(self.build_dir,"opt/xos/xos")):
+            os.makedirs(os.path.join(self.build_dir,"opt/xos/xos"))
+
+        if app_list:
+            dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_app_list /opt/xos/xos/xosbuilder_app_list" % name)
+            file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_app_list") % name, "w").write("\n".join(app_list)+"\n")
+
+        if migration_list:
+            dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_migration_list /opt/xos/xos/xosbuilder_migration_list" % name)
+            file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_migration_list") % name, "w").write("\n".join(migration_list)+"\n")
+
+    def create_ui_dockerfile(self):
+        dockerfile_fn = "Dockerfile.UI"
+
+        app_list = []
+        migration_list = []
+
+        dockerfile = ["FROM %s" % self.source_ui_image]
+        for controller in ServiceController.objects.all():
+            if self.check_controller_unready(controller):
+                 logger.warning("Controller %s has unready resources" % str(controller))
+                 continue
+
+            dockerfile = dockerfile + self.get_controller_docker_lines(controller, self.UI_KINDS)
+            if controller.service_controller_resources.filter(kind="models").exists():
+                app_list.append("services." + controller.name)
+                migration_list.append(controller.name)
+
+        self.create_xos_app_data("ui", dockerfile, app_list, migration_list)
+
+        file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n")
+
+        return {"dockerfile_fn": dockerfile_fn,
+                "docker_image_name": "xosproject/xos-ui"}
+
+    def create_synchronizer_dockerfile(self, controller):
+        # bake in the synchronizer from this controller
+        sync_lines = self.get_controller_docker_lines(controller, self.SYNC_CONTROLLER_KINDS)
+        if not sync_lines:
+            return []
+
+        dockerfile_fn = "Dockerfile.%s" % controller.name
+        dockerfile = ["FROM %s" % self.source_sync_image]
+
+        # Now bake in models from this controller as well as the others
+        # It's important to bake all services in, because some services'
+        # synchronizers may depend on models from another service.
+        app_list = []
+        for c in ServiceController.objects.all():
+            dockerfile = dockerfile + self.get_controller_docker_lines(c, self.SYNC_ALLCONTROLLER_KINDS)
+            if controller.service_controller_resources.filter(kind="models").exists():
+                app_list.append("services." + controller.name)
+
+        self.create_xos_app_data(controller.name, dockerfile, app_list, None)
+
+        dockerfile = dockerfile + sync_lines
+        file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n")
+
+        return {"dockerfile_fn": dockerfile_fn,
+                "docker_image_name": "xosproject/xos-synchronizer-%s" % controller.name}
+
+    def create_docker_compose(self):
+         xos = XOS.objects.all()[0]
+
+         volume_list = []
+         for volume in xos.volumes.all():
+             volume_list.append({"host_path": volume.host_path,
+                                 "container_path": volume.container_path,
+                                 "read_only": volume.read_only})
+
+         containers = {}
+
+         containers["xos_db"] = \
+                            {"image": "xosproject/xos-postgres",
+                             "expose": [5432]}
+
+         db_container_name = xos.docker_project_name + "_xos_db_1"
+
+         containers["xos_ui"] = \
+                            {"image": "xosproject/xos-ui",
+                             "command": "python /opt/xos/manage.py runserver 0.0.0.0:%d --insecure --makemigrations" % xos.ui_port,
+                             "ports": {"%d"%xos.ui_port : "%d"%xos.ui_port},
+                             "links": ["xos_db"],
+                             #"external_links": [db_container_name],
+                             "volumes": volume_list}
+
+#         containers["xos_bootstrap_ui"] = {"image": "xosproject/xos",
+#                             "command": "python /opt/xos/manage.py runserver 0.0.0.0:%d --insecure --makemigrations" % xos.bootstrap_ui_port,
+#                             "ports": {"%d"%xos.bootstrap_ui_port : "%d"%xos.bootstrap_ui_port},
+#                             #"external_links": [db_container_name],
+#                             "links": ["xos_db"],
+#                             "volumes": volume_list}
+
+         for c in ServiceController.objects.all():
+             if self.check_controller_unready(c):
+                 logger.warning("Controller %s has unready resources" % str(c))
+                 continue
+
+             containers["xos_synchronizer_%s" % c.name] = \
+                            {"image": "xosproject/xos-synchronizer-%s" % c.name,
+                             "command": 'bash -c "sleep 120; cd /opt/xos/synchronizers/%s; bash ./run.sh"' % c.name,
+                             #"external_links": [db_container_name],
+                             "links": ["xos_db"],
+                             "volumes": volume_list}
+
+         vars = { "containers": containers }
+
+         template_loader = jinja2.FileSystemLoader( "/opt/xos/synchronizers/onboarding/templates/" )
+         template_env = jinja2.Environment(loader=template_loader)
+         template = template_env.get_template("docker-compose.yml.j2")
+         buffer = template.render(vars)
+
+         if not os.path.exists("/opt/xos/synchronizers/onboarding/docker-compose"):
+             os.makedirs("/opt/xos/synchronizers/onboarding/docker-compose")
+         file("/opt/xos/synchronizers/onboarding/docker-compose/docker-compose.yml", "w").write(buffer)
+
+#    def build_xos(self):
+#        dockerfiles=[]
+#        dockerfiles.append(self.create_ui_dockerfile())
+#
+#        for controller in ServiceController.objects.all():
+#            dockerfiles.append(self.create_synchronizer_dockerfile(controller))
+
+
+
diff --git a/xos/synchronizers/openstack/steps/purge_disabled_users.py b/xos/synchronizers/openstack/steps/purge_disabled_users.py
index 0973b8c..6b1dac3 100644
--- a/xos/synchronizers/openstack/steps/purge_disabled_users.py
+++ b/xos/synchronizers/openstack/steps/purge_disabled_users.py
@@ -7,19 +7,19 @@
 from core.models.user import User
 from xos.logger import observer_logger as logger
 
-class SyncRoles(OpenStackSyncStep):
-    provides=[User]
-    requested_interval=0
-    observes=User
-
-    def fetch_pending(self, deleted):
-        if (deleted):
-            # users marked as deleted
-            return User.deleted_objects.all()
-        else:
-            # disabled users that haven't been updated in over a week 
-            one_week_ago = datetime.datetime.now() - datetime.timedelta(days=7)
-            return User.objects.filter(is_active=False, updated__gt=one_week_ago)             
-
-    def sync_record(self, user):
-        user.delete() 
+#class SyncRoles(OpenStackSyncStep):
+#    provides=[User]
+#    requested_interval=0
+#    observes=User
+#
+#    def fetch_pending(self, deleted):
+#        if (deleted):
+#            # users marked as deleted
+#            return User.deleted_objects.all()
+#        else:
+#            # disabled users that haven't been updated in over a week
+#            one_week_ago = datetime.datetime.now() - datetime.timedelta(days=7)
+#            return User.objects.filter(is_active=False, updated__gt=one_week_ago)
+#
+#    def sync_record(self, user):
+#        user.delete()
diff --git a/xos/tools/rebuild.py b/xos/tools/rebuild.py
new file mode 100755
index 0000000..dc2c482
--- /dev/null
+++ b/xos/tools/rebuild.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python                                                                                                               
+
+import os
+import sys
+sys.path.append("/opt/xos")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+import django
+from core.models import XOS
+django.setup()
+
+xoses = XOS.objects.all()
+if not xoses:
+    print "There is no XOS model"
+
+for xos in xoses:
+    xos.rebuild()
+
diff --git a/xos/tosca/custom_types/exampleservice.m4 b/xos/tosca/custom_types/exampleservice.m4._unused
similarity index 100%
copy from xos/tosca/custom_types/exampleservice.m4
copy to xos/tosca/custom_types/exampleservice.m4._unused
diff --git a/xos/tosca/custom_types/exampleservice.yaml b/xos/tosca/custom_types/exampleservice.yaml._unused
similarity index 100%
copy from xos/tosca/custom_types/exampleservice.yaml
copy to xos/tosca/custom_types/exampleservice.yaml._unused
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 109fc1d..9497b6d 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -6,6 +6,43 @@
 include(macros.m4)
 
 node_types:
+    tosca.nodes.XOS:
+        derived_from: tosca.nodes.Root
+        description: The root of XOS
+        properties:
+            xos_base_props
+            ui_port:
+                type: integer
+                required: false
+                description: TCP port of user interface
+            bootstrap_ui_port:
+                type: integer
+                required: false
+                descrption: TCP port of bootstrap user interface
+            docker_project_name:
+                type: string
+                required: false
+                description: Docker project name
+            enable_build:
+                type: boolean
+                required: false
+                description: True if XOS build should be enabled
+
+
+    tosca.nodes.XOSVolume:
+        derived_from: tosca.nodes.Root
+        description: A volume that should be attached to the XOS docker container
+        properties:
+            xos_base_props
+            host_path:
+                type: string
+                required: false
+                description: path of resource on host
+            read_only:
+                type: boolean
+                required: false
+                description: True if mount read only
+
     tosca.nodes.Service:
         derived_from: tosca.nodes.Root
         description: >
@@ -17,6 +54,68 @@
             xos_base_props
             xos_base_service_props
 
+    tosca.nodes.ServiceController:
+        derived_from: tosca.nodes.Root
+        description: >
+            An XOS Service Controller.
+        properties:
+            xos_base_props
+            base_url:
+                type: string
+                required: false
+                description: Base url, to allow resources to use relative URLs
+            models:
+                type: string
+                required: false
+                description: url of models.py
+            admin:
+                type: string
+                required: false
+                description: url of admin.py
+            synchronizer:
+                type: string
+                required: false
+                description: url of synchronizer manifest
+            tosca_custom_types:
+                type: string
+                required: false
+                description: url of tosca custom_types
+            rest_service:
+                type: string
+                required: false
+                description: url of REST API service file
+            rest_tenant:
+                type: string
+                required: false
+                description: url of REST API tenant file
+            private_key:
+                type: string
+                required: false
+                description: private key
+            public_key:
+                type: string
+                required: false
+                description: public key
+
+    tosca.nodes.ServiceControllerResource:
+        derived_from: tosca.nodes.Root
+        description: >
+            An XOS Service Resource.
+        properties:
+            xos_base_props
+            kind:
+                type: string
+                required: false
+                description: models, admin, django_library, synchronizer, rest, tosca_custom_types, or tosca_resource
+            format:
+                type: string
+                required: false
+                description: python, manifest, or docker
+            url:
+                type: string
+                required: false
+                description: url of resource, may be relative to base_url or absolute
+
     tosca.nodes.Tenant:
         derived_from: tosca.nodes.Root
         description: >
@@ -1034,6 +1133,15 @@
     tosca.relationships.UsesAgent:
         derived_from: tosca.relationships.Root
 
+    tosca.relationships.HasResource:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.UsedByController:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.UsedByXOS:
+        derived_from: tosca.relationships.Root
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 8b4c669..66229d5 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -21,6 +21,73 @@
 
 
 node_types:
+    tosca.nodes.XOS:
+        derived_from: tosca.nodes.Root
+        description: The root of XOS
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            ui_port:
+                type: integer
+                required: false
+                description: TCP port of user interface
+            bootstrap_ui_port:
+                type: integer
+                required: false
+                descrption: TCP port of bootstrap user interface
+            docker_project_name:
+                type: string
+                required: false
+                description: Docker project name
+            enable_build:
+                type: boolean
+                required: false
+                description: True if XOS build should be enabled
+
+
+    tosca.nodes.XOSVolume:
+        derived_from: tosca.nodes.Root
+        description: A volume that should be attached to the XOS docker container
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            host_path:
+                type: string
+                required: false
+                description: path of resource on host
+            read_only:
+                type: boolean
+                required: false
+                description: True if mount read only
+
     tosca.nodes.Service:
         derived_from: tosca.nodes.Root
         description: >
@@ -80,6 +147,98 @@
                 required: false
                 description: Version number of Service.
 
+    tosca.nodes.ServiceController:
+        derived_from: tosca.nodes.Root
+        description: >
+            An XOS Service Controller.
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            base_url:
+                type: string
+                required: false
+                description: Base url, to allow resources to use relative URLs
+            models:
+                type: string
+                required: false
+                description: url of models.py
+            admin:
+                type: string
+                required: false
+                description: url of admin.py
+            synchronizer:
+                type: string
+                required: false
+                description: url of synchronizer manifest
+            tosca_custom_types:
+                type: string
+                required: false
+                description: url of tosca custom_types
+            rest_service:
+                type: string
+                required: false
+                description: url of REST API service file
+            rest_tenant:
+                type: string
+                required: false
+                description: url of REST API tenant file
+            private_key:
+                type: string
+                required: false
+                description: private key
+            public_key:
+                type: string
+                required: false
+                description: public key
+
+    tosca.nodes.ServiceControllerResource:
+        derived_from: tosca.nodes.Root
+        description: >
+            An XOS Service Resource.
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            kind:
+                type: string
+                required: false
+                description: models, admin, django_library, synchronizer, rest, tosca_custom_types, or tosca_resource
+            format:
+                type: string
+                required: false
+                description: python, manifest, or docker
+            url:
+                type: string
+                required: false
+                description: url of resource, may be relative to base_url or absolute
+
     tosca.nodes.Tenant:
         derived_from: tosca.nodes.Root
         description: >
@@ -881,6 +1040,8 @@
                 required: false
                 description: list of access devices, in format "uplink vlan", multiple entries separated by commas
 
+# XXX - uncomment if we want access device to be specified as separate Tosca
+# objects, rather than encoding them into VOLTDevice.access_devices
 #    tosca.nodes.AccessDevice:
 #        derived_from: tosca.nodes.Root
 #        description: >
@@ -1856,6 +2017,18 @@
     tosca.relationships.MemberOfDevice:
         derived_from: tosca.relationships.Root
 
+    tosca.relationships.UsesAgent:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.HasResource:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.UsedByController:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.UsedByXOS:
+        derived_from: tosca.relationships.Root
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
diff --git a/xos/tosca/resources/exampleservice.py b/xos/tosca/resources/exampleservice._unused
similarity index 100%
rename from xos/tosca/resources/exampleservice.py
rename to xos/tosca/resources/exampleservice._unused
diff --git a/xos/tosca/resources/exampletenant.py b/xos/tosca/resources/exampletenant._unused
similarity index 100%
rename from xos/tosca/resources/exampletenant.py
rename to xos/tosca/resources/exampletenant._unused
diff --git a/xos/tosca/resources/servicecontroller.py b/xos/tosca/resources/servicecontroller.py
new file mode 100644
index 0000000..34b95c2
--- /dev/null
+++ b/xos/tosca/resources/servicecontroller.py
@@ -0,0 +1,42 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import ServiceController, ServiceControllerResource
+
+from xosresource import XOSResource
+
+class XOSServiceController(XOSResource):
+    provides = "tosca.nodes.ServiceController"
+    xos_model = ServiceController
+    copyin_props = ["base_url"]
+
+    def postprocess_resource_prop(self, obj, kind, format):
+        value = self.get_property(kind)
+        if value:
+            scr = ServiceControllerResource.objects.filter(service_controller=obj, kind=kind, format=format)
+            if scr:
+                scr=scr[0]
+                if scr.url != value:
+                    self.info("updating resource %s" % kind)
+                    scr.url = value
+                    scr.save()
+            else:
+                self.info("adding resource %s" % kind)
+                scr = ServiceControllerResource(service_controller=obj, name=kind, kind=kind, format=format, url=value)
+                scr.save()
+
+    def postprocess(self, obj):
+        # allow these common resource to be specified directly by the ServiceController tosca object
+        self.postprocess_resource_prop(obj, "models", "python")
+        self.postprocess_resource_prop(obj, "admin", "python")
+        self.postprocess_resource_prop(obj, "tosca_custom_types", "yaml")
+        self.postprocess_resource_prop(obj, "synchronizer", "manifest")
+        self.postprocess_resource_prop(obj, "private_key", "raw")
+        self.postprocess_resource_prop(obj, "public_key", "raw")
+        self.postprocess_resource_prop(obj, "rest_service", "python")
+        self.postprocess_resource_prop(obj, "rest_tenant", "python")
+
diff --git a/xos/tosca/resources/servicecontrollerresource.py b/xos/tosca/resources/servicecontrollerresource.py
new file mode 100644
index 0000000..96ea83d
--- /dev/null
+++ b/xos/tosca/resources/servicecontrollerresource.py
@@ -0,0 +1,27 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import ServiceControllerResource, ServiceController
+
+from xosresource import XOSResource
+
+class XOSServiceControllerResource(XOSResource):
+    provides = "tosca.nodes.ServiceControllerResource"
+    xos_model = ServiceControllerResource
+    copyin_props = ["kind", "format", "url"]
+
+    def get_xos_args(self, throw_exception=True):
+        args = super(XOSServiceControllerResource, self).get_xos_args()
+
+        controller_name = self.get_requirement("tosca.relationships.UsedByController", throw_exception=throw_exception)
+        if controller_name:
+            args["service_controller"] = self.get_xos_object(ServiceController, throw_exception=throw_exception, name=controller_name)
+
+        return args
+
+
+
diff --git a/xos/tosca/resources/xosmodel.py b/xos/tosca/resources/xosmodel.py
new file mode 100644
index 0000000..188bb4f
--- /dev/null
+++ b/xos/tosca/resources/xosmodel.py
@@ -0,0 +1,30 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import XOS, XOSVolume
+
+from xosresource import XOSResource
+
+class XOSXOS(XOSResource):
+    provides = "tosca.nodes.XOS"
+    xos_model = XOS
+    copyin_props = ["ui_port", "bootstrap_ui_port", "docker_project_name", "enable_build"]
+
+class XOSVolume(XOSResource):
+    provides = "tosca.nodes.XOSVolume"
+    xos_model = XOSVolume
+    copyin_props = ["host_path"]
+    name_field = "container_path"
+
+    def get_xos_args(self, throw_exception=True):
+        args = super(XOSVolume, self).get_xos_args()
+
+        xos_name = self.get_requirement("tosca.relationships.UsedByXOS", throw_exception=throw_exception)
+        if xos_name:
+            args["xos"] = self.get_xos_object(XOS, throw_exception=throw_exception, name=xos_name)
+
+        return args
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 012f814..f65a231 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -189,6 +189,17 @@
 
         raise Exception("artifact %s not found" % name)
 
+    def intrinsic_path_join(self, obj=None, name=None, varname=None, method=None):
+        if obj!="SELF":
+            raise Exception("only SELF is supported for get_artifact first arg")
+        if method!="ENV_VAR":
+            raise Exception("only ENV_VAR is supported for get_artifact fourth arg")
+
+        if not (name in os.environ):
+            raise Exception("environment variable %s not found" % name)
+
+        return os.path.join(os.environ[name], varname)
+
     def try_intrinsic_function(self, v):
         try:
             jsv = v.replace("'", '"')
@@ -205,6 +216,8 @@
             return self.intrinsic_get_artifact(*jsv["get_artifact"])
         elif "get_script_env" in jsv:
             return self.intrinsic_get_script_env(*jsv["get_script_env"])
+        elif "path_join" in jsv:
+            return self.intrinsic_path_join(*jsv["path_join"])
 
         return v
 
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index 7835689..9f20937 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -197,6 +197,13 @@
     'rest_framework_swagger',
 )
 
+# add services that were configured by xosbuilder to INSTALLED_APPS
+if os.path.exists("/opt/xos/xos/xosbuilder_app_list"):
+    for line in file("/opt/xos/xos/xosbuilder_app_list").readlines():
+        line = line.strip()
+        if line:
+            INSTALLED_APPS = list(INSTALLED_APPS) + [line]
+
 if DJANGO_VERSION[1] >= 7:
     # if django >= 1.7, then change the admin module
     INSTALLED_APPS = list(INSTALLED_APPS)