diff --git a/modules/aggregator/config-templates/aggregator.php b/modules/aggregator/config-templates/module_aggregator.php
similarity index 61%
rename from modules/aggregator/config-templates/aggregator.php
rename to modules/aggregator/config-templates/module_aggregator.php
index e17552425fc9468846744effa3fb224e72f3adbc..1c2d851c00a949a14b2acdaf86e042ee216f2673 100644
--- a/modules/aggregator/config-templates/aggregator.php
+++ b/modules/aggregator/config-templates/module_aggregator.php
@@ -6,19 +6,28 @@ $config = array(
 	/* List of aggregators. */
 	'aggregators' => array(
 		'example' => array(
-			array('type' => 'flatfile'),  /* Metadata from metadata-directory. */
-			array('type' => 'xml', 'url' => 'https://idp.example.org/Metadata'),
-			array('type' => 'xml', 'file' => 'static-metadata.xml'),
+			'source' => array(
+				array('type' => 'flatfile'),  /* Metadata from metadata-directory. */
+				array('type' => 'xml', 'url' => 'https://idp.example.org/Metadata'),
+				array('type' => 'xml', 'file' => 'static-metadata.xml'),
+			),
 		),
+		'example2' => array(
+			'source' => array(
+				array('type' => 'xml', 'url' => 'https://idp.example.org/Metadata2'),
+			),
+			'set' => 'saml2',
+			'sign.privatekey' => 'server2.key',
+			'sign.certificate' => 'server2.crt',
+		)
 	),
 
 	
-	'maxCache' 		=> 60*60*24, // 24 hour cache time
 	'maxDuration' 	=> 60*60*24*5, // Maximum 5 days duration on ValidUntil.
 
 	// If base64 encoded for entity is already cached in the entity, should we
 	// reconstruct the XML or re-use.
-	'reconstruct' => TRUE,
+	'reconstruct' => FALSE,
 
 	/* Whether metadata should be signed. */
 	'sign.enable' => FALSE,
@@ -34,4 +43,3 @@ $config = array(
 
 );
 
-?>
\ No newline at end of file
diff --git a/modules/aggregator/docs/aggregator.txt b/modules/aggregator/docs/aggregator.txt
new file mode 100644
index 0000000000000000000000000000000000000000..14f5042a30853b0768323f36b5a8179f44670e37
--- /dev/null
+++ b/modules/aggregator/docs/aggregator.txt
@@ -0,0 +1,90 @@
+aggregator Module
+=================
+
+<!--
+	This file is written in Markdown syntax.
+	For more information about how to use the Markdown syntax, read here:
+	http://daringfireball.net/projects/markdown/syntax
+-->
+
+  * Version: `$Id$`
+  * Author: Andreas Ă…kre Solberg <andreas.solberg@uninett.no>, UNINETT AS
+  * Package: simpleSAMLphp
+
+This module, aggregates a set of metadata of SAML entities to SAML 2.0 documents with an `EntitiesDescriptor` with multiple entities inside.
+
+Multiple aggregates can be configured.
+
+
+
+The configuration file: module_aggregate.php
+--------------------------------------------
+
+The configuration file includes an option `aggregators`, which includes a indexed list of different aggregator configurations that all can be accessed independently. The structure is as follows:
+
+	'aggregators' => array(
+		'aggr1' => array(
+			'sources' => [...]
+			[...local params...]
+		),
+		'aggr2' => ...
+	)
+	[...global params...]
+
+All of the global parameters can be overriden for each aggregator. Here is a list of the available (global) paramters:
+
+`set`
+: By default all SAML types are available, including: `array('saml20-idp-remote', 'saml20-sp-remote', 'shib13-idp-remote', 'shib13-sp-remote')`. This list can be reduced by specifying one of the following values:
+
+  * `saml20-idp-remote`
+  * `saml20-sp-remote`
+  * `shib13-idp-remote`
+  * `shib13-sp-remote`
+  * `saml2`
+  * `shib13`
+
+`foo`
+: sldkfjdslkjf
+
+`reconstruct`
+: Whether simpleSAMLphp should regenerate the metadata XML (TRUE) or pass-through the input metadata XML (FALSE).
+
+`maxDuration`
+: Max validity of metadata (duration) in seconds.
+
+`sign.enable`
+: Enable signing of metadata document
+
+`sign.privatekey`
+: Private key to use when signing
+
+`sign.privatekey_pass`
+: Optionally a passphrase to the private key
+
+`sign.certificate`
+: Certificate to embed, corresponding to the private key.
+
+
+Accessing the aggregate
+-----------------------
+
+On the SimpleSAMLphp frontpage on the federation tab, there is a link to the aggregator named *Metadata aggregator*.
+
+When accessing the aggregator endpoint without specifying an aggregate ID, a list of available aggregators will be presented, with different options for mime-type presenting the result.
+
+The endpoint supports the following query parameter:
+
+`id`
+: The ID of the aggregator (From configuration file)
+
+`set`
+: Subset the available types of SAML entities. Similar to the `set` parameter described over in the configuration file description.
+
+`exclude`
+: Specify a `tag` that will be excluded from the metadata set. Useful for leaving out your own federation metadata.
+
+`mimetype`
+: Select Mime-Type that will be used. Default is `application/samlmetadata+xml`.
+
+
+
diff --git a/modules/aggregator/lib/Aggregator.php b/modules/aggregator/lib/Aggregator.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f3474b58dfd959afc4556f30e30bf62f9560d7c
--- /dev/null
+++ b/modules/aggregator/lib/Aggregator.php
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * Aggregates metadata for multiple sources into one signed file
+ *
+ * @author Andreas Ă…kre Solberg <andreas.solberg@uninett.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+class sspmod_aggregator_Aggregator {
+
+	// Configuration for the whole aggregator module
+	private $gConfig;
+	
+	// Configuration for the specific aggregate
+	private $aConfig;
+	
+	private $sets;
+	
+	private $excludeTags = array();
+	
+	private $id;
+
+	/**
+	 * Constructor for the Aggregator.
+	 *
+	 */
+	public function __construct($gConfig, $aConfig, $id) {
+		$this->gConfig = $gConfig;
+		$this->aConfig = $aConfig;
+		$this->id = $id;
+		
+		$this->sets = array('saml20-idp-remote', 'saml20-sp-remote', 'shib13-idp-remote', 'shib13-sp-remote');
+		
+		if ($this->aConfig->hasValue('set')) {
+			$this->limitSets($this->aConfig->getString('set'));
+		}
+	}
+
+
+
+	public function limitSets($set) {
+
+		
+		if (is_array($set)) {
+			$this->sets = array_intersect($this->sets, $set);
+			return;
+		}
+		
+		switch($set) {
+			case 'saml2' :
+				$this->sets = array_intersect($this->sets, array('saml20-idp-remote', 'saml20-sp-remote')); break;
+			case 'shib13' :
+				$this->sets = array_intersect($this->sets, array('shib13-idp-remote', 'shib13-sp-remote')); break;
+			case 'idp' :
+				$this->sets = array_intersect($this->sets, array('saml20-idp-remote', 'shib13-idp-remote')); break;
+			case 'sp' :
+				$this->sets = array_intersect($this->sets, array('saml20-sp-remote', 'shib13-sp-remote')); break;
+
+			default:
+				$this->sets = array_intersect($this->sets, array($set));
+		}
+	}
+
+	/**
+	 * Add tag to excelude when collecting source metadata.
+	 * 
+	 * $exclude 	May be string or array identifying a tag to exclude.
+	 */
+	public function exclude($exclude) {
+		$this->excludeTags = array_merge($this->excludeTags, SimpleSAML_Utilities::arrayize($exclude));
+	}
+	
+	/**
+	 * Returns a list of entities with metadata
+	 */ 
+	public function getSources() {
+		
+		$sourcesDef = $this->aConfig->getArray('sources');
+
+		try {
+			$sources = SimpleSAML_Metadata_MetaDataStorageSource::parseSources($sourcesDef);
+		} catch (Exception $e) {
+			throw new Exception('Invalid aggregator source configuration for aggregator ' .
+				var_export($id, TRUE) . ': ' . $e->getMessage());
+		}
+
+
+		#echo $exclude; exit;
+		/* Find list of all available entities. */
+		$entities = array();
+		#echo '<pre>'; print_r($this->sets); exit;
+		
+		foreach ($sources as $source) {
+			foreach ($this->sets as $set) {
+				
+				foreach ($source->getMetadataSet($set) as $entityId => $metadata) {
+					
+					if (isset($metadata['tags']) && 
+							count(array_intersect($this->excludeTags, $metadata['tags'])) > 0) {
+						
+						SimpleSAML_Logger::debug('Excluding entity ID [' . $entityId . '] becuase it is tagged with one of [' . 
+							var_export($this->excludeTags, TRUE) . ']');
+						continue;
+					} else {
+						#echo('<pre>'); print_r($metadata); exit;
+					}
+					if (!array_key_exists($entityId, $entities)) 
+						$entities[$entityId] = array();
+
+					if (array_key_exists($set, $entities[$entityId])) {
+						/* Entity already has metadata for the given set. */
+						continue;
+					}
+
+					$entities[$entityId][$set] = $metadata;
+				}
+			}
+		}
+		return $entities;
+		
+	}
+	
+	public function getMaxDuration() {
+		if ($this->aConfig->hasValue('maxDuration'))
+			return $this->aConfig->getInteger('maxDuration');
+		if ($this->gConfig->hasValue('maxDuration'))
+			return $this->gConfig->getInteger('maxDuration');
+		return NULL;
+	}
+	
+	
+	
+	public function getReconstruct() {
+		if ($this->aConfig->hasValue('reconstruct'))
+			return $this->aConfig->getBoolean('reconstruct');
+		if ($this->gConfig->hasValue('reconstruct'))
+			return $this->gConfig->getBoolean('reconstruct');
+		return FALSE;
+	}
+
+	public function shouldSign() {
+		if ($this->aConfig->hasValue('sign.enable'))
+			return $this->aConfig->getBoolean('sign.enable');
+		if ($this->gConfig->hasValue('sign.enable'))
+			return $this->gConfig->getBoolean('sign.enable');
+		return FALSE;
+	}
+	
+	
+	public function getSigningInfo() {
+		if ($this->aConfig->hasValue('sign.privatekey')) {
+			return array(
+				'privatekey' => $this->aConfig->getString('sign.privatekey'),
+				'privatekey_pass' => $this->aConfig->getString('sign.privatekey_pass', NULL),
+				'certificate' => $this->aConfig->getString('sign.certificate'),
+				'id' => 'ID'
+			);
+		}
+		
+		return array(
+			'privatekey' => $this->gConfig->getString('sign.privatekey'),
+			'privatekey_pass' => $this->gConfig->getString('sign.privatekey_pass', NULL),
+			'certificate' => $this->gConfig->getString('sign.certificate'),
+			'id' => 'ID'
+		);
+	}
+	
+
+	
+	public function getMetadataDocument() {
+
+		// Get metadata entries
+		$entities = $this->getSources();
+		
+		
+		// Generate XML Document
+		$xml = new DOMDocument();
+		$entitiesDescriptor = $xml->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'EntitiesDescriptor');
+		$entitiesDescriptor->setAttribute('Name', $this->id);
+		$xml->appendChild($entitiesDescriptor);
+		
+		
+		$maxDuration = $this->getMaxDuration();
+		$reconstruct = $this->getReconstruct();
+
+
+		/* Build EntityDescriptor elements for them. */
+		foreach ($entities as $entity => $sets) {
+
+			$entityDescriptor = NULL;
+			foreach ($sets as $set => $metadata) {
+				if (!array_key_exists('entityDescriptor', $metadata)) {
+					/* One of the sets doesn't contain an EntityDescriptor element. */
+					$entityDescriptor = FALSE;
+					break;
+				}
+
+				if ($entityDescriptor == NULL) {
+					/* First EntityDescriptor elements. */
+					$entityDescriptor = $metadata['entityDescriptor'];
+					continue;
+				}
+
+				assert('is_string($entityDescriptor)');
+				if ($entityDescriptor !== $metadata['entityDescriptor']) {
+					/* Entity contains multiple different EntityDescriptor elements. */
+					$entityDescriptor = FALSE;
+					break;
+				}
+			}
+
+			if (is_string($entityDescriptor) && !$reconstruct) {
+				/* All metadata sets for the entity contain the same entity descriptor. Use that one. */
+				$tmp = new DOMDocument();
+				$tmp->loadXML(base64_decode($entityDescriptor));
+				$entityDescriptor = $tmp->documentElement;
+			} else {
+				
+				$tmp = new SimpleSAML_Metadata_SAMLBuilder($entity, $maxDuration, $maxDuration);
+
+				$orgmeta = NULL;
+				foreach ($sets as $set => $metadata) {
+					$tmp->addMetadata($set, $metadata);
+					$orgmeta = $metadata;
+				}
+				$tmp->addOrganizationInfo($orgmeta);
+				$entityDescriptor = $tmp->getEntityDescriptor();
+			}
+
+			$entitiesDescriptor->appendChild($xml->importNode($entityDescriptor, TRUE));
+		}
+		
+		
+		/* Sign the metadata if enabled. */
+		if ($this->shouldSign()) {
+			$signer = new SimpleSAML_XML_Signer($this->getSigningInfo());
+			$signer->sign($entitiesDescriptor, $entitiesDescriptor, $entitiesDescriptor->firstChild);
+		}
+		
+
+		return $xml;
+	}
+	
+
+}
diff --git a/modules/aggregator/www/index.php b/modules/aggregator/www/index.php
index 36a34dc14812f3fab0dde0b1ef4ff95f2b7c9508..618abfb8f8497ba54754c5d92831578524cc8187 100644
--- a/modules/aggregator/www/index.php
+++ b/modules/aggregator/www/index.php
@@ -1,159 +1,36 @@
 <?php
 
