diff --git a/README.md b/README.md
index 925ac47a62946234e38eb9e88edb2c785509eb06..4f2ea6030f72ac683edbf89313d5493778db35c5 100644
--- a/README.md
+++ b/README.md
@@ -12,23 +12,26 @@ Esta guía debería funcionar en Debian o sus derivados. Testeado en *Debian* y
    - como root: `apt install git`
 2. Cloná el repositorio oficial BFA
    - `git clone https://gitlab.bfa.ar/blockchain/nucleo.git bfa`
-3. Ejecutá el script de instalación. Esto cambiará algunas configuraciones en tu sistema. Si te preocupa (¿debería?), podés ejecutar este paso manualmente.
+3. Ejecutá el script de instalación. Esto cambiará algunas configuraciones en tu sistema. Si te preocupa (¿debería?), podés ejecutar este escript paso a paso manualmente.
    - como root: `bfa/bin/installbfa.sh`  
-   Van a aparecer varios *warnings* mientras se instala web3. Esto parece ser "normal". Ignorarlo no parece causar problemas.
+   Van a aparecer varios **warnings** mientras se instala web3. Esto parece ser "normal". Ignorarlo no parece causar problemas.
 4. Cambiá al usuario `bfa`
    - como root: `su - bfa`
-5. Comenzá la sincronización. Esto puede llevar un rato largo (este script se ejecuta automáticamente cuando se reinicia el sistema).
+5. Comenzá la sincronización. **Esto puede llevar un rato largo** (este script se ejecuta automáticamente cuando se reinicia el sistema).
    - como bfa: `start.sh`
 6. Monitoreá los logs con `bfalog.sh`. Apretá CTRL-C en cualquier momento para detener el `tail -f`.
 7. Cambiá la configuración de tu nodo usando `admin.sh syncmode`
    - Hacé esto antes de haber sincronizado mucho en el paso anterior, ya que esto podría remover todos los datos de la cadena que hayas bajado y reiniciar la sincronización de la cadena.
 8. Esperá a aque termine de sincronizar
-9. Ejecutá `maymine.sh` para actualizar tu configuración (detecta si estás autorizado a sellar/minar o no). Podés ejecutar esto todas las veces que quieras. Si tratás de sellar y no tenés permiso, van a aparecer errores en el log (pero no se rompe nada).
-10. Herramientas simples super básicas (más bien pruebas de concepto, para inspirar a los programadores):
+9. Herramientas simples super básicas (más bien pruebas de concepto, para inspirar a los programadores):
     - `explorer.sh` : Sigue el bloque más nuevo "*lastest*" por default, pero podés especificar un número de bloque cualquiera como argumento, por ejemplo `explorer.sh 0` permite ver el génesis (bloque 0).
 	- `walker.pl` : También toma un número de bloque para iniciar. Sigue esperando nuevos bloques.
 
