This closes #1166
diff --git a/brooklyn-docs/guide/misc/release-notes.md b/brooklyn-docs/guide/misc/release-notes.md
index 3661ab1..ef79155 100644
--- a/brooklyn-docs/guide/misc/release-notes.md
+++ b/brooklyn-docs/guide/misc/release-notes.md
@@ -16,7 +16,7 @@
 
 ### Introduction
 
-Version 0.8.0 is [TODO add description] 
+Version 0.9.0 is [TODO add description] 
 
 Thanks go to our community for their improvements, feedback and guidance, and
 to Brooklyn's commercial users for funding much of this development.
@@ -24,7 +24,21 @@
 
 ### New Features
 
-[TODO]
+1. Parameters (config keys) can now be defined in YAML, using `brooklyn.parameters`.
+This allows YAML entities to advertise how they should be parameterized,
+for use in the UI and in documentation tools, and do coercion on these values.
+For a good demonstration, see the "Custom Entities" section of the YAML chapter of the user guide. 
+
+[ TODO - 
+restructuring and graduation; 
+the `br` client CLI tool;
+test framework;
+`$brooklyn:external(...)` extension for taking values from other sources is supported in more places;
+better YAML editor in the UI;
+OSGi-native mode using Karaf, to simplify packaging of blueprints;
+a new pure-java WinRM client (winrm4j), hugely reducing the number of dependencies and distro size;
+several version bumps (jclouds) and performance and reliability improvements
+]
  
 
 ### Backwards Compatibility
@@ -49,10 +63,10 @@
 and the referencing catalog item ID also takes priority; this has no effect in most cases, but if you have a chain of
 referenced types blueprint plan source code and the catalog item ID are now set correctly. 
 
-For changes in prior versions, please refer to the release notes for 
-[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
-
 3. Task cancellation is now propagated to dependent submitted tasks, including backgrounded tasks if they are transient.
 Previously when a task was cancelled the API did not guarantee semantics but the behaviour was to cancel sub-tasks only 
 in very limited cases. Now the semantics are more precise and controllable, and more sub-tasks are cancelled.
 This can prevent some leaked waits on `attributeWhenReady`.
+
+For changes in prior versions, please refer to the release notes for 
+[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
diff --git a/brooklyn-docs/guide/yaml/custom-entities.md b/brooklyn-docs/guide/yaml/custom-entities.md
index 6f21c14..9698aa8 100644
--- a/brooklyn-docs/guide/yaml/custom-entities.md
+++ b/brooklyn-docs/guide/yaml/custom-entities.md
@@ -24,15 +24,26 @@
 {% endhighlight %}
 
 This starts a simple `nc` listener on port 4321 which will respond `hello` to the first
-session which connects to it. Test it by running `telnet localhost 4321`.  
+session which connects to it. Test it by running `telnet localhost 4321`
+or opening `http://localhost:4321` in a browser.
+
+Note that it only allows you connect once, and after that it fails.
+This is deliberate! We'll repair this later in this example.
+Until then however, in the *Applications* view you can click the server,
+go to the `Effectors` tab, and click `restart` to bring if back to life.  
 
 This is just a simple script, but it shows how any script can be easily embedded here,
 including a script to download and run other artifacts.
 Many artifacts are already packaged such that they can be downloaded and launched 
 with a simple script, and `VanillaSoftwareProcess` can also be used for them. 
-We can specify a `download.url` which downloads artifacts (unpacking TAR, TGZ, and ZIP archives)
+
+
+#### Downloading Files
+
+We can specify a `download.url` which downloads an artifact 
+(and automatically unpacking TAR, TGZ, and ZIP archives)
 before running `launch.command` relative to where that file is installed (or unpacked),
-with `./start.sh` being the default `launch.command`.
+with the default `launch.command` being `./start.sh`.
 
 So if we create a file `/tmp/netcat-server.tgz` containing just `start.sh` in the root
 which consists of the two lines in the previous example,
@@ -42,17 +53,130 @@
 {% readj example_yaml/vanilla-bash-netcat-file.yaml %}
 {% endhighlight %}
 
-The one requirement of the script is that it store the process ID (PID) in the file
+
+#### Port Inferencing
+
+If you're deploying to a cloud machine, a firewall might block the port 4321.
+We can tell Brooklyn to open this port explicitly by specifying `inboundPorts: [ 4321 ]`;
+however a more idiomatic way is to specify a config ending with `.port`,
+such as:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port.yaml %}
+{% endhighlight %}
+
+The regex for ports to be opened can be configured using
+the config `inboundPorts.configRegex` (which has `.*\.port` as the default value).
+
+Config keys of type `org.apache.brooklyn.api.location.PortRange` (aka `port`)
+have special behaviour: when configuring, you can use range notation `8000-8100` or `8000+` to tell Brooklyn
+to find **one** port matching; this is useful when ports might be in use.
+In addition, any such config key will be opened, 
+irrespective of whether it matches the `inboundPorts.configRegex`. 
+To prevent any inferencing of ports to open, you can set the config `inboundPorts.autoInfer` to `false`.
+
+Note that in the example above, `netcat.port` must be specified in a `brooklyn.config` block.
+This block can be used to hold any config (including for example the `launch.command`),
+but for convenience Brooklyn allows config keys declared on the underlying type
+to be specified up one level, alongside the type.
+However config keys which are *not* declared on the type *must* be declared in the `brooklyn.config` block. 
+
+
+#### Declaring New Config Keys
+
+We can define config keys to be presented to the user 
+using the `brooklyn.parameters` block:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port-parameter.yaml %}
+{% endhighlight %}
+
+The example above will allow a user to specify a message to send back
+and the port where netcat will listen.
+The metadata on these parameters is available at runtime in the UI
+and through the API, and is used when populating a catalog.
+
+The example also shows how these values can be passed as environment variables to the launch command.
+The `$brooklyn:config(...)` function returns the config value supplied or default.
+For the type `port`, an attribute sensor is also created to report the *actual* port used after port inference,
+and so the `$brooklyn:attributeWhenReady(...)` function is used.
+(If `$brooklyn:config("netcat.port")` had been used, `4321+` would be passed as `NETCAT_PORT`.)
+
+This gives us quite a bit more power in writing our blueprint:
+
+* Multiple instances of the server can be launched simultaneously on the same host, 
+  as the `4321+` syntax enables Brooklyn to assign them different ports
+* If this type is added to the catalog, a user can configure the message and the port;
+  we'll show this in the next section
+
+
+#### Using the Catalog and Clustering
+
+The *Catalog* tab allows you to add blueprints which you can refer to in other blueprints.
+In that tab, click *+* then *YAML*, and enter the following:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-catalog.bom %}
+{% endhighlight %}
+
+This is the same example as in the previous section, wrapped according to the catalog YAML requirements,
+with one new block added defining an enricher. An enricher creates a new sensor from other values;
+in this case it will create a `main.uri` sensor by populating a `printf`-style string `"http://%s:%s"`
+with the sensor values.
+
+With this added to the catalog, we can reference the type `netcat-example` when we deploy an application.
+Return to the *Home* or *Applications* tab, click *+*, and submit this YAML blueprint:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-reference.yaml %}
+{% endhighlight %}
+
+This extends the previous blueprint which we registered in the catalog,
+meaning that we don't need to include it each time.
+Here, we've elected to supply our own message, but we'll use the default port.
+More importantly, we can package it for others to consume -- or take items others have built.
+
+We can go further and use this to deploy a cluster,
+this time giving a custom port as well as a custom message: 
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-cluster.yaml %}
+{% endhighlight %}
+
+In either of the above examples, if you explore the tree in the *Applications* view
+and look at the *Summary* tab of any of the server instances, you'll now see the URL where netcat is running.
+But remember, netcat will stop after one run, so you'll only be able to use each link once
+before you have to restart it.  You can also run `restart` on the cluster,
+and if you haven't yet experimented with `resize` on the cluster you might want to do that.
+
+
+#### Determining Successful Launch
+
+One requirement of the launch script is that it store the process ID (PID) in the file
 pointed to by `$PID_FILE`, hence the second line of the script.