-$globalConfig = SimpleSAML_Configuration::getInstance();
-$aggregatorConfig = SimpleSAML_Configuration::getConfig('aggregator.php');
+$config = SimpleSAML_Configuration::getInstance();
+$gConfig = SimpleSAML_Configuration::getConfig('module_aggregator.php');
 
 
-$reconstruct = $aggregatorConfig->getBoolean('reconstruct', FALSE);
-$aggregators = $aggregatorConfig->getArray('aggregators');
-
-$metadataSets = array('saml20-idp-remote', 'saml20-sp-remote', 'shib13-idp-remote', 'shib13-sp-remote');
-if ($aggregatorConfig->hasValue('default-set')) {
-	$metadataSets = $aggregatorConfig->getArray('default-set');
-}
-if (isset($_REQUEST['set'])) {
-	switch($_REQUEST['set']) {
-		case 'saml2' :
-			$metadataSets = array('saml20-idp-remote', 'saml20-sp-remote'); break;
-		case 'shib13' :
-			$metadataSets = array('shib13-idp-remote', 'shib13-sp-remote'); break;
-		case 'idp' :
-			$metadataSets = array('saml20-idp-remote', 'shib13-idp-remote'); break;
-		case 'sp' :
-			$metadataSets = array('saml20-sp-remote', 'shib13-sp-remote'); break;
-		
-		default:
-			$metadataSets = array($_REQUEST['set']);
-	}
-}
-
-// print_r($metadataSets); exit;
+// Get list of aggregators
+$aggregators = $gConfig->getConfigItem('aggregators');
 