-Hay otros programas "interesantes" en los directorios `bin/` y `src/`, pero para los desarrolladores, el branch `dev` es más intersante.
+Hay otros programas "interesantes" en los directorios `bin/` y `src/`,
+pero para los desarrolladores, el branch `dev` es más intersante y tambien el
+([repositorio contrib](https://gitlab.bfa.ar/blockchain/contrib)).
+
+**Puede tardarse alrededor de una hora conectarse la primera vez. En el log no se ve nada. Hay que tener paciencia.**
 
 ## start.sh
 
@@ -84,7 +87,14 @@ Cuando un operador de un sellador vota para promover o remover un sellador,
 este script hace la mayor parte. Toma un solo argumento, el número de la 
 cuenta del sellador por el cual se vota.
 
-## monitor.sh
+## unlock.js
+
+Debloqua las cuentas del sistema (vease tambien `monitor.js`). Si una cuenta
+tiene clave, se puede poner la clave con este script.
+
+## monitor.js
 
-Esto se corre cada minuto desde `cron.sh`. Actualizará el un archivo
-en `network/status` que muestra información de la red muy básica.
+Esto se corre desde `cron.sh`. Cada minuto actualizará el archivo
+`network/status` que muestra información del estado del nodo muy básica.
+Tambien habilita sellar/minar si la cuenta (eth.accounts[0]) esta permitido
+segun la red. Desbloqua cuentas si no tienen passwords.
\ No newline at end of file
diff --git a/bin/MasterDistiller.js b/bin/MasterDistiller.js
index c7deec68f64b8d9ce450fe7e760356a978c66845..b43cc1f65f2c9d25a7c9e376dbf3d1715ca45258 100755
--- a/bin/MasterDistiller.js
+++ b/bin/MasterDistiller.js
@@ -9,7 +9,7 @@ const   rl          =   require('readline').createInterface(
 var     web3;
 var     Distillery;
 var     bfa;
-var     notation    =   [ 6 ];
+var     notation    =   [ 15 ];
 
 function    init()
 {
@@ -21,14 +21,36 @@ function    init()
         case 9:
             notation.push( "Gwei" );
             break;
+	case 12:
+	    notation.push( "micro" );
+	    break;
+	case 15:
+	    notation.push( "finney" );
+	    break;
+	case 18:
+	    notation.push( "ether" );
+	    break;
+	case 21:
+	    notation.push( "kether" );
+	    break;
+	case 24:
+	    notation.push( "grand" );
+	    break;
+	case 27:
+	    notation.push( "mether" );
+	    break;
+	case 30:
+	    notation.push( "gether" );
+	    break;
+	case 33:
+	    notation.push( "tether" );
+	    break;
         default:
-            notation = [ 6, Math.pow(10, 6), "Mwei" ];
+            notation = [ 15, Math.pow(10, 15), "finney" ];
     }
     bfa             =   new Libbfa();
     web3            =   bfa.newweb3();
     Distillery      =   bfa.contract( web3, 'Distillery' );
-    if ( undefined == Distillery )
-        fatal('Can not initialise Distillery contact.');
     web3.eth.getBalance( Distillery.contractaddress ).then(
         function receivedOwnBalance(val) {
             Distillery.contractbalance = val;
diff --git a/bin/bfaupdate.sh b/bin/bfaupdate.sh
new file mode 100644
index 0000000000000000000000000000000000000000..f6109cc0f79cc5b3572b8a89bbd1dd559285c4dd
--- /dev/null
+++ b/bin/bfaupdate.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# Robert Martin-Legene <robert@nic.ar>
+
+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
+
+set -x
+cd ${BFAHOME}
+git pull
+npm rebuild
diff --git a/bin/cron.sh b/bin/cron.sh
index 50a0b0ad669253b9e806284b01d57aa52916ad94..6710a967f89f623893b4c7dd33c5ef8b494af880 100755
--- a/bin/cron.sh
+++ b/bin/cron.sh
@@ -1,10 +1,8 @@
 #!/bin/bash
 
 trap 'exit 1' ERR
-# Go $HOME
-cd
 # Log in home directory
-exec < /dev/null > bfa-cron-output.log 2>&1
+exec < /dev/null > ${HOME}/bfa-cron-output.log 2>&1
 # Go to script bin, 'cause we expect to find $BFAHOME/bin/env there
 cd `dirname $0`
 source ./env
diff --git a/bin/libbfa.js b/bin/libbfa.js
index d0bf4832f6d4dc868d467ebfc55f95214a363cc7..2146b270a883e80d45416b7f560c2962d4ac6f78 100644
--- a/bin/libbfa.js
+++ b/bin/libbfa.js
@@ -52,16 +52,16 @@ module.exports = class Libbfa
     {
         var     contractdir             =   [ this.networkdir, 'contracts', name ].join('/');
         if ( ! this.fs.existsSync( contractdir ) )
-            return;
+	    this.fatal( 'The directory containing this contract ("' + contractdir + '") does not exist.' );
         var     contractaddress         =   this.fs.realpathSync( contractdir ).replace(/^.*\//, '');
         if ( undefined == contractaddress )
-            return;
+	    this.fatal( 'Contract address for "' + name + '" not found.' );
         var     abistr                  =   this.fs.readFileSync( contractdir + '/abi' ).toString();
         if ( undefined == abistr )
-            return;
+	    this.fatal( 'ABI for contract "' + name + '" not found' );
         var     abi                     =   JSON.parse( abistr );
         if ( undefined == abi )
-            return;
+	    this.fatal( 'Failed to convert ABI for contract "' + name + '" into JSON.' );
         var     c                       =   new w3.eth.Contract( abi, contractaddress );
         c.abi                           =   abi;
         c.contractaddress               =   contractaddress;
@@ -81,32 +81,46 @@ module.exports = class Libbfa
         w3.eth.extend({
             //property: 'bfaclique',
             methods: [{
-                name: 'getSigners',
+                name: 'bfaGetSigners',
                 call: 'clique_getSigners',
                 params: 0
             }]
         });
         w3.eth.extend({
             methods: [{
-                name: 'minerstart',
+                name: 'bfaMinerstart',
                 call: 'miner_start',
                 params: 0
             }]
         });
         w3.eth.extend({
             methods: [{
-                name: 'minerstop',
+                name: 'bfaMinerstop',
                 call: 'miner_stop',
                 params: 0
             }]
         });
         w3.eth.extend({
             methods: [{
-                name: 'adminpeers',
+                name: 'bfaAdminpeers',
                 call: 'admin_peers',
                 params: 0
             }]
         });
+        w3.eth.extend({
+            methods: [{
+                name: 'bfaAdminaddPeer',
+                call: 'admin_addPeer',
+                params: 1
+            }]
+        });
+        w3.eth.personal.extend({
+            methods: [{
+                name:   'bfalistWallets',
+                call:   'personal_listWallets',
+                params: 0
+            }]
+        });
         if ( undefined != process.env.BFAACCOUNT ) {
             w3.eth.defaultAccount  =   this.account;
         }
diff --git a/bin/monitor.js b/bin/monitor.js
index 0d5eaa0331bc5bce0b61f19ce673992bd296529c..2838293a4437fd92aa98d2c65c7b746eb46762ab 100755
--- a/bin/monitor.js
+++ b/bin/monitor.js
@@ -5,41 +5,207 @@
 "use strict"
 
 const   Libbfa      =   require( process.env.BFAHOME + '/bin/libbfa.js');
+var     bfa         =   new Libbfa();
+var     web3        =   bfa.newweb3();
+var     lastUnlock  =   0;
 
-function    monitor()
+function    peerlist()
 {
-    var bfa         =   new Libbfa();
-    var web3        =   bfa.newweb3();
-    var now         =   new Date();
-    //console.log(web3.eth);
-    web3.eth.adminpeers().then(
-        function(nodelist) {
-            var         peers   =   [];
+    web3.eth.bfaAdminpeers().then(
+        function gotAdminPeers( nodelist ) {
+            var         now                 =   new Date();
+            var         nowpeers            =   [];
+            var         peers               =   [];
+            var         newpeers            =   [];
+            if ( bfa.fs.existsSync(  bfa.networkdir + '/peers.cache' ) )
+            {
+                var     data        =   bfa.fs.readFileSync( bfa.networkdir + '/peers.cache' ).toString();
+                if ( data.length > 0 )
+                    peers           =   data.split(/\r?\n/);
+                var     i           =   peers.length;
+                // for some odd reason, I keep seeing empty entries
+                while ( i-- > 0 )
+                    if ( peers[i] == '' )
+                        peers.splice(i,1);
+            }
             nodelist.forEach(
                 function(node) {
-                    if ( typeof(node.protocols.eth) == 'object' )
-                    {   
-                        var     dir     =   "out";
-                        if ( node.network.inbound )
-                            dir         =   "in";
-                        peers.push( "peer "+dir+": "+node.enode );
+                    if ( 'object' == typeof(node.protocols.eth) )
+                    {
+                        // default info - likely to get overwritten.
+                        var     info        =   "<" + node.id + ">";
+                        var     dir         =   "";
+                        if ( undefined != node.network )
+                        {
+                            if ( 'boolean' == typeof(node.network.inbound) )
+                            {
+                                if ( node.network.inbound )
+                                    dir     =   "in";
+                                else
+                                    dir     =   "out";
+                            }
+                            if ( 'string' == typeof(node.enode) )
+                            {
+                                info        =   node.enode;
+                                if (peers.indexOf( node.enode ) == -1 && dir == "out" )
+                                    newpeers.push( node.enode );
+                            }
+                            else
+                            {
+                                info        =   "";
+                                if ( undefined != node.id )
+                                    info    +=  "<" + node.id + ">";
+                                if ( undefined != node.network.remoteAddress )
+                                {
+                                    if ( info != "" )
+                                        info+=  "@";
+                                    info    +=  node.network.remoteAddress;
+                                }
+                            }
+                        }
+                        nowpeers.push( "peer " + dir + ": " + info );
                     }
                 }
             );
+            // write network/status
             bfa.fs.writeFileSync(
                 bfa.networkdir + '/status', 
                 "UTC: " + now.toUTCString() + "\n"
-                    + "BFA peers: " + peers.length + "\n"
-                    + peers.sort().join("\n") + "\n",
+                    + "BFA peers: " + nowpeers.length + "\n"
+                    + nowpeers.sort().join("\n") + "\n",
                 { mode: 0o644 }
             );
+            // Try to connect to a random node if we have very few peers
+            if ( nowpeers.length < 5 && peers.length > 0 )
+            {
+                var     i       =   Math.floor( Math.random() * peers.length );
+                var     enode   =   peers[i];
+                console.log(
+                    "We have "
+                    + nowpeers.length
+                    + " peer" + ( nowpeers.length==1 ? '' : 's' ) + ", so will try to connect to "
+                    + enode
+                );
+                web3.eth.bfaAdminaddPeer( enode );
+            }
+            // write network/peers.cache
+            // peers.cache is a list of peers we have connected out to in the past.
+            peers               =   peers.concat( newpeers );
+            if (peers.length > 100)
+                peers.splice( 0, peers.length - 100 );
+            bfa.fs.writeFileSync(
+                bfa.networkdir + '/peers.cache',
+                peers.join("\n") + "\n",
+                { mode: 0o644 }
+            );
+        },
+        function failedToGetAdminPeers(x)
+        {
+            // ignore connection problems?
+        }
+    );
+}
+
+// Function to determine if our defaultAccount is allowed to seal/mine.
+// It will adjust the behaviour accordingly, i.e. stop or start mining.
+function    mayseal()
+{
+    var me          =   web3.eth.defaultAccount;
+    if ( undefined == me )
+    {
+        console.log( "Failed to get default account information." );
+        return;
+    }
+    me              =   me.toLowerCase();
+    web3.eth.isMining().
+    then(
+        // returns a boolean whether or not we are currently mining/sealing.
+        function( isMining )
+        {
+            // Get a list of clique.getSigners, so we can see if we are
+            // in the list of authorized sealers.
+            web3.eth.bfaGetSigners()
+            .then(
+                function gotListOfSealers(x)
+                {
+                    var lcsealers   =   x.map( name => name.toLowerCase() );
+                    var isSigner    =   (lcsealers.indexOf(me) > -1);
+                    if ( isSigner )
+                    {
+                        if ( ! isMining )
+                        {
+                            console.log( 'Started to seal.' );
+                            web3.eth.bfaMinerstart();
+                        }
+                    }
+                    else
+                    {
+                        if ( isMining )
+                        {
+                            console.log( 'I was trying to seal, but am not authorized. Stopped trying.' );
+                            web3.eth.bfaMinerstop();
+                        }
+                    }
+                },
+                function failedToGetListOfSealers(x)
+                {
+                    console.log(x);
+                }
+            );
         },
-        function(x)
+        function failedToGetIsMiningBool(x)
         {
-            console.log( x );
-            process.exit(1)
+            // Probably geth is not running.
+            //console.log(x);
         }
     );
 }
 
-monitor();
+function    unlock()
+{
+    if ( lastUnlock + 600 > Date.now() / 1000 )
+        return;
+    web3.eth.personal.bfalistWallets()
+    .then(
+        function pushone(x)
+        {
+            var     i           =   x.length;
+            var     wallets     =   new Array();
+            while ( i-- > 0 )
+                if ( x[i].status == "Locked" )
+                    wallets.push( x[i] );
+            i                   =   wallets.length;
+            if ( i == 0 )
+                return;
+            var     promises    =   new Array();
+            while ( i-- > 0 )
+            {
+                var     j       =   wallets[i].accounts.length;
+                while ( j-- > 0 )
+                {
+                    var addr    =   wallets[i].accounts[j].address;
+                    var promise =
+                        web3.eth.personal.unlockAccount( addr, "", 0 )
+                        .catch( error => { } );
+                    promises.push( promise );
+                }
+            }
+            lastUnlock = Date.now() / 1000;
+        }
+        ,
+        function err(x)
+        {
+            // we don't care?
+        }
+    )
+}
+
+function    timer()
+{
+    peerlist();
+    mayseal();
+    unlock();
+}
+
+setTimeout(  timer,      5 * 1000   );
+setInterval( timer,     60 * 1000   );
diff --git a/bin/start.sh b/bin/start.sh
index 74a395df2a735ce9cdc40414fafdba5ac18a9ac6..38d4f4b9f028e0c936ed93cf3f102e00b17cf4ea 100755
--- a/bin/start.sh
+++ b/bin/start.sh
@@ -14,39 +14,11 @@ enodeDGSI="59ae768ecdee632e0daceccb6f71b215392eba89230d626573f2fb4e9c0786c9a6610
 bootDGSIv4="enode://${enodeDGSI}@[200.108.146.100]:30301"
 bootnodes="${bootARIUv6},${bootARIUv4},${bootUNCv4},${bootDGSIv4}"
 
-function accountlist
-{
-    local accts=
-    local filename
-    for filename in ${BFANODEDIR}/keystore/*--*
-    do
-	if [ -r "$filename" -a ${#filename} -ge 40 ]
-	then
-	    acct=${filename:$(( ${#filename} - 40 ))}
-	    accts="${accts},${acct}"
-	fi
-    done
-    # strip leading comma
-    accts=${accts#,}
-    if [ ${#accts} -ge 40 ]
-    then
-        echo "--password /dev/null --unlock ${accts}"
-    fi
-}
-
 # touch the "miner" file if you are authorized to mine
 # If you don't want to restart after touching the file,
 # you can use attach.sh and issue the command:
 #   miner.start()
 
-function getminer
-{
-    if [ -e "${BFANODEDIR}/miner" ]
-    then
-	echo "--mine"
-    fi
-}
-
 function getsyncmode
 {
     local syncmode=$( cat "${BFANODEDIR}/syncmode" 2>/dev/null || true )
@@ -96,17 +68,11 @@ function startmonitor
             echo "A monitor is already running."
             false
         ) || exit
-        if [ -t 1 ]
-        then
-            echo Running monitor every 60 seconds.
-        fi
-        while :
-        do
+        (
             monitor.js &
             echo $! > $pidfile
             wait
-            sleep 60
-        done &
+        ) 2>&1 | ${BFAHOME}/bin/log.sh ${BFANODEDIR}/log &
     ) 9>> $pidfile
 }
 
@@ -134,12 +100,11 @@ function startgeth
             echo '***'
             echo
             # (re)configure parameters (you never know if they changed)
-	    flexargs="$( accountlist) $( getminer ) $( getsyncmode ) --extradata $( extradata )"
+	    flexargs="$( getsyncmode ) --extradata $( extradata )"
             set -x
             geth			            \
 	        --datadir ${BFANODEDIR}	            \
 	        --networkid ${BFANETWORKID}         \
-	        --bootnodes "${bootnodes}"          \
 	        --rpc			            \
 	        --rpcport $rpcport	            \
 	        --rpcapi "eth,net,web3,admin,clique,miner,personal"	\
@@ -148,7 +113,8 @@ function startgeth
                 --gcmode archive                    \
 	        --cache 512		            \
 	        --verbosity ${BFAVERBOSITY:-3}	    \
-	        ${flexargs}			    &
+	        ${flexargs}			    \
+	        --bootnodes "${bootnodes}"          &
             set +x
             echo $! > ${BFANODEDIR}/geth.pid
             wait
diff --git a/bin/unlock.js b/bin/unlock.js
new file mode 100755
index 0000000000000000000000000000000000000000..d7f2315141f2adc0b8b71f7e36a06e72a6ecfd6f
--- /dev/null
+++ b/bin/unlock.js
@@ -0,0 +1,126 @@
+#!/usr/bin/node
+
+const   Libbfa          =   require( process.env.BFAHOME + '/bin/libbfa.js');
+const	bfa	        =   new Libbfa();
+const   Writable        =   require('stream').Writable;
+var     mutableStdout   =   new Writable(
+    {
+        write:          function( chunk, encoding, callback )
+            {
+                if ( ! this.muted )
+                    process.stdout.write(chunk, encoding);
+                callback();
+            }
+    } );
+mutableStdout.muted     =   false;
+const   rl              =   require('readline').createInterface(
+    {
+        input:          process.stdin,
+        output:         mutableStdout,
+        terminal:       true,
+        historySize:    0
+    } );
+var	web3            =   bfa.newweb3();
+
+// First time we try to unlock, we will use this empty passphrase.
+// Do not edit.
+var     passphrase  =   "";
+
+function atLeastOneFailedSoGetNewPass(x)
+{
+    if ( !process.stdin.isTTY )
+        bye( 0, "Stdin is not a tty. Will not try with other passwords." );
+    // first we print the question
+    mutableStdout.muted         =   false;
+    rl.question(
+        "Enter another password to try: ",
+        (answer) => {
+            process.stdout.write( "\n" );
+            mutableStdout.muted =   false;
+            passphrase          =   answer;
+            if ( answer == "" )
+                bye( 0, "Bye." );
+            unlockall();
+        }
+    );
+    // Asking the question is an async event, so
+    // we set the object to mute output while the
+    // user types his password.
+    mutableStdout.muted         =   true;
+}
+
+function bye( exitcode, msg )
+{
+    console.log( msg );
+    rl.close();
+    process.exit( exitcode );
+}
+
+function unlockall()
+{
+    var     wallets             =   new Array();
+    web3.eth.personal.bfalistWallets()
+    .then(
+        function pushone(x)
+        {
+            var     failures    =   0;
+            var     promises    =   new Array();
+            var     i           =   x.length;
+            while ( i-- > 0 )
+                if ( x[i].status == "Locked" )
+                    wallets.push( x[i] );
+            i                   =   wallets.length;
+            if ( i == 0 )
+                bye( 0, "List of accounts to unlock is empty." );
+            while ( i-- > 0 )
+            {
+                var     j       =   wallets[i].accounts.length;
+                while ( j-- > 0 )
+                {
+                    var addr    =   wallets[i].accounts[j].address;
+                    //console.log( "Trying to unlock " + addr + "." );
+                    var promise =
+                        web3.eth.personal.unlockAccount( addr, passphrase, 0 )
+                        .catch(
+                            error =>
+                            {
+                                failures++;
+                                return error;
+                            }
+                        );
+                    promises.push( promise );
+                }
+            }
+            var     empty       =   "";
+            if ( passphrase == "" )
+                empty           =   " with an empty passphrase";
+            console.log(
+                "Attempting to unlock "
+                + promises.length
+                + " account" + ( promises.length == 1 ? '' : 's' )
+                + empty + "."
+            );
+            Promise.all( promises )
+            .then(
+                function(x)
+                {
+                    if ( failures == 0 )
+                        bye( 0, "OK." );
+                    console.log( failures + " account" + (failures==1 ? " is" : "s are") + " still locked." );
+                    atLeastOneFailedSoGetNewPass();
+                },
+                function(x)
+                {
+                    console.log( x );
+                    bye( 1, "I can't imagine this text will ever be shown." );
+                }
+            );
+        },
+        function errWalletList(x)
+        {
+            bye( 1, x );
+        }
+    )
+}
+
+unlockall();