-This is because Brooklyn wants to monitor the services under management. 
-(There are other options; you can set `checkRunning.command` and `stop.command` instead,
-as documented on the Javadoc of the `VanillaSoftwareProcess` class,
-and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.)
+This is because Brooklyn wants to monitor the services under management.
+You'll observe this if you connect to one of the netcat services,
+as the process exits afterwards and Brooklyn sets the entity to an `ON_FIRE` state.
+(You can also test this with a `killall nc` before connecting
+or issueing a `stop` command on the server -- but not on the example,
+as stopping an application root causes it to be removed altogether!) 
+
+There are other options for determining launch: you can set `checkRunning.command` and `stop.command` instead,
+as documented on the javadoc and config keys of the {% include java_link.html class_name="VanillaSoftwareProcess" package_path="org/apache/brooklyn/entity/software/base" project_subpath="software/base" %} class,
+and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-more-commands.yaml %}
+{% endhighlight %}
 
 And indeed, once you've run one `telnet` to the server, you'll see that the 
-service has gone "on fire" in Brooklyn -- because the `nc` process has stopped. 
+service has gone "on fire" in Brooklyn -- because the `nc` process stops after one run. 
+
+
+#### Attaching Policies
+
 Besides detecting this failure, Brooklyn policies can be added to the YAML to take appropriate 
-action. A simple recovery here might just be to restart the process:
+action. A simple recovery here might just to automatically restart the process:
 
 {% highlight yaml %}
 {% readj example_yaml/vanilla-bash-netcat-restarter.yaml %}
@@ -67,6 +191,9 @@
 Running with this blueprint, you'll see that the service shows as on fire for 15s after a `telnet`,
 before the policy restarts it. 
 
+
+### Sensors and Effectors
+
 For an even more interesting way to test it, look at the blueprint defining
 [a netcat server and client](example_yaml/vanilla-bash-netcat-w-client.yaml).
 This uses `initializers` to define an effector to `sayHiNetcat` on the `Simple Pinger` client,
@@ -100,8 +227,11 @@
           period: 1s
           command: tail -1 server-input
 