+// If aggregator ID is not provided, show the list of available aggregates
 if (!array_key_exists('id', $_GET)) {
-	$t = new SimpleSAML_XHTML_Template($globalConfig, 'aggregator:list.php');
-	$t->data['sources'] = array_keys($aggregators);
+	$t = new SimpleSAML_XHTML_Template($config, 'aggregator:list.php');
+	$t->data['sources'] = $aggregators->getOptions();
 	$t->show();
 	exit;
 }
-
 $id = $_GET['id'];
-if (!array_key_exists($id, $aggregators)) {
+if (!in_array($id, $aggregators->getOptions())) 
 	throw new SimpleSAML_Error_NotFound('No aggregator with id ' . var_export($id, TRUE) . ' found.');
-}
 
+$aConfig = $aggregators->getConfigItem($id);
 
-/* Parse metadata sources. */
-$sources = $aggregators[$id];
-if (!is_array($sources)) {
-	throw new Exception('Invalid aggregator source configuration for aggregator ' .
-		var_export($id, TRUE) . ': Aggregator wasn\'t an array.');
-};
 
-try {
-	$sources = SimpleSAML_Metadata_MetaDataStorageSource::parseSources($sources);
-} catch (Exception $e) {
-	throw new Exception('Invalid aggregator source configuration for aggregator ' .
-		var_export($id, TRUE) . ': ' . $e->getMessage());
-}
+$aggregator = new sspmod_aggregator_Aggregator($gConfig, $aConfig, $id);
 
-$exclude = NULL;
-if (array_key_exists('exclude', $_REQUEST)) $exclude = $_REQUEST['exclude'];
-
-#echo $exclude; exit;
-/* Find list of all available entities. */
-$entities = array();
-foreach ($sources as $source) {
-	foreach ($metadataSets as $set) {
-		foreach ($source->getMetadataSet($set) as $entityId => $metadata) {
-			if (isset($exclude) && 
-					array_key_exists('tags', $metadata) && 
-					in_array($exclude, $metadata['tags'])) {
-				SimpleSAML_Logger::debug('Excluding entity ID [' . $entityId . '] becuase it is tagged with [' . $exclude . ']');
-				continue;
-			} else {
-				#echo('<pre>'); print_r($metadata); exit;
-			}
-			if (!array_key_exists($entityId, $entities)) 
-				$entities[$entityId] = array();
-				
-			if (array_key_exists($set, $entities[$entityId])) {
-				/* Entity already has metadata for the given set. */
-				continue;
-			}
-			
-			$entities[$entityId][$set] = $metadata;
-		}
-	}
-}
+if (isset($_REQUEST['set'])) 
+	$aggregator->limitSets($_REQUEST['set']);
 
-$xml = new DOMDocument();
-$entitiesDescriptor = $xml->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'EntitiesDescriptor');
-$entitiesDescriptor->setAttribute('Name', $id);
-$xml->appendChild($entitiesDescriptor);
-
-
-
-/* Build EntityDescriptor elements for them. */
-foreach ($entities as $entity => $sets) {
-
-	$entityDescriptor = NULL;
-	foreach ($sets as $set => $metadata) {
-		if (!array_key_exists('entityDescriptor', $metadata)) {
-			/* One of the sets doesn't contain an EntityDescriptor element. */
-			$entityDescriptor = FALSE;
-			break;
-		}
-
-		if ($entityDescriptor == NULL) {
-			/* First EntityDescriptor elements. */
-			$entityDescriptor = $metadata['entityDescriptor'];
-			continue;
-		}
-
-		assert('is_string($entityDescriptor)');
-		if ($entityDescriptor !== $metadata['entityDescriptor']) {
-			/* Entity contains multiple different EntityDescriptor elements. */
-			$entityDescriptor = FALSE;
-			break;
-		}
-	}
-
-	if (is_string($entityDescriptor) && !$reconstruct) {
-		/* All metadata sets for the entity contain the same entity descriptor. Use that one. */
-		$tmp = new DOMDocument();
-		$tmp->loadXML(base64_decode($entityDescriptor));
-		$entityDescriptor = $tmp->documentElement;
-	} else {
-		$tmp = new SimpleSAML_Metadata_SAMLBuilder($entity, 
-			$aggregatorConfig->getValue('maxCache', NULL), $aggregatorConfig->getValue('maxDuration', NULL));
-		
-		$orgmeta = NULL;
-		foreach ($sets as $set => $metadata) {
-			$tmp->addMetadata($set, $metadata);
-			$orgmeta = $metadata;
-		}
-		$tmp->addOrganizationInfo($orgmeta);
-		$entityDescriptor = $tmp->getEntityDescriptor();
-	}
-
-	$entitiesDescriptor->appendChild($xml->importNode($entityDescriptor, TRUE));
-}
+if (isset($_REQUEST['exclude'])) 
+	$aggregator->exclude($_REQUEST['exclude']);
 
 
-/* Sign the metadata if enabled. */
-if ($aggregatorConfig->getBoolean('sign.enable', FALSE)) {
-	$privateKey = $aggregatorConfig->getString('sign.privatekey');
-	$privateKeyPass = $aggregatorConfig->getString('sign.privatekey_pass', NULL);
-	$certificate = $aggregatorConfig->getString('sign.certificate');
-
-	$signer = new SimpleSAML_XML_Signer(array(
-		'privatekey' => $privateKey,
-		'privatekey_pass' => $privateKeyPass,
-		'certificate' => $certificate,
-		'id' => 'ID',
-		));
-	$signer->sign($entitiesDescriptor, $entitiesDescriptor, $entitiesDescriptor->firstChild);
-}
+$xml = $aggregator->getMetadataDocument();
 
 
 /* Show the metadata. */