diff --git a/bin/sealeradd.sh b/bin/sealeradd.sh
new file mode 100755
index 0000000000000000000000000000000000000000..53025d20b04bc6634e9575c7370175fb8ac9af37
--- /dev/null
+++ b/bin/sealeradd.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Robert Martin-Legene <robert@nic.ar>
+
+# Proposes to promote a new sealer or to demote an existing sealer.
+# Also sends this vote to the new Sealers contract, so we have a place
+# a contract can look up which addresses are sealers.
+
+if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source bfa/bin/env ?" >&2; exit 1; fi
+source ${BFAHOME}/bin/libbfa.sh || exit 1
+
+operation="false"
+if [ $( basename $0 ) == "sealeradd.sh" ]
+then
+    operation="true"
+fi
+victim="$1"
+(
+    echo "clique.propose(\"0x${victim#0x}\", ${operation});"
+    contractCall Sealers admin_promote \"0x${victim#0x}\" ${operation}
+) | geth_attach
diff --git a/bin/sealerrem.sh b/bin/sealerrem.sh
new file mode 120000
index 0000000000000000000000000000000000000000..9f238ec6fc1160bcb11d5db94c4a25a38c68c150
--- /dev/null
+++ b/bin/sealerrem.sh
@@ -0,0 +1 @@
+sealeradd.sh
\ No newline at end of file
diff --git a/src/contract.Sealers.solc b/src/contract.Sealers.solc
new file mode 100644
index 0000000000000000000000000000000000000000..03804744d7b7758a192b1e2c1f34a38271955344
--- /dev/null
+++ b/src/contract.Sealers.solc
@@ -0,0 +1,125 @@
+pragma solidity ^0.4.22;
+
+contract Sealers {
+
+    struct Proposal {
+	address         victim;     // Whom are we voting about.
+        uint            votestart;  // In which block did this vote begin?
+	bool		promotion;  // true=promotion, false=demotion
+	address[]	voters;     // List of voters.
+    }
+    Proposal[]          public	adminproposals;
+    address[]           public  sealers;
+
+    event               vote( address voter, address victim, bool promotionOrDemotion );
+    event               adminChange( address admin, bool promotionOrDemotion );
+
+    constructor() public {
+        sealers[0] = msg.sender;
+    }
+
+    function isSealer( address subj ) public view returns (bool) {
+        for (uint i=sealers.length; i>0; i--)
+        {
+            if (subj == sealers[i-1])
+                return true;
+        }
+        return false;
+    }
+    modifier onlySealersTX {
+        require( isSealer( tx.origin ), "tx.origin is not a known sealer and can not call this function." );
+        _;
+    }
+    modifier onlySealers {
+        require( isSealer( msg.sender ), "msg.sender is not a known sealer and can not call this function." );
+        _;
+    }
+
+    function sealerLength() public view returns (uint) {
+        return sealers.length;
+    }
+
+    function sealerPosition( uint idx ) public view returns (address) {
+        require( idx <= sealers.length, "There are not that many sealers registered in the list." );
+        return sealers[idx];
+    }
+
+    function _trimProposals() private {
+        uint    i   =   adminproposals.length;
+        while ( i-- > 0 )
+        {
+            if ( adminproposals[i].votestart < block.number+30000 )
+            {
+                while ( i < adminproposals.length )
+                {
+                    adminproposals[i] = adminproposals[i+1];
+                    i--;
+                }
+                adminproposals.length--;
+            }
+        }
+    }
+
+    function admin_promote( address victim, bool promotion ) public onlySealersTX {
+        // Janitor
+        _trimProposals();
+        bool    isS =   isSealer(victim);
+        // Is already Sealer and want to promote him?
+        if ( isS && promotion )
+            revert("You can't promote someone who is already a sealer.");
+        // Is not Sealer and want to demote him?
+        if ( !isS && !promotion )
+            revert("You can't demote someone who is not a sealer.");
+        // First see if we can find the index of an already running proposal for this victim,promotion tupple.
+	uint	proppos =   adminproposals.length;
+	while ( proppos>0 && adminproposals[proppos-1].victim != victim && adminproposals[proppos-1].promotion != promotion)
+		proppos--;
+        // If we found no matching proposals, we will add it, so we can now vote on it.
+	if (proppos == 0)
+        {
+            address[] memory sillycompiler;
+	    proppos = adminproposals.push( Proposal(victim, block.number, promotion, sillycompiler ) );
+        }
+        // substract one, to make proppos actually reflect the position in the index.
+        proppos--;
+        // Make things a bit more easy to read by using a shorter variable name
+        Proposal memory prop = adminproposals[proppos];
+        // Now look through the proposal and see if this sender already voted.
+        uint    voterpos   =  prop.voters.length;
+        while ( voterpos>0 && prop.voters[voterpos-1] != msg.sender )
+            voterpos--;
+        // Return if sender already voted
+        if ( voterpos > 0 )
+            revert("You can not vote twice.");
+        // Send notification of the valid vote
+        emit vote( msg.sender, victim, promotion );
+        // Do we have enough votes to perform the operation?
+        if ( prop.voters.length < sealers.length/2+1 )
+            return;
+        //
+        // Is it a promotion or a demotion?
+        if ( prop.promotion )
+        {
+            sealers.push( victim );
+        }
+        else
+        {
+            uint    i = sealers.length;
+            // Delete all occurences of the victim from the sealer list (should be just a single occurence)
+            while ( i-- > 0 ) {
+                if (sealers[i] == victim )
+                {
+                    for (uint j=i; j<sealers.length-1; j--)
+                        sealers[j] = sealers[j+1];
+                    sealers.length--;
+                }
+            }
+        }
+        // Remove the proposal, as the voting is complete.
+        for (i=proppos; i<adminproposals.length-1; i--)
+            adminproposals[i] = adminproposals[i+1];
+        adminproposals.length--;
+        // Send notification
+        emit adminChange( victim, promotion );
+    }
+}