-This is still a simple example, but worth going through carefully.
-It shows many of the building blocks used in real-world blueprints,
+
+#### Summary
+
+These examples are relatively simple example, but they
+illustrate many of the building blocks used in real-world blueprints,
 and how they can often be easily described and combined in Brooklyn YAML blueprints.
 Next, if you need to drive off-piste, or you want to write tests against these blueprints,
 have a look at, for example, `VanillaBashNetcatYamlTest.java` in the Brooklyn codebase,
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
new file mode 100644
index 0000000..7551818
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
@@ -0,0 +1,35 @@
+brooklyn.catalog:
+  id: netcat-example
+  version: "1.0"
+  item:
+    type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+    name: Simple Netcat Server
+
+    launch.command: |
+      echo $MESSAGE | nc -l $NETCAT_PORT &
+      echo $! > $PID_FILE
+        
+    env:
+      MESSAGE: $brooklyn:config("message")
+      NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+      
+    brooklyn.parameters:
+    - name: message
+      description: a message to send to the caller
+      default: hello
+    - name: netcat.port
+      type: port
+      description: the port netcat should run on
+      default: 4321+
+
+    brooklyn.enrichers:
+    - type: org.apache.brooklyn.enricher.stock.Transformer
+      brooklyn.config:
+        uniqueTag: main-uri-generator
+        enricher.sourceSensor: $brooklyn:sensor("host.address")
+        enricher.targetSensor: $brooklyn:sensor("main.uri")
+        enricher.targetValue:
+          $brooklyn:formatString:
+          - "http://%s:%s/"
+          - $brooklyn:attributeWhenReady("host.address")
+          - $brooklyn:attributeWhenReady("netcat.port")
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
new file mode 100644
index 0000000..70b69af
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
@@ -0,0 +1,11 @@
+name: Netcat Cluster Example
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.group.DynamicCluster
+  memberSpec:
+    $brooklyn:entitySpec:
+      type: netcat-example
+      message: hello from cluster member
+      netcat.port: 8000+
+  initialSize: 3
+  restartMode: parallel
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
new file mode 100644
index 0000000..f4e894f
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
@@ -0,0 +1,16 @@
+name: Netcat Example with Explicit Check and Stop Commands
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo hello | nc -l 4321 &
+    echo $! > $PID_FILE
+
+  # The following overrides demonstrate the use of a custom shell environment as well as
+  # check-running and stop commands. These are optional; default behavior will "do the
+  # right thing" with the pid file automatically.
+
+  env:                  { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
+  checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
+  stop.command:         echo $STOP_MARKER  >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
new file mode 100644
index 0000000..90f83b4
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
@@ -0,0 +1,21 @@
+name: Netcat Example with Parameter
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo $MESSAGE | nc -l $NETCAT_PORT &
+    echo $! > $PID_FILE
+    
+  env:
+    MESSAGE: $brooklyn:config("message")
+    NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+  
+  brooklyn.parameters:
+  - name: message
+    description: a message to send to the caller
+    default: hello
+  - name: netcat.port
+    type: port
+    description: the port netcat should run on
+    default: 4321+
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
new file mode 100644
index 0000000..3ec0212
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
@@ -0,0 +1,13 @@
+name: Netcat Example with Port Opened
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+  name: Simple Netcat Server
+  launch.command: |
+    echo hello | nc -l 4321 &
+    echo $! > $PID_FILE
+    
+  brooklyn.config:
+    # matching the regex `.*\.port` will cause the port to be opened
+    # if in a cloud where configurable security groups are available
+    netcat.port: 4321
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
new file mode 100644
index 0000000..0f10c55
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
@@ -0,0 +1,5 @@
+name: Netcat Type Reference Example
+location: localhost
+services:
+- type: netcat-example
+  message: hello from netcat using a registered type
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
index adaa933..47e54ab 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat Example with Restarter Policy
+name: Netcat Example with Restarter Policy
 location: localhost
 services:
 - type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
index 8290b79..78ed99e 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat with Client
+name: Netcat Example with Client
 
 location: localhost
 
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
index 3023038..df616af 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
@@ -6,13 +6,3 @@
   launch.command: |
     echo hello | nc -l 4321 &
     echo $! > $PID_FILE
-
-  # The following overrides demonstrate the use of a custom shell environment as well as
-  # check-running and stop commands. These are optional; default behavior will "do the
-  # right thing" with the pid file automatically.
-
-  env:                  { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
-  checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
-  stop.command:         echo $STOP_MARKER  >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
-
-# can also define download.url, in which case the launch command defaults to ./start.sh in that (archive) file
diff --git a/brooklyn-docs/guide/yaml/yaml-reference.md b/brooklyn-docs/guide/yaml/yaml-reference.md
index 36656bb..7a628a0 100644
--- a/brooklyn-docs/guide/yaml/yaml-reference.md
+++ b/brooklyn-docs/guide/yaml/yaml-reference.md
@@ -67,22 +67,32 @@
   * `description`: short text describing the parameter behaviour/usage, presented
     to the user
   * `type`: the type of the parameter, one of `string`, `integer`, `long`, `float`,
-    `double`, `timestamp`, `port`, a fully qualified Java type name. Default is `string`.
-  * `default`: a default value, converted to the type above
-  * `constraints`: a list of constraints the parameter should meet, currently
-    `required` is supported
+    `double`, `timestamp`, `duration`, `port`, or a fully qualified Java type name;
+    the default is `string`;
+    obvious coercion is supported so 
+    `timestamp` accepts most common ISO date formats, `duration` accepts `5m`, and port accepts `8080+`
+  * `default`: a default value; this will be coerced to the declared `type`
+  * `constraints`: a list of constraints the parameter should meet;
+    currently `required` is supported, with the default being not required
 
-  A shorthand notation is also supported where the name of the parameter is directly
-  passed as an item in the list. For example:
+  A shorthand notation is also supported where just the name of the parameter is supplied
+  as an item in the list, with the other values being unset or the default.
+  See `displayName` in the following example for an illustration of this:
 
 ~~~ yaml
 brooklyn.parameters:
-- displayName
-- name: user.name
-  constraints:
-  - required
+# user.age parameter is required, and fully specified
 - name: user.age
   type: integer
+  label: Age
+  description: the age of the user
+  constraints:
+  - required
+# user.name is optional, and has a default
+- name: user.name
+  default: You
+# shorthand notation: displayName will be an optional config of type string with no default
+- displayName
 ~~~
 
 Entities, policies, and initializers may accept additional key-value pairs,
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
index 4e9dc62..fd7047e 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
@@ -20,13 +20,23 @@
 
 import java.io.Serializable;
 
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 
+/** A wrapper around a {@link ConfigKey} which will be added to an {@link Entity},
+ * providing additional information for rendering in a UI */
 public interface SpecParameter<T> extends Serializable {
     /** Short name, to be used in UI */
     String getLabel();
-    /** Visible by default in UI, not all inputs may be visible at once */
+    /** Whether visible by default in UI, not all inputs may be visible at once */
     boolean isPinned();
-    /** Type information for the input */
-    ConfigKey<T> getType();
+    /** All config key info for this spec parameter;
+     * this is the config key which is added to the defined type */
+    ConfigKey<T> getConfigKey();
+    /** An optional sensor which may also be added to the defined type */
+    @Nullable AttributeSensor<?> getSensor();
+
 }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
index fdf5807..869ebc0 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
@@ -62,20 +62,20 @@
         SpecParameter<?> firstInput = inputs.get(0);
         assertEquals(firstInput.getLabel(), "simple");
         assertEquals(firstInput.isPinned(), true);
-        assertEquals(firstInput.getType().getName(), "simple");
-        assertEquals(firstInput.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(firstInput.getConfigKey().getName(), "simple");
+        assertEquals(firstInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
         
         SpecParameter<?> secondInput = inputs.get(1);
         assertEquals(secondInput.getLabel(), "explicit_name");
         assertEquals(secondInput.isPinned(), true);
-        assertEquals(secondInput.getType().getName(), "explicit_name");
-        assertEquals(secondInput.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(secondInput.getConfigKey().getName(), "explicit_name");
+        assertEquals(secondInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
         
         SpecParameter<?> thirdInput = inputs.get(2);
         assertEquals(thirdInput.getLabel(), "third_input");
         assertEquals(thirdInput.isPinned(), true);
-        assertEquals(thirdInput.getType().getName(), "third_input");
-        assertEquals(thirdInput.getType().getTypeToken(), TypeToken.of(Integer.class));
+        assertEquals(thirdInput.getConfigKey().getName(), "third_input");
+        assertEquals(thirdInput.getConfigKey().getTypeToken(), TypeToken.of(Integer.class));
     }
 
     @Test
@@ -99,8 +99,8 @@
         SpecParameter<?> firstInput = inputs.get(0);
         assertEquals(firstInput.getLabel(), "simple");
         assertTrue(firstInput.isPinned());
-        assertEquals(firstInput.getType().getName(), "simple");
-        assertEquals(firstInput.getType().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
+        assertEquals(firstInput.getConfigKey().getName(), "simple");
+        assertEquals(firstInput.getConfigKey().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
     }
 
     @Test
@@ -126,7 +126,7 @@
         SpecParameter<?> input = inputs.get(0);
         assertEquals(input.getLabel(), "more_config");
         assertFalse(input.isPinned());
-        assertEquals(input.getType().getName(), "more_config");
+        assertEquals(input.getConfigKey().getName(), "more_config");
     }
 
     private String add(String... def) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 2e59185..f158c2c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -49,7 +49,6 @@
 
 public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializable {
     
-    @SuppressWarnings("unused")
     private static final Logger log = LoggerFactory.getLogger(BasicConfigKey.class);
     private static final long serialVersionUID = -1762014059150215376L;
     
@@ -119,6 +118,13 @@
         public BasicConfigKey<T> build() {
             return new BasicConfigKey<T>(this);
         }
+        
+        public String getName() {
+            return name;
+        }
+        public String getDescription() {
+            return description;
+        }
     }
     
     private String name;
@@ -165,7 +171,7 @@
         this.constraint = Predicates.alwaysTrue();
     }
 
-    protected BasicConfigKey(Builder<T> builder) {
+    public BasicConfigKey(Builder<T> builder) {
         this.name = checkNotNull(builder.name, "name");
         this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(builder.type, "type"));
         this.typeToken = TypeTokens.getTypeTokenIfNotRaw(builder.type);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 1a273f6..c508349 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -28,10 +28,9 @@
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
-import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
index 63ea52d..9b10d1d 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
@@ -173,14 +173,26 @@
         return ConfigKeys.newConfigKey(paramType.getParameterClass(), paramType.getName(), paramType.getDescription(), paramType.getDefaultValue());
     }
 
-    /** returns an unsubmitted task which will invoke the given effector on the given entities;
-     * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+    /** convenience for {@link #invocationParallel(Effector, Map, Iterable)} */
     public static TaskAdaptable<List<?>> invocation(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+        return invocationParallel(eff, params, entities);
+    }
+    
+    /** returns an unsubmitted task which will invoke the given effector on the given entities in parallel;
+     * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+    public static TaskAdaptable<List<?>> invocationParallel(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
         List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
         for (Entity e: entities) tasks.add(invocation(e, eff, params));
         return Tasks.parallel("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
     }
 
+    /** as {@link #invocationParallel(Effector, Map, Iterable)} but executing sequentially */
+    public static TaskAdaptable<List<?>> invocationSequential(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+        List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
+        for (Entity e: entities) tasks.add(invocation(e, eff, params));
+        return Tasks.sequential("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
+    }
+
     /** returns an unsubmitted task which will invoke the given effector on the given entities
      * (this form of method is a convenience for {@link #invocation(Effector, Map, Iterable)}) */
     public static TaskAdaptable<List<?>> invocation(Effector<?> eff, MutableMap<?, ?> params, Entity ...entities) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index 6cb8bb4..1d4cfea 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -19,7 +19,6 @@
 
 package org.apache.brooklyn.core.objs;
 
-import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
@@ -28,8 +27,8 @@
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ValueResolver;
 import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.time.Duration;
 
 public abstract class AbstractConfigurationSupportInternal implements BrooklynObjectInternal.ConfigurationSupportInternal {
 
@@ -63,7 +62,7 @@
         Object resolved = Tasks.resolving(unresolved)
                 .as(Object.class)
                 .defaultValue(marker)
-                .timeout(Duration.of(5, TimeUnit.MILLISECONDS))
+                .timeout(ValueResolver.REAL_REAL_QUICK_WAIT)
                 .context(getContext())
                 .swallowExceptions()
                 .get();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
index dfbe1c1..2bc2346 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -36,12 +36,18 @@
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.BrooklynType;
 import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
+import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.Duration;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -54,14 +60,26 @@
 public class BasicSpecParameter<T> implements SpecParameter<T>{
     private static final long serialVersionUID = -4728186276307619778L;
 
-    private String label;
-    private boolean pinned;
-    private ConfigKey<T> type;
+    private final String label;
+    
+    /** pinning may become a priority or other more expansive indicator */
+    @Beta
+    private final boolean pinned;
+    
+    private final ConfigKey<T> configKey;
+    private final AttributeSensor<?> sensor;
 
-    public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> type) {
+    @Beta // TBD whether "pinned" stays
+    public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config) {
+        this(label, pinned, config, null);
+    }
+
+    @Beta // TBD whether "pinned" and "sensor" stay
+    public <SensorType> BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config, AttributeSensor<SensorType> sensor) {
         this.label = label;
         this.pinned = pinned;
-        this.type = type;
+        this.configKey = config;
+        this.sensor = sensor;
     }
 
     @Override
@@ -73,15 +91,20 @@
     public boolean isPinned() {
         return pinned;
     }
+    
+    @Override
+    public ConfigKey<T> getConfigKey() {
+        return configKey;
+    }
 
     @Override
-    public ConfigKey<T> getType() {
-        return type;
+    public AttributeSensor<?> getSensor() {
+        return sensor;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(label, pinned, type);
+        return Objects.hashCode(label, pinned, configKey);
     }
 
     @Override
@@ -95,7 +118,7 @@
         BasicSpecParameter<?> other = (BasicSpecParameter<?>) obj;
         return Objects.equal(label,  other.label) &&
                 pinned == other.pinned &&
-                Objects.equal(type, other.type);
+                Objects.equal(configKey, other.configKey);
     }
 
     @Override
@@ -103,7 +126,7 @@
         return Objects.toStringHelper(this)
                 .add("label", label)
                 .add("pinned", pinned)
-                .add("type", type)
+                .add("type", configKey)
                 .toString();
     }
 
@@ -154,6 +177,7 @@
                 .put("long", Long.class)
                 .put("float", Float.class)
                 .put("double", Double.class)
+                .put("duration", Duration.class)
                 .put("timestamp", Date.class)
                 .put("port", PortRange.class)
                 .build();
@@ -191,13 +215,23 @@
                 throw new IllegalArgumentException("'name' value missing from input definition " + obj + " but is required. Check for typos.");
             }
 
-            ConfigKey inputType = BasicConfigKey.builder(inferType(type, loader))
-                    .name(name)
-                    .description(description)
-                    .defaultValue(defaultValue)
-                    .constraint(constraints)
-                    .build();
-            return new BasicSpecParameter(Objects.firstNonNull(label, name), true, inputType);
+            ConfigKey configType;
+            AttributeSensor sensorType = null;
+            
+            TypeToken typeToken = inferType(type, loader);
+            Builder builder = BasicConfigKey.builder(typeToken)
+                .name(name)
+                .description(description)
+                .defaultValue(defaultValue)
+                .constraint(constraints);
+            
+            if (PortRange.class.equals(typeToken.getRawType())) {
+                sensorType = new PortAttributeSensorAndConfigKey(builder);
+                configType = ((HasConfigKey)sensorType).getConfigKey();
+            } else {
+                configType = builder.build();
+            }
+            return new BasicSpecParameter(Objects.firstNonNull(label, name), true, configType, sensorType);
         }
 
         @SuppressWarnings({ "rawtypes" })
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 2da7463..89c4e32 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -69,6 +69,9 @@
         /**
          * Returns the uncoerced value for this config key, if available, not taking any default.
          * If there is no local value and there is an explicit inherited value, will return the inherited.
+         * Returns {@link Maybe#absent()} if the key is not explicitly set on this object or an ancestor.
+         * <p>
+         * See also {@link #getLocalRaw(ConfigKey).
          */
         @Beta
         Maybe<Object> getRaw(ConfigKey<?> key);
@@ -82,6 +85,9 @@
         /**
          * Returns the uncoerced value for this config key, if available,
          * not following any inheritance chains and not taking any default.
+         * Returns {@link Maybe#absent()} if the key is not explicitly set on this object.
+         * <p>
+         * See also {@link #getRaw(ConfigKey).
          */
         @Beta
         Maybe<Object> getLocalRaw(ConfigKey<?> key);
@@ -96,6 +102,11 @@
          * Attempts to coerce the value for this config key, if available,
          * taking a default and {@link Maybe#absent absent} if the uncoerced
          * cannot be resolved within a short timeframe.
+         * <p>
+         * Note: if no value for the key is available, not even as a default,
+         * this returns a {@link Maybe#isPresent()} containing <code>null</code>
+         * (following the semantics of {@link #get(ConfigKey)} 
+         * rather than {@link #getRaw(ConfigKey)}).
          */
         @Beta
         <T> Maybe<T> getNonBlocking(ConfigKey<T> key);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index eb4ff10..e55d6d9 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -44,6 +44,7 @@
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityDynamicType;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
@@ -248,13 +249,13 @@
             }
             
             entity.tags().addTags(spec.getTags());
-            ((AbstractEntity)entity).configure(getConfigKeysFromSpecParameters(spec));
-            ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
+            addSpecParameters(spec, ((AbstractEntity)entity).getMutableEntityType());
             
+            ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
             for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
                 entity.config().set((ConfigKey)entry.getKey(), entry.getValue());
             }
-
+            
             Entity parent = spec.getParent();
             if (parent != null) {
                 parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent;
@@ -268,12 +269,11 @@
         }
     }
 
-    private <T extends Entity> List<ConfigKey<?>> getConfigKeysFromSpecParameters(EntitySpec<T> spec) {
-        List<ConfigKey<?>> configKeys = MutableList.of();
+    private void addSpecParameters(EntitySpec<?> spec, EntityDynamicType edType) {
         for (SpecParameter<?> param : spec.getParameters()) {
-            configKeys.add(param.getType());
+            edType.addConfigKey(param.getConfigKey());
+            if (param.getSensor()!=null) edType.addSensor(param.getSensor());
         }
-        return configKeys;
     }
 
     /**
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
index f76baaa..2a26fa7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
@@ -19,11 +19,11 @@
 package org.apache.brooklyn.core.sensor;
 
 import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
@@ -45,6 +45,7 @@
 */
 public abstract class AttributeSensorAndConfigKey<ConfigType,SensorType> extends BasicAttributeSensor<SensorType> 
         implements ConfigKey.HasConfigKey<ConfigType> {
+    
     private static final long serialVersionUID = -3103809215973264600L;
     private static final Logger log = LoggerFactory.getLogger(AttributeSensorAndConfigKey.class);
 
@@ -87,6 +88,10 @@
         configKey = ConfigKeys.newConfigKeyWithDefault(orig.configKey, 
                 TypeCoercions.coerce(defaultValue, orig.configKey.getTypeToken()));
     }
+    public AttributeSensorAndConfigKey(Builder<ConfigType> configKeyBuilder, TypeToken<SensorType> sensorType) {
+        super(sensorType, configKeyBuilder.getName(), configKeyBuilder.getDescription());
+        configKey = new BasicConfigKey<ConfigType>(configKeyBuilder);
+    }
 
     public ConfigKey<ConfigType> getConfigKey() { return configKey; }
     
@@ -106,7 +111,7 @@
         SensorType sensorValue = e.getAttribute(this);
         if (sensorValue!=null) return sensorValue;
         
-        ConfigType v = ((EntityLocal)e).getConfig(this);
+        ConfigType v = e.config().get(this);
         try {
             return convertConfigToSensor(v, e);
         } catch (Throwable t) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
index 3de0de6..aa396d2 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
@@ -29,6 +29,7 @@
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.location.Locations;
@@ -40,6 +41,7 @@
 import com.google.common.base.Optional;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
 
 /**
  * A {@link Sensor} describing a port on a system,
@@ -68,6 +70,10 @@
     public PortAttributeSensorAndConfigKey(PortAttributeSensorAndConfigKey orig, Object defaultValue) {
         super(orig, TypeCoercions.coerce(defaultValue, PortRange.class));
     }
+    public PortAttributeSensorAndConfigKey(BasicConfigKey.Builder<PortRange> builder) {
+        super(builder, TypeToken.of(Integer.class));
+    }
+    
     @Override
     protected Integer convertConfigToSensor(PortRange value, Entity entity) {
         if (value==null) return null;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
index 781cb0c..f09e72f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
@@ -98,6 +98,14 @@
 
     MethodEffector<Collection<Entity>> RESIZE_BY_DELTA = new MethodEffector<Collection<Entity>>(DynamicCluster.class, "resizeByDelta");
 
+    @SetFromFlag("restartMode")
+    ConfigKey<String> RESTART_MODE = ConfigKeys.newStringConfigKey(
+            "dynamiccluster.restartMode", 
+            "How this cluster should handle restarts; "
+            + "by default it is disallowed, but this key can specify a different mode. "
+            + "Modes supported by dynamic cluster are 'off', 'sequqential', or 'parallel'. "
+            + "However subclasses can define their own modes or may ignore this.", null);
+
     @SetFromFlag("quarantineFailedEntities")
     ConfigKey<Boolean> QUARANTINE_FAILED_ENTITIES = ConfigKeys.newBooleanConfigKey(
             "dynamiccluster.quarantineFailedEntities", "If true, will quarantine entities that fail to start; if false, will get rid of them (i.e. delete them)", true);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index f434fcf..aaf06c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -47,6 +47,7 @@
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.effector.Effectors;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.factory.EntityFactory;
 import org.apache.brooklyn.core.entity.factory.EntityFactoryForLocation;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
@@ -522,7 +523,28 @@
 
     @Override
     public void restart() {
-        throw new UnsupportedOperationException();
+        String mode = getConfig(RESTART_MODE);
+        if (mode==null) {
+            throw new UnsupportedOperationException("Restart not supported for this cluster: "+RESTART_MODE.getName()+" is not configured.");
+        }
+        if ("off".equalsIgnoreCase(mode)) {
+            throw new UnsupportedOperationException("Restart not supported for this cluster.");
+        }
+        
+        if ("sequential".equalsIgnoreCase(mode)) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            DynamicTasks.queue(Effectors.invocationSequential(Startable.RESTART, null, 
+                Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+        } else if ("parallel".equalsIgnoreCase(mode)) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            DynamicTasks.queue(Effectors.invocationParallel(Startable.RESTART, null, 
+                Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+        } else {
+            throw new IllegalArgumentException("Unknown "+RESTART_MODE.getName()+" '"+mode+"'");
+        }
+        
+        DynamicTasks.waitForLast();
+        ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
     }
 
     @Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
index 85c9537..3bc22e0 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
@@ -234,7 +234,7 @@
     public static List<FlagConfigKeyAndValueRecord> findAllParameterConfigKeys(List<SpecParameter<?>> parameters, ConfigBag input) {
         List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
         for (SpecParameter<?> param : parameters) {
-            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getType(), input);
+            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getConfigKey(), input);
             if (record.isValuePresent())
                 output.add(record);
         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index f850fbd..872e713 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -122,10 +122,10 @@
         return coerce(value, TypeToken.of(targetType));
     }
 
-    /** @see #coerce(Object, Class) */
+    /** @see #coerce(Object, Class); allows a null value in the contents of the Maybe */
     public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
         try {
-            return Maybe.of( coerce(value, targetTypeToken) );
+            return Maybe.ofAllowingNull( coerce(value, targetTypeToken) );
         } catch (Throwable t) {
             Exceptions.propagateIfFatal(t);
             return Maybe.absent(t); 
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
index 2809482..0f85c3f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
@@ -42,6 +42,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -57,14 +58,25 @@
  */
 public class ValueResolver<T> implements DeferredSupplier<T> {
 
+    // TODO most of these usages should be removed when we have
+    // an ability to run resolution in a non-blocking mode
+    // (i.e. running resolution tasks in the same thread,
+    // or in a context where they can only wait on subtasks
+    // which are guaranteed to have the same constraint)
     /** 
      * Period to wait if we're expected to return real quick 
      * but we want fast things to have time to finish.
      * <p>
      * Timings are always somewhat arbitrary but this at least
      * allows some intention to be captured in code rather than arbitrary values. */
+    @Beta
     public static Duration REAL_QUICK_WAIT = Duration.millis(50);
     /** 
+     * Like {@link #REAL_QUICK_WAIT} but even smaller, for use when potentially
+     * resolving multiple items in sequence. */
+    @Beta
+    public static Duration REAL_REAL_QUICK_WAIT = Duration.millis(5);
+    /** 
      * Period to wait if we're expected to return quickly 
      * but we want to be a bit more generous for things to finish,
      * without letting a caller get annoyed. 
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
index 49cb2d6..30745f0 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
@@ -103,7 +103,7 @@
     private void assertInput(SpecParameter<?> input, String label, boolean pinned, ConfigKey<?> type) {
         assertEquals(input.getLabel(), label);
         assertEquals(input.isPinned(), pinned);
-        assertEquals(input.getType(), type);
+        assertEquals(input.getConfigKey(), type);
     }
 
 }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
index 9f2eaaf..07ad81a 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
@@ -56,7 +56,7 @@
         SpecParameter<?> input = parse(name);
         assertEquals(input.getLabel(), name);
         assertTrue(input.isPinned());
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getTypeToken(), TypeToken.of(String.class));
         assertNull(type.getDefaultValue());
@@ -70,8 +70,8 @@
         String name = "minRam";
         SpecParameter<?> input = parse(ImmutableMap.of("name", name));
         assertEquals(input.getLabel(), name);
-        assertEquals(input.getType().getName(), name);
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(String.class));
+        assertEquals(input.getConfigKey().getName(), name);
+        assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(String.class));
     }
 
     @Test
@@ -99,7 +99,7 @@
         assertEquals(input.getLabel(), label);
         assertTrue(input.isPinned());
 
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getTypeToken(), TypeToken.of(String.class));
         assertEquals(type.getDefaultValue(), defaultValue);
@@ -123,7 +123,7 @@
         assertEquals(input.getLabel(), name);
         assertTrue(input.isPinned());
 
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertEquals(type.getName(), name);
         assertEquals(type.getDefaultValue(), defaultValue);
         assertEquals(type.getDescription(), description);
@@ -137,7 +137,7 @@
         SpecParameter<?> input = parse(ImmutableMap.of(
                 "name", name,
                 "constraints", ImmutableList.of(constraint)));
-        ConfigKey<?> type = input.getType();
+        ConfigKey<?> type = input.getConfigKey();
         assertConstraint(type.getConstraint(), StringPredicates.isNonBlank());
     }
 
@@ -153,7 +153,7 @@
         SpecParameter<?> input = parse(ImmutableMap.of(
                 "name", name,
                 "type", BasicSpecParameterFromListTest.class.getName()));
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
+        assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
     }
 
     @Test(expectedExceptions = IllegalArgumentException.class)
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
index 74625fd..514d9c9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
@@ -68,15 +68,21 @@
         Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator());
 
         EntitySpec<?> spec = null;
-
         try {
-            spec = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            // the raw type isn't needed according to eclipse IDE, but jenkins maven fails without it;
+            // must be a java version or compiler thing. don't remove even though it looks okay without it!
+            EntitySpec<?> specRaw = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+            spec = specRaw;
             EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType());
             EntityType type = typeMap.getSnapshot();
 
             AtomicInteger paramPriorityCnt = new AtomicInteger();
-            for (SpecParameter<?> input: spec.getParameters())
+            for (SpecParameter<?> input: spec.getParameters()) {
                 config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt));
+                if (input.getSensor()!=null)
+                    sensors.add(SensorTransformer.sensorSummaryForCatalog(input.getSensor()));
+            }
             for (Sensor<?> x: type.getSensors())
                 sensors.add(SensorTransformer.sensorSummaryForCatalog(x));
             for (Effector<?> x: type.getEffectors())
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 2d9f8a0..f5079b9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -159,7 +159,7 @@
         // which results in correctly ordered items on the wire (as a list). Clients which use the java bindings
         // though will push the items in an unordered set - so give them means to recover the correct order.
         Double priority = input.isPinned() ? Double.valueOf(paramPriorityCnt.incrementAndGet()) : null;
-        return entityConfigSummary(input.getType(), input.getLabel(), priority, null);
+        return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
     }
 
 }
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
index 91aa294..cb4149a 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
@@ -118,7 +118,8 @@
         entity().initDriver(machine);
 
         // Note: must only apply config-sensors after adding to locations and creating driver; 
-        // otherwise can't do things like acquire free port from location, or allowing driver to set up ports
+        // otherwise can't do things like acquire free port from location
+        // or allowing driver to set up ports; but must be careful in init not to block on these!
         super.preStartCustom(machine);
         
         entity().preStart();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
index dd01a69..9c955ed 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
@@ -21,11 +21,12 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal.ConfigurationSupportInternal;
 import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -54,15 +55,30 @@
      */
     @Override
     protected String getInstallLabelExtraSalt() {
+        // run non-blocking in case a value set later is used (e.g. a port)
+        Integer hash = hashCodeIfResolved(SoftwareProcess.DOWNLOAD_URL.getConfigKey(), 
+            VanillaSoftwareProcess.INSTALL_COMMAND, SoftwareProcess.SHELL_ENVIRONMENT);
         
-        Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);
-        String installCommand = getEntity().getConfig(VanillaSoftwareProcess.INSTALL_COMMAND);
-        Map<String, Object> shellEnv = getEntity().getConfig(VanillaSoftwareProcess.SHELL_ENVIRONMENT);
+        // if any of the above blocked then we must make a unique install label,
+        // as other yet-unknown config is involved 
+        if (hash==null) return Identifiers.makeRandomId(8);
         
-        // TODO a user-friendly hash would be nice, but tricky since we don't want it to be too long or contain path chars
-        return Identifiers.makeIdFromHash(Objects.hash(url.or(null), installCommand, shellEnv));
+        // a user-friendly hash is nice, but tricky since it would have to be short; 
+        // go with a random one unless it's totally blank
+        if (hash==0) return "default";
+        return Identifiers.makeIdFromHash(hash);
     }
     
+    private Integer hashCodeIfResolved(ConfigKey<?> ...keys) {
+        int hash = 0;
+        for (ConfigKey<?> k: keys) {
+            Maybe<?> value = ((ConfigurationSupportInternal)getEntity().config()).getNonBlocking(k);
+            if (value.isAbsent()) return null;
+            hash = hash*31 + (value.get()==null ? 0 : value.get().hashCode());
+        }
+        return hash;
+    }
+
     @Override
     public void install() {
         Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);