diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index e01712aa6..5bf8e5242 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -1,110 +1,153 @@
 {
-	"ImportPath": "github.com/arduino/arduino-create-agent",
-	"GoVersion": "go1.4.2",
+	"ImportPath": ".",
+	"GoVersion": "go1.4",
 	"Deps": [
+		{
+			"ImportPath": "github.com/Sirupsen/logrus",
+			"Comment": "v0.8.7-53-g446d1c1",
+			"Rev": "446d1c146faa8ed3f4218f056fcd165f6bcfda81"
+		},
+		{
+			"ImportPath": "github.com/carlescere/scheduler",
+			"Comment": "0.1-13-g025e9f5",
+			"Rev": "025e9f58462112b9fb636708374467aef7161289"
+		},
 		{
 			"ImportPath": "github.com/facchinm/go-serial",
 			"Rev": "3cfbd2fab741f7c9d238cb3bd17980110c81bd46"
 		},
+		{
+			"ImportPath": "github.com/facchinm/go-serial-native",
+			"Rev": "6c62fb894d873784830ec370e85ab1f29c2e0324"
+		},
 		{
 			"ImportPath": "github.com/facchinm/systray",
 			"Rev": "b32308b0b662e45cde7c5986bc29def5d48bafbb"
 		},
+		{
+			"ImportPath": "github.com/getlantern/filepersist",
+			"Rev": "e91ecdee50cac4a38975041089abdddcb94c4e97"
+		},
 		{
 			"ImportPath": "github.com/getlantern/golog",
 			"Rev": "5245e761fd1c8e9ccba87de802a0f666ed55980f"
 		},
 		{
 			"ImportPath": "github.com/gin-gonic/gin",
-			"Comment": "v1.0rc1-128-gfc5e355",
-			"Rev": "fc5e3557242901e24016d4474168515434871dd5"
+			"Comment": "v1.0rc1-148-g52fcc5d",
+			"Rev": "52fcc5dbf6e94df33ad313858fb94b713e9d1b4a"
 		},
 		{
 			"ImportPath": "github.com/googollee/go-engine.io",
-			"Rev": "597faf3df88a01780a5881fafbcbccd9d5c3fab0"
+			"Rev": "a66cf68b24e56df0cacdc6041b93742f983a296a"
 		},
 		{
 			"ImportPath": "github.com/googollee/go-socket.io",
-			"Comment": "0.9.x-33-g9c83c02",
-			"Rev": "9c83c02fa6172eaea8252b6428e3cef3576c9746"
+			"Comment": "0.9.x-51-g14bb3d0",
+			"Rev": "14bb3d0368089a74cb52164a7a697cf404bda9c6"
 		},
 		{
 			"ImportPath": "github.com/gorilla/websocket",
-			"Rev": "a3ec486e6a7a41858210b0fc5d7b5df593b3c4a3"
-		},
-		{
-			"ImportPath": "github.com/inconshreveable/go-update",
-			"Rev": "68f5725818189545231c1fd8694793d45f2fc529"
+			"Rev": "2dbddebb8266b93c5e6b119efb54e89043186f3f"
 		},
 		{
 			"ImportPath": "github.com/itsjamie/gin-cors",
-			"Rev": "bd9551838cd52133960a2f44ab990be71744b663"
+			"Rev": "9526714e382db4023de25bfdad89cec9b7446092"
 		},
 		{
 			"ImportPath": "github.com/kardianos/osext",
 			"Rev": "6e7f843663477789fac7c02def0d0909e969b4e5"
 		},
-		{
-			"ImportPath": "github.com/kardianos/service",
-			"Rev": "1ab12303aaf4cf68a53a5717a31b5acd72c57019"
-		},
 		{
 			"ImportPath": "github.com/kr/binarydist",
 			"Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd"
 		},
+		{
+			"ImportPath": "github.com/kr/fs",
+			"Rev": "2788f0dbd16903de03cb8186e5c7d97b69ad387b"
+		},
 		{
 			"ImportPath": "github.com/manucorporat/sse",
-			"Rev": "c142f0f1baea5cef7f98a8a6c222f6134368c1f5"
+			"Rev": "fe6ea2c8e398672518ef204bf0fbd9af858d0e15"
 		},
 		{
 			"ImportPath": "github.com/mattn/go-colorable",
-			"Rev": "40e4aedc8fabf8c23e040057540867186712faa5"
+			"Rev": "3dac7b4f76f6e17fb39b768b89e3783d16e237fe"
+		},
+		{
+			"ImportPath": "github.com/mattn/go-isatty",
+			"Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
+		},
+		{
+			"ImportPath": "github.com/mattn/go-shellwords",
+			"Comment": "v1.0.0",
+			"Rev": "f4e566c536cf69158e808ec28ef4182a37fdc981"
 		},
 		{
 			"ImportPath": "github.com/miekg/dns",
-			"Rev": "7864d445e5087e8d761dbefec43f29b92f7650eb"
+			"Rev": "75e6e86cc601825c5dbcd4e0c209eab180997cd7"
 		},
 		{
 			"ImportPath": "github.com/oleksandr/bonjour",
-			"Rev": "415cafc6ab8894ea7b7bcbed933ce2d8c611683c"
+			"Rev": "7ee314fe2bb898db0072d9af4b540b816fe7c6b5"
+		},
+		{
+			"ImportPath": "github.com/pivotal-golang/archiver/extractor",
+			"Rev": "f598990ddfeda2767a09346acf844a7d216c1256"
 		},
 		{
-			"ImportPath": "github.com/sanderhahn/gozip/patchzip",
-			"Rev": "85893825032e0136f506cc9e3f36d42d56305ee9"
+			"ImportPath": "github.com/pkg/sftp",
+			"Rev": "2bc00847a6f77f5d32fa46b0ab829f0531d73244"
+		},
+		{
+			"ImportPath": "github.com/sfreiberg/simplessh",
+			"Rev": "e2df5a8be6f36be66ed1af2e8767a354788e902b"
 		},
 		{
 			"ImportPath": "github.com/skratchdot/open-golang/open",
 			"Rev": "c8748311a7528d0ba7330d302adbc5a677ef9c9e"
 		},
 		{
-			"ImportPath": "github.com/termie/go-shutil",
-			"Rev": "bcacb06fecaeec8dc42af03c87c6949f4a05c74c"
+			"ImportPath": "github.com/vharitonsky/iniflags",
+			"Comment": "v.1.0-28-g02b57ef",
+			"Rev": "02b57ef987a5ee59eedc7dcd93315a43f6dcc4a7"
 		},
 		{
-			"ImportPath": "github.com/vharitonsky/iniflags",
-			"Comment": "v.1.0-21-g14f4b95",
-			"Rev": "14f4b95d09b99e2475e82c37a2f943ef2469a890"
+			"ImportPath": "github.com/xrash/smetrics",
+			"Rev": "81a89232431423f9140fa413823ce3045f02f19c"
 		},
 		{
-			"ImportPath": "golang.org/x/net/context",
-			"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
+			"ImportPath": "golang.org/x/crypto/curve25519",
+			"Rev": "f18420efc3b4f8e9f3d51f6bd2476e92c46260e9"
+		},
+		{
+			"ImportPath": "golang.org/x/crypto/ssh",
+			"Rev": "f18420efc3b4f8e9f3d51f6bd2476e92c46260e9"
 		},
 		{
-			"ImportPath": "golang.org/x/net/internal/iana",
-			"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
+			"ImportPath": "golang.org/x/net/context",
+			"Rev": "84ba27dd5b2d8135e9da1395277f2c9333a2ffda"
 		},
 		{
 			"ImportPath": "golang.org/x/net/ipv4",
-			"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
+			"Rev": "84ba27dd5b2d8135e9da1395277f2c9333a2ffda"
 		},
 		{
 			"ImportPath": "golang.org/x/net/ipv6",
-			"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
+			"Rev": "84ba27dd5b2d8135e9da1395277f2c9333a2ffda"
+		},
+		{
+			"ImportPath": "golang.org/x/sys/unix",
+			"Rev": "833a04a10549a95dc34458c195cbad61bbb6cb4d"
 		},
 		{
 			"ImportPath": "gopkg.in/bluesuncorp/validator.v5",
-			"Comment": "v5.10",
-			"Rev": "d2748d5ae5e5af5ec3a85e3a59bf0db9d741c82d"
+			"Comment": "v5.12",
+			"Rev": "d5acf1dac43705f8bfbb71d878e290e2bed3950b"
+		},
+		{
+			"ImportPath": "gopkg.in/inconshreveable/go-update.v0",
+			"Rev": "d8b0b1d421aa1cbf392c05869f8abbc669bb7066"
 		}
 	]
 }
diff --git a/README.md b/README.md
index 1c8921a25..9ca35fcee 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,6 @@
 arduino-create-agent
 ====================
 
-Version X.XX
-
 Get the latest version of the Agent for all supported platforms:
 
 * [Windows](http://downloads.arduino.cc/CreateBridgeStable/ArduinoCreateAgent-1.1-windows-installer.exe)
@@ -21,6 +19,16 @@ Please use the current latest version:
 * [MacOSX dev](http://downloads.arduino.cc/CreateBridge/staging/ArduinoCreateAgent-1.0-osx-installer.dmg) 
 * [Linux x64 dev](http://downloads.arduino.cc/CreateBridge/staging/ArduinoCreateAgent-1.0-linux-x64-installer.run)
 
+## Compiling
+
+`compile_webidebridge.sh` contains the cross-platform script we use to deploy the agent for all the supported platforms; it needs to be adjusted as per your `go` installation paths and OS.
+
+You can use `bootstrapPlatforms` function to compile the needed CGO-enabled environment
+
+Other prerequisites are:
+* libappindicator (Linux only)
+* [go-selfupdate] (https://github.com/sanbornm/go-selfupdate) if you want to test automatic updates
+
 ## Submitting an issue
 
 Please attach the output of the commands running at the debug console if useful.
diff --git a/certificates.go b/certificates.go
new file mode 100644
index 000000000..bfa4d11ac
--- /dev/null
+++ b/certificates.go
@@ -0,0 +1,332 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Generate a self-signed X.509 certificate for a TLS server. Outputs to
+// 'cert.pem' and 'key.pem' and will overwrite existing files.
+
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"fmt"
+	"io/ioutil"
+	"math/big"
+	"net"
+	"os"
+	"strings"
+	"text/template"
+	"time"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/gin-gonic/gin"
+)
+
+var (
+	host      = "localhost"
+	validFrom = ""
+	validFor  = 365 * 24 * time.Hour * 2 // 2 years
+	rsaBits   = 2048
+)
+
+func publicKey(priv interface{}) interface{} {
+	switch k := priv.(type) {
+	case *rsa.PrivateKey:
+		return &k.PublicKey
+	case *ecdsa.PrivateKey:
+		return &k.PublicKey
+	default:
+		return nil
+	}
+}
+
+func pemBlockForKey(priv interface{}) *pem.Block {
+	switch k := priv.(type) {
+	case *rsa.PrivateKey:
+		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
+	case *ecdsa.PrivateKey:
+		b, err := x509.MarshalECPrivateKey(k)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
+			os.Exit(2)
+		}
+		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
+	default:
+		return nil
+	}
+}
+
+func generateKey(ecdsaCurve string) (interface{}, error) {
+	switch ecdsaCurve {
+	case "":
+		return rsa.GenerateKey(rand.Reader, rsaBits)
+	case "P224":
+		return ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
+	case "P256":
+		return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	case "P384":
+		return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+	case "P521":
+		return ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
+	default:
+		return nil, fmt.Errorf("Unrecognized elliptic curve: %q", ecdsaCurve)
+	}
+}
+
+func generateSingleCertificate(isCa bool) (*x509.Certificate, error) {
+	var notBefore time.Time
+	var err error
+	if len(validFrom) == 0 {
+		notBefore = time.Now()
+	} else {
+		notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to parse creation date: %s\n", err.Error())
+		}
+	}
+
+	notAfter := notBefore.Add(validFor)
+
+	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+	if err != nil {
+		return nil, fmt.Errorf("failed to generate serial number: %s\n", err.Error())
+	}
+
+	template := x509.Certificate{
+		SerialNumber: serialNumber,
+		Subject: pkix.Name{
+			Organization:       []string{"Arduino LLC US"},
+			Country:            []string{"US"},
+			CommonName:         "localhost",
+			OrganizationalUnit: []string{"IT"},
+		},
+		NotBefore: notBefore,
+		NotAfter:  notAfter,
+
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
+		BasicConstraintsValid: true,
+	}
+
+	hosts := strings.Split(host, ",")
+	for _, h := range hosts {
+		if ip := net.ParseIP(h); ip != nil {
+			template.IPAddresses = append(template.IPAddresses, ip)
+		} else {
+			template.DNSNames = append(template.DNSNames, h)
+		}
+	}
+
+	if isCa {
+		template.IsCA = true
+		template.KeyUsage |= x509.KeyUsageCertSign
+		template.Subject.CommonName = "Arduino"
+	}
+
+	return &template, nil
+}
+
+func generateCertificates() {
+
+	os.Remove("ca.cert.pem")
+	os.Remove("ca.key.pem")
+	os.Remove("cert.pem")
+	os.Remove("key.pem")
+
+	// Create the key for the certification authority
+	caKey, err := generateKey("P256")
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+
+	keyOut, err := os.OpenFile("ca.key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+	pem.Encode(keyOut, pemBlockForKey(caKey))
+	keyOut.Close()
+	log.Println("written ca.key.pem")
+
+	// Create the certification authority
+	caTemplate, err := generateSingleCertificate(true)
+
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+
+	derBytes, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, publicKey(caKey), caKey)
+
+	certOut, err := os.Create("ca.cert.pem")
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+	certOut.Close()
+	log.Print("written ca.cert.pem")
+
+	ioutil.WriteFile("ca.cert.cer", derBytes, 0644)
+	log.Print("written ca.cert.cer")
+
+	// Create the key for the final certificate
+	key, err := generateKey("P256")
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+
+	keyOut, err = os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+	pem.Encode(keyOut, pemBlockForKey(key))
+	keyOut.Close()
+	log.Println("written key.pem")
+
+	// Create the final certificate
+	template, err := generateSingleCertificate(false)
+
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+
+	derBytes, err = x509.CreateCertificate(rand.Reader, template, caTemplate, publicKey(key), caKey)
+
+	certOut, err = os.Create("cert.pem")
+	if err != nil {
+		log.Error(err.Error())
+		os.Exit(1)
+	}
+	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+	certOut.Close()
+	log.Print("written cert.pem")
+
+	ioutil.WriteFile("cert.cer", derBytes, 0644)
+	log.Print("written cert.cer")
+
+}
+
+func certHandler(c *gin.Context) {
+	if strings.Contains(c.Request.UserAgent(), "Firefox") {
+		c.Header("content-type", "application/x-x509-ca-cert")
+		c.File("ca.cert.cer")
+		return
+	}
+	noFirefoxTemplate.Execute(c.Writer, gin.H{
+		"url": "http://" + c.Request.Host + c.Request.URL.String(),
+	})
+}
+
+const noFirefoxTemplateHTML = `<!DOCTYPE html>
+<html>
+  <head>
+  <style>
+html {
+    background-color: #0ca1a6;
+    background-repeat: no-repeat;
+    background-position: 2% 11%;
+    background-size: 10%;
+    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIxMjguN3B4IiBoZWlnaHQ9IjE0MS4zcHgiIHZpZXdCb3g9IjAgMCAxMjguNyAxNDEuMyIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTI4LjcgMTQxLjMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnIGlkPSJjcmVhdGVsb2dvIj48cGF0aCBmaWxsPSIjN0ZDQkNEIiBkPSJNMTI0LDMyLjljMC0xNi0xMy41LTI4LjktMzAtMjguOWMtMS41LDAtMy4xLDAuMS00LjYsMC4zQzc2LjUsNi4xLDY3LjksMTUuNCw2My4xLDIyLjVDNTguMywxNS40LDQ5LjcsNi4xLDM2LjgsNC4zYy0xLjUtMC4yLTMuMS0wLjMtNC42LTAuM2MtMTYuNiwwLTMwLDEzLTMwLDI4LjljMCwxNiwxMy41LDI5LDMwLDI5YzEuNSwwLDMuMS0wLjEsNC42LTAuM2MxMi45LTEuOCwyMS41LTExLjEsMjYuMy0xOC4yYzQuOCw3LjEsMTMuNCwxNi40LDI2LjMsMTguMmMxLjUsMC4yLDMuMSwwLjMsNC42LDAuM0MxMTAuNiw2MS44LDEyNCw0OC45LDEyNCwzMi45eiBNMzUuNCw1MS40Yy0xLjEsMC4yLTIuMSwwLjItMy4yLDAuMmMtMTAuOSwwLTE5LjgtOC40LTE5LjgtMTguN2MwLTEwLjMsOC45LTE4LjcsMTkuOC0xOC43YzEsMCwyLjEsMC4xLDMuMiwwLjJjMTIuMSwxLjcsMTkuNSwxMy43LDIyLDE4LjVDNTQuOSwzNy43LDQ3LjUsNDkuNiwzNS40LDUxLjR6IE02OC44LDMyLjljMi41LTQuOCw5LjktMTYuNywyMi0xOC41YzEuMS0wLjEsMi4xLTAuMiwzLjItMC4yYzEwLjksMCwxOS44LDguNCwxOS44LDE4LjdjMCwxMC4zLTguOSwxOC43LTE5LjcsMTguN2MtMSwwLTIuMS0wLjEtMy4yLTAuMkM3OC43LDQ5LjYsNzEuMywzNy43LDY4LjgsMzIuOXoiLz48cmVjdCB4PSIyMy45IiB5PSIzMC4xIiBmaWxsPSIjN0ZDQkNEIiB3aWR0aD0iMTgiIGhlaWdodD0iNS44Ii8+PHBvbHlnb24gZmlsbD0iIzdGQ0JDRCIgcG9pbnRzPSI5NiwzNS45IDEwMi4xLDM1LjkgMTAyLjEsMzAuMSA5NiwzMC4xIDk2LDI0IDkwLjIsMjQgOTAuMiwzMC4xIDg0LjEsMzAuMSA4NC4xLDM1LjkgOTAuMiwzNS45IDkwLjIsNDIgOTYsNDIgIi8+PHBhdGggZmlsbD0iIzdGQ0JDRCIgZD0iTTEwLjcsNzAuNUw1LjUsODcuMmg0LjJsMC45LTMuNGg0LjhsMC45LDMuNGg0LjRsLTUuMy0xNi44SDEwLjd6IE0xMS40LDgwLjZsMS41LTYuMWwxLjcsNi4xSDExLjR6Ii8+PHBhdGggZmlsbD0iIzdGQ0JDRCIgZD0iTTM2LjUsNzYuMWMwLTMuMi0xLjctNS42LTYuNC01LjZoLTYuOHYxNi44aDQuMnYtNS4zaDIuMWwyLjUsNS4zSDM3bC0zLjMtNi4xQzM1LjYsODAuMSwzNi41LDc4LjMsMzYuNSw3Ni4xeiBNMjkuNyw3OC42aC0yLjJ2LTQuN2gyLjFjMS45LDAsMi41LDAuOCwyLjUsMi4yQzMyLjEsNzcuOSwzMS4yLDc4LjYsMjkuNyw3OC42eiIvPjxwYXRoIGZpbGw9IiM3RkNCQ0QiIGQ9Ik00NS40LDcwLjVoLTUuNXYxNi44aDUuNWM3LjEsMCw4LjMtNC42LDguMy04LjVDNTMuNyw3My40LDUxLjYsNzAuNSw0NS40LDcwLjV6IE00NS42LDgzLjhINDR2LTkuOWgxLjJjMy43LDAsNCwxLjgsNCw1QzQ5LjMsODEuNyw0OSw4My44LDQ1LjYsODMuOHoiLz48cGF0aCBmaWxsPSIjN0ZDQkNEIiBkPSJNNjYsODAuN2MwLDIuNi0wLjcsMy40LTIuNiwzLjRjLTEuNiwwLTIuNi0wLjYtMi42LTMuNFY3MC41aC00LjJ2MTFjMCw1LjEsMy43LDYsNi41LDZjMywwLDYuNy0xLjEsNi43LTZ2LTExaC00VjgwLjd6Ii8+PHBvbHlnb24gZmlsbD0iIzdGQ0JDRCIgcG9pbnRzPSI3My43LDczLjkgNzgsNzMuOSA3OCw4My44IDczLjcsODMuOCA3My43LDg3LjIgODYuNiw4Ny4yIDg2LjYsODMuOCA4Mi4yLDgzLjggODIuMiw3My45IDg2LjYsNzMuOSA4Ni42LDcwLjUgNzMuNyw3MC41ICIvPjxwb2x5Z29uIGZpbGw9IiM3RkNCQ0QiIHBvaW50cz0iOTkuOSw4MC41IDk0LjUsNzAuNSA5MCw3MC41IDkwLDg3LjIgOTMuOSw4Ny4yIDkzLjksNzYuNSA5OS42LDg3LjIgMTAzLjgsODcuMiAxMDMuOCw3MC41IDk5LjksNzAuNSAiLz48cGF0aCBmaWxsPSIjN0ZDQkNEIiBkPSJNMTEzLjYsNzAuMmMtNS4xLDAtNywzLjctNyw4LjhjMCw1LjYsMi4yLDguNSw3LDguNWM1LjQsMCw3LjItMy44LDcuMi04LjdDMTIwLjgsNzIuNCwxMTguMyw3MC4yLDExMy42LDcwLjJ6IE0xMTMuNiw4NC4xYy0yLjQsMC0yLjYtMS43LTIuNi01LjFjMC00LjEsMC40LTUuNCwyLjYtNS40YzIuNCwwLDIuOCwxLjEsMi44LDUuMkMxMTYuNCw4Mi43LDExNiw4NC4xLDExMy42LDg0LjF6Ii8+PHBhdGggZmlsbD0iIzdGQ0JDRCIgZD0iTTEyNi4yLDUuMWMtMC4yLTAuNS0wLjUtMS0wLjktMS4zYy0wLjQtMC40LTAuOC0wLjctMS4zLTAuOWMtMC41LTAuMi0xLjEtMC4zLTEuNi0wLjNjLTAuNiwwLTEuMSwwLjEtMS42LDAuM2MtMC41LDAuMi0wLjksMC41LTEuMywwLjljLTAuNCwwLjQtMC43LDAuOC0wLjksMS4zYy0wLjIsMC41LTAuMywxLjEtMC4zLDEuN2MwLDAuNiwwLjEsMS4xLDAuMywxLjdjMC4yLDAuNSwwLjUsMSwwLjksMS40YzAuNCwwLjQsMC44LDAuNywxLjMsMC45YzAuNSwwLjIsMS4xLDAuMywxLjYsMC4zYzAuNiwwLDEuMS0wLjEsMS42LTAuM2MwLjUtMC4yLDAuOS0wLjUsMS4zLTAuOWMwLjQtMC40LDAuNy0wLjgsMC45LTEuNGMwLjItMC41LDAuMy0xLjEsMC4zLTEuN0MxMjYuNSw2LjIsMTI2LjQsNS42LDEyNi4yLDUuMXogTTEyNS4zLDguMWMtMC4yLDAuNC0wLjQsMC43LTAuNywxYy0wLjMsMC4zLTAuNiwwLjUtMSwwLjdjLTAuNCwwLjItMC44LDAuMy0xLjIsMC4zYy0wLjQsMC0wLjktMC4xLTEuMi0wLjNjLTAuNC0wLjItMC43LTAuNC0xLTAuN2MtMC4zLTAuMy0wLjUtMC42LTAuNy0xYy0wLjItMC40LTAuMy0wLjgtMC4zLTEuM2MwLTAuNSwwLjEtMC45LDAuMy0xLjNjMC4yLTAuNCwwLjQtMC44LDAuNy0xLjFjMC4zLTAuMywwLjYtMC41LDEtMC43YzAuNC0wLjIsMC44LTAuMywxLjItMC4zYzAuNCwwLDAuOCwwLjEsMS4yLDAuM2MwLjQsMC4yLDAuNywwLjQsMSwwLjdjMC4zLDAuMywwLjUsMC42LDAuNywxLjFjMC4yLDAuNCwwLjMsMC44LDAuMywxLjNDMTI1LjUsNy4yLDEyNS40LDcuNywxMjUuMyw4LjF6Ii8+PHBhdGggZmlsbD0iIzdGQ0JDRCIgZD0iTTEyMy4yLDcuMmMwLjItMC4xLDAuNC0wLjMsMC41LTAuNWMwLjEtMC4yLDAuMi0wLjQsMC4yLTAuN2MwLTAuNC0wLjEtMC43LTAuNC0xYy0wLjMtMC4zLTAuOC0wLjQtMS40LTAuNGMtMC4zLDAtMC41LDAtMC43LDBjLTAuMiwwLTAuNCwwLTAuNiwwdjQuMmgxLjFWNy40aDAuNGwwLjcsMS40aDEuMkwxMjMuMiw3LjJ6IE0xMjIuMiw2LjZjLTAuMSwwLTAuMiwwLTAuMywwVjUuNWMwLjEsMCwwLjIsMCwwLjMsMGMwLjUsMCwwLjcsMC4yLDAuNywwLjZDMTIyLjksNi40LDEyMi43LDYuNiwxMjIuMiw2LjZ6Ii8+PHBhdGggZmlsbD0iIzdGQ0JDRCIgZD0iTTM5LjgsMTE2LjVjMC0yLjctMS4yLTMuNy00LjEtMy43aC0zLjV2Ny4yaDMuN0MzOC4xLDEyMC4xLDM5LjgsMTE5LjIsMzkuOCwxMTYuNXoiLz48cG9seWdvbiBmaWxsPSIjN0ZDQkNEIiBwb2ludHM9IjY3LjksMTIyLjQgNzQsMTIyLjQgNzAuOSwxMTMuMyAiLz48cGF0aCBmaWxsPSIjN0ZDQkNEIiBkPSJNNS41LDEwMS4ydjM3LjZoMTE1LjF2LTM3LjZINS41eiBNMjAuMSwxMjkuM2MtNSwwLTguNC0yLjctOC40LTkuM2MwLTYuMSwzLjUtOS41LDguNi05LjVjMS44LDAsMy43LDAuNCw0LjgsMS4xbC0wLjgsMi4xYy0xLTAuOC0yLjctMS4yLTQuMS0xLjJjLTQuMSwwLTYuMiwyLjctNi4yLDcuNGMwLDQuOSwyLDcuNCw2LjIsNy40YzEuOCwwLDMuNy0wLjUsNC42LTEuMWwwLjYsMS45QzI0LDEyOSwyMi4xLDEyOS4zLDIwLjEsMTI5LjN6IE00MC40LDEyOS4xbC0zLjktNy4xaC00LjN2Ny4xSDMwdi0xOC4yaDUuOWM0LjYsMCw2LjMsMS45LDYuMyw1LjZjMCwyLjUtMS4zLDQuMy0zLjQsNS4xbDQuMyw3LjVINDAuNHogTTU5LjksMTEyLjhINDkuOHY1LjloOS4xdjEuOWgtOS4xdjYuNGgxMC4xdjJINDcuNnYtMTguMmgxMi4zVjExMi44eiBNNzYuMiwxMjkuMWwtMS42LTQuOGgtNy4zbC0xLjYsNC44aC0yLjNsNi4zLTE4LjJoMi40bDYuNCwxOC4ySDc2LjJ6IE05NS45LDExMi44aC02LjN2MTYuMmgtMi4zdi0xNi4yaC02LjN2LTJoMTQuOFYxMTIuOHogTTExMi4zLDExMi44aC0xMC4xdjUuOWg5LjF2MS45aC05LjF2Ni40aDEwLjF2MkgxMDB2LTE4LjJoMTIuM1YxMTIuOHoiLz48L2c+PC9zdmc+);
+    margin: 0;
+    padding: 0;
+    height: 100%;
+}
+
+body {
+    margin: 0;
+    height: 100%;
+}
+
+.container {
+    font-family: TypoNine;
+    font-weight: 400;
+    border: 0;
+    color: #4D4D4D;
+    background-color: #ecf1f1;
+    display: block;
+    min-height: 100%;
+    height: auto;
+    margin: 0 auto;
+    min-width: 600px;
+    padding: 2% 4%;
+    position: relative;
+    width: 65%;
+    box-shadow: 0 0 10px 0 rgba(21,110,114,.8);
+}
+
+h1 {
+    text-align: left;
+    font-weight: 400;
+    color: #4D4D4D;
+    margin: 0;
+    font-family: "TyponineSans Regular 18";
+    font-size: 2.5em;
+    -webkit-font-smoothing: antialiased;
+    text-transform: none;
+    letter-spacing: .05em;
+    line-height: 1.65em;
+}
+
+.image {
+    text-align: center;
+    margin: 2em 0;
+}
+
+p {
+    line-height: 1.5em;
+    font-size: 1.35em;
+    letter-spacing: .05em;
+    font-family: "TyponineSans Light 17";
+}
+
+    </style>
+    <script type="text/javascript">
+       WebFontConfig = {
+        custom: {
+          families: [
+            'TyponineSans Light 17',
+            'TyponineSans Regular 18',
+            'TyponineSans Monospace Medium 3',
+          ],
+          urls: [
+            '//arduino.cc/fonts/fonts.css',
+            '//arduino.cc/css/arduino-icons.css'
+          ]
+        }
+      };
+      (function() {
+        var wf = document.createElement('script');
+        wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
+          '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
+        wf.type = 'text/javascript';
+        wf.async = 'true';
+        var s = document.getElementsByTagName('script')[0];
+        s.parentNode.insertBefore(wf, s);
+      })();
+    </script>
+  </head>
+  <body>
+    <div class="container">
+        <h1>Oops, this is not Firefox</h1>
+        <div class="image">
+            <img width="418" height="257" title="" alt="" src="data:image/png;base64,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" />
+        </div>
+        <p>You need to open this link in Firefox to trust this certificate: {{.host}}{{.url}}</p>
+    </div>
+  </body>
+
+</html>`
+
+var noFirefoxTemplate = template.Must(template.New("home").Parse(noFirefoxTemplateHTML))
diff --git a/compile_webidebridge.sh b/compile_webidebridge.sh
index f9a5c8639..29f9231fc 100755
--- a/compile_webidebridge.sh
+++ b/compile_webidebridge.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # git submodule init
 # git submodule update
 
@@ -29,10 +31,10 @@ createZipEmbeddableFileArduino()
 	mkdir arduino/arduino
 	cp -r arduino/hardware arduino/tools\_$GOOS\_$GOARCH arduino/boards.json arduino/arduino
 	cp config.ini arduino
-    cp fakecerts/*.pem arduino/
+	cp fakecerts/*.pem fakecerts/*.cer arduino/
 	mv arduino/arduino/tools* arduino/arduino/tools
 	cd arduino
-	zip -r arduino.zip config.ini *.pem > /dev/null
+	zip -r arduino.zip config.ini *.pem *.cer > /dev/null
 	cd ..
 	#cat arduino/arduino.zip >> $3
 	#zip  --adjust-sfx $3
diff --git a/config.ini b/config.ini
index f0639f2c6..dae4bce0e 100644
--- a/config.ini
+++ b/config.ini
@@ -7,3 +7,4 @@ v = true  # show debug logging
 appName = CreateBridge
 updateUrl = http://downloads.arduino.cc/
 #updateUrl = http://localhost/
+origins = http://webide.arduino.cc:8080
\ No newline at end of file
diff --git a/conn.go b/conn.go
index e6981a75c..f8285633a 100644
--- a/conn.go
+++ b/conn.go
@@ -57,6 +57,9 @@ func uploadHandler(c *gin.Context) {
 	extraInfo.authdata.UserName = c.PostForm("auth_user")
 	extraInfo.authdata.Password = c.PostForm("auth_pass")
 	commandline := c.PostForm("commandline")
+	if commandline == "undefined" {
+		commandline = ""
+	}
 	extraInfo.use_1200bps_touch, _ = strconv.ParseBool(c.PostForm("use_1200bps_touch"))
 	extraInfo.wait_for_upload_port, _ = strconv.ParseBool(c.PostForm("wait_for_upload_port"))
 	extraInfo.networkPort, _ = strconv.ParseBool(c.PostForm("network"))
@@ -78,7 +81,11 @@ func uploadHandler(c *gin.Context) {
 			c.String(http.StatusBadRequest, err.Error())
 		}
 
-		go spProgramRW(port, board, board_rewrite, path, commandline, extraInfo)
+		if board_rewrite != "" {
+			board = board_rewrite
+		}
+
+		go spProgramRW(port, board, path, commandline, extraInfo)
 	}
 }
 
diff --git a/fakecerts/cert.pem b/fakecerts/cert.pem
deleted file mode 100644
index 7f648b91c..000000000
--- a/fakecerts/cert.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBDCCAewCCQD9LvUUyrKRXzANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJV
-UzEUMBIGA1UEChMLQXJkdWlubyBMTEMxCzAJBgNVBAsTAklUMRIwEAYDVQQDEwls
-b2NhbGhvc3QwHhcNMTUwNjExMDcyNTM2WhcNMTYwNjEwMDcyNTM2WjBEMQswCQYD
-VQQGEwJVUzEUMBIGA1UEChMLQXJkdWlubyBMTEMxCzAJBgNVBAsTAklUMRIwEAYD
-VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7
-3UTunwOvcw1hgFDN6bdHxssg/H33faLILjUJIBZwyRCVLc8zcExk7D0YLxnMBaxs
-t4DoPfMlAXiJ+GT3fiOaYTKHmGT3Qz85WC6Yngz9A/usDQSSYe9rI4k3oLNA4F+b
-6VU5FBj1V3nj79gs3IMEkTMnit+9HZ8PNlJEZSzEuxbrQdIV530H8MvHpUoCkinP
-uPur9zGFysIS1euoVybNKmVXcxmy0QHDNfOddH2SBaFD6QriucOIMW6AVHjseL9E
-A4IKKum88LLDiKPeu3gr12vNnjydLFGnNg+wlTnXUeeuXgMfNVaTDaIC8NEA+mSQ
-M6Ag6CymhWqpe/cqa45hAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBABCXfQZLhYI4
-B0VjgwDDRohTAhsSyaQvFEhGOx2Oo28RhKgY2xRtfjDlm7IzTLga4ApsHqIMf85F
-z4bpDDciwPhQDJ6a0ueXJv+xb6RGBnGAbnOiHPfgdd/P2vGfsR7mRaTdXJC4r9PI
-C3kzjwTlplaulLbHs5XIXImGvAvRX5sPmkqlBAQs8PVG4I71pKXo1M4kl7uzr96+
-+DKnVz2oACAPCW6zTlT/MlBH4nSeCQfGiE8iWAqODviONOQjFnaTKLw6d1YnbnLp
-1gokB8sk1TAM3qjb6giZpe945xISSNUDAVRW+3NIKag+qOURnN+QGi9ac3cPepXb
-4Kr4QM+kV+g=
------END CERTIFICATE-----
diff --git a/fakecerts/key.pem b/fakecerts/key.pem
deleted file mode 100644
index 26c54a281..000000000
--- a/fakecerts/key.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAu91E7p8Dr3MNYYBQzem3R8bLIPx9932iyC41CSAWcMkQlS3P
-M3BMZOw9GC8ZzAWsbLeA6D3zJQF4ifhk934jmmEyh5hk90M/OVgumJ4M/QP7rA0E
-kmHvayOJN6CzQOBfm+lVORQY9Vd54+/YLNyDBJEzJ4rfvR2fDzZSRGUsxLsW60HS
-Fed9B/DLx6VKApIpz7j7q/cxhcrCEtXrqFcmzSplV3MZstEBwzXznXR9kgWhQ+kK
-4rnDiDFugFR47Hi/RAOCCirpvPCyw4ij3rt4K9drzZ48nSxRpzYPsJU511Hnrl4D
-HzVWkw2iAvDRAPpkkDOgIOgspoVqqXv3KmuOYQIDAQABAoIBADzL4df7W/z2ldj8
-5qmMvkSbP/d/3uAuJ3TzBzTZzolXeGdeuNRjvkVW8nqWG5ocJ+3TAQOnsL1EGZdE
-7J/vkWQPmoOpPNuMRrSvJf08AOYM2PCYEeexjAK3MFvxRLF1K1vQikT7jQww8ABl
-CSeTgU8EEnL0jW2tXWFV6g+6Ul+jwfq5IvbUpMsMOPuUEQy85rm04bCw/vUnhZXk
-gFSpAp5mKPI6J/v2fkJTjgxi0wURxHKFdH/dFr69k9G7Vv9L8meiZYwA0QsYcmeJ
-EAGpZHQXpTCmmmzWM85vz9vg4qUBwF8ypXcWjuqfjAopXvuenyIkfa9paDriRnNM
-A3JmUQECgYEA66fdY8cU1cJw5jmhc10mi6lDz0Xswba4kaYnb2fwr7tlkBFzYYVn
-GY6f2fNxdx8ZpOCYzembJ67qCXODw4LLle1slgHzOWHQ4RJd2EDw/i9woPHGsT3a
-TIk0tX3wOjtJJEXNzQEiqcDqGqrpY3pnm4lFGR4RSE6vIYfPwyO9y7ECgYEAzBUv
-hKy19GSFK8tkYa/WcmNMCGwLJB0pEMuT3heDjSEKYNaTh8YcIpeEY3aCWbMKxWdu
-O5TIouyNJHtm4Q0umG08ZGekLTZy22it2UJabROvHVHeSnPki9a12Uc3KgB6mBzb
-nnHXQ8hR60o0GTPMudVW12aZh9gy+EcGWQEwibECgYAIdQ3M9ugYg9HpcUgKC93G
-RVzZo3jLuvMExyHDLcfFhsQuV3H8nf0FPcke2U3JKKSS9jyFp0yaL/zWOf8QlTEZ
-QFwVRhykgo4shaw4hpwfgzRXklW/Jqke9g2eNdbZQCdv1dF8+f10eifsrRWTLGFr
-g5GnRRz5q1k9qtIZ/r5hAQKBgCegMXLMaiQC5IylWF9TWgsp1piICf3AH7ogHRzx
-adycPrygzVJ+l/Xrf4wkdZjfF8dOluvArthbn+gmgcpO2e5Ev4YrTYht2w1ZHPBj
-XtVxDf5eaBACwqyYSwTePArOvv8ME2SHbCnAGo/Z/5WpJiYrE0qNpF/pDbSBbe0Y
-OwlxAoGAdPEjpeeCpyUcoJMVnIxM7AtsOqomd0lBrRgRq05FYvGMAygoKzz+OBp+
-VgptcGJp+6t5MY9R3asRaFp3rOcXvX5R4wBMfijlzoezMEFZN/+xpm7LN2E0domO
-xyku5Kcn9G/KTxCduepOtjqoNYkKYAcrdkmRfZ9C9xvuB2lKjk8=
------END RSA PRIVATE KEY-----
diff --git a/hub.go b/hub.go
index 421989701..b03ec0a75 100755
--- a/hub.go
+++ b/hub.go
@@ -57,6 +57,7 @@ func (h *hub) run() {
 			c.send <- []byte("{\"Version\" : \"" + version + "\"} ")
 			c.send <- []byte("{\"Commands\" : [\"list\", \"open [portName] [baud] [bufferAlgorithm (optional)]\", \"send [portName] [cmd]\", \"sendnobuf [portName] [cmd]\", \"close [portName]\", \"bufferalgorithms\", \"baudrates\", \"restart\", \"exit\", \"program [portName] [board:name] [$path/to/filename/without/extension]\", \"programfromurl [portName] [board:name] [urlToHexFile]\"]} ")
 			c.send <- []byte("{\"Hostname\" : \"" + *hostname + "\"} ")
+			c.send <- []byte("{\"OS\" : \"" + runtime.GOOS + "\"} ")
 		case c := <-h.unregister:
 			delete(h.connections, c)
 			// put close in func cuz it was creating panics and want
@@ -216,11 +217,10 @@ func checkCmd(m []byte) {
 	//log.Print("Done with checkCmd")
 }
 
-var multi_writer = io.MultiWriter(&logger_ws, os.Stderr)
-
 func logAction(sl string) {
 	if strings.HasPrefix(sl, "log on") {
 		*logDump = "on"
+		multi_writer := io.MultiWriter(&logger_ws, os.Stderr)
 		log.SetOutput(multi_writer)
 	} else if strings.HasPrefix(sl, "log off") {
 		*logDump = "off"
diff --git a/info.go b/info.go
index 194d46e4c..6fe3df038 100644
--- a/info.go
+++ b/info.go
@@ -7,7 +7,7 @@ import (
 func infoHandler(c *gin.Context) {
 	c.JSON(200, gin.H{
 		"http":  "http://localhost" + port,
-		"https": "https://localhost" + port,
+		"https": "https://localhost" + portSSL,
 		"ws":    "ws://localhost" + port,
 		"wss":   "wss://localhost" + portSSL,
 	})
diff --git a/main.go b/main.go
index 99391714d..e04ba5477 100755
--- a/main.go
+++ b/main.go
@@ -39,10 +39,12 @@ var (
 	hostname       = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")
 	updateUrl      = flag.String("updateUrl", "", "")
 	appName        = flag.String("appName", "", "")
+	genCert        = flag.Bool("generateCert", false, "")
 	globalToolsMap = make(map[string]string)
 	tempToolsPath  = createToolsDir()
 	port           string
 	portSSL        string
+	origins        = flag.String("origins", "", "Allowed origin list for CORS")
 )
 
 type NullWriter int
@@ -53,7 +55,7 @@ type logWriter struct{}
 
 func (u *logWriter) Write(p []byte) (n int, err error) {
 	h.broadcastSys <- p
-	return 0, nil
+	return len(p), nil
 }
 
 var logger_ws logWriter
@@ -77,6 +79,11 @@ func main() {
 
 	flag.Parse()
 
+	if *genCert == true {
+		generateCertificates()
+		os.Exit(0)
+	}
+
 	if *hibernate == false {
 
 		go func() {
@@ -109,6 +116,20 @@ func main() {
 				iniflags.Parse()
 			}
 
+			// move CORS to config file compatibility, Vagrant version
+			if *origins == "" {
+				log.Println("Patching config.ini for compatibility")
+				f, err := os.OpenFile(dest+"/"+*configIni, os.O_APPEND|os.O_WRONLY, 0666)
+				if err != nil {
+					panic(err)
+				}
+				_, err = f.WriteString("\norigins = http://webide.arduino.cc:8080\n")
+				if err != nil {
+					panic(err)
+				}
+				f.Close()
+				restart("")
+			}
 			//log.SetFormatter(&log.JSONFormatter{})
 
 			log.SetLevel(log.InfoLevel)
@@ -194,8 +215,14 @@ func main() {
 
 			socketHandler := wsHandler().ServeHTTP
 
+			extraOriginStr := "https://create.arduino.cc, http://create.arduino.cc, https://create-dev.arduino.cc, http://create-dev.arduino.cc, http://create-staging.arduino.cc, https://create-staging.arduino.cc"
+
+			for i := 8990; i < 9001; i++ {
+				extraOriginStr = extraOriginStr + ", http://localhost:" + strconv.Itoa(i) + ", https://localhost:" + strconv.Itoa(i)
+			}
+
 			r.Use(cors.Middleware(cors.Config{
-				Origins:         "https://create.arduino.cc, http://create.arduino.cc, https://create-dev.arduino.cc, http://create-dev.arduino.cc, http://webide.arduino.cc:8080, http://create-staging.arduino.cc, https://create-staging.arduino.cc, http://localhost:8989, https://localhost:8990",
+				Origins:         *origins + ", " + extraOriginStr,
 				Methods:         "GET, PUT, POST, DELETE",
 				RequestHeaders:  "Origin, Authorization, Content-Type",
 				ExposedHeaders:  "",
@@ -204,7 +231,10 @@ func main() {
 				ValidateHeaders: false,
 			}))
 
+			r.LoadHTMLFiles("templates/nofirefox.html")
+
 			r.GET("/", homeHandler)
+			r.GET("/certificate.crt", certHandler)
 			r.POST("/upload", uploadHandler)
 			r.GET("/socket.io/", socketHandler)
 			r.POST("/socket.io/", socketHandler)
@@ -212,6 +242,11 @@ func main() {
 			r.Handle("WSS", "/socket.io/", socketHandler)
 			r.GET("/info", infoHandler)
 			go func() {
+				// check if certificates exist; if not, use plain http
+				if _, err := os.Stat(filepath.Join(dest, "cert.pem")); os.IsNotExist(err) {
+					return
+				}
+
 				start := 8990
 				end := 9000
 				i := start
@@ -270,7 +305,7 @@ const homeTemplateHtml = `<!DOCTYPE html>
     var log = document.getElementById('log');
     var pause = document.getElementById('myCheck');
     var messages = [];
-    var only_log = false;
+    var only_log = true;
 
     function appendLog(msg) {
 
@@ -361,7 +396,9 @@ body {
 <form id="form">
     <input type="submit" value="Send" />
     <input type="text" id="msg" size="64"/>
-    <input name="pause" type="checkbox" value="pause" id="myCheck"/> Pause <br>
+    <input name="pause" type="checkbox" value="pause" id="myCheck"/> Pause
+    <!--<input type="button" value="Install Certificate" onclick="window.open('http://localhost:8991/certificate.crt')" />-->
+</form>
 </form>
 </body>
 </html>
diff --git a/programmer.go b/programmer.go
index 167b2e703..e3bf5aa3b 100644
--- a/programmer.go
+++ b/programmer.go
@@ -7,8 +7,9 @@ import (
 	"fmt"
 	log "github.com/Sirupsen/logrus"
 	"github.com/facchinm/go-serial"
-	"github.com/kardianos/osext"
 	"github.com/mattn/go-shellwords"
+	"github.com/sfreiberg/simplessh"
+	"github.com/xrash/smetrics"
 	"io"
 	"mime/multipart"
 	"net/http"
@@ -41,11 +42,105 @@ type boardExtraInfo struct {
 	authdata             basicAuthData
 }
 
+// Scp uploads sourceFile to remote machine like native scp console app.
+func Scp(client *simplessh.Client, sourceFile, targetFile string) error {
+
+	session, err := client.SSHClient.NewSession()
+	if err != nil {
+		return err
+	}
+	defer session.Close()
+
+	src, srcErr := os.Open(sourceFile)
+
+	if srcErr != nil {
+		return srcErr
+	}
+
+	srcStat, statErr := src.Stat()
+
+	if statErr != nil {
+		return statErr
+	}
+
+	go func() {
+		w, _ := session.StdinPipe()
+
+		fmt.Fprintln(w, "C0644", srcStat.Size(), filepath.Base(targetFile))
+
+		if srcStat.Size() > 0 {
+			io.Copy(w, src)
+			fmt.Fprint(w, "\x00")
+			w.Close()
+		} else {
+			fmt.Fprint(w, "\x00")
+			w.Close()
+		}
+
+	}()
+
+	if err := session.Run("scp -t " + targetFile); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func spProgramSSHNetwork(portname string, boardname string, filePath string, commandline string, authdata basicAuthData) error {
+	log.Println("Starting network upload")
+	log.Println("Board Name: " + boardname)
+
+	if authdata.UserName == "" {
+		authdata.UserName = "root"
+	}
+
+	if authdata.Password == "" {
+		authdata.Password = "arduino"
+	}
+
+	ssh_client, err := simplessh.ConnectWithPassword(portname+":22", authdata.UserName, authdata.Password)
+	if err != nil {
+		log.Println("Error connecting via ssh")
+		return err
+	}
+	defer ssh_client.Close()
+
+	err = Scp(ssh_client, filePath, "/tmp/sketch"+filepath.Ext(filePath))
+	if err != nil {
+		log.Printf("Upload: %s\n", err)
+		return err
+	}
+
+	if commandline == "" {
+		// very special case for Yun (remove once AVR boards.txt is fixed)
+		commandline = "merge-sketch-with-bootloader.lua /tmp/sketch.hex && /usr/bin/run-avrdude /tmp/sketch.hex"
+	}
+
+	fmt.Println(commandline)
+
+	ssh_output, err := ssh_client.Exec(commandline)
+	if err == nil {
+		log.Printf("Flash: %s\n", ssh_output)
+		mapD := map[string]string{"ProgrammerStatus": "Busy", "Msg": string(ssh_output)}
+		mapB, _ := json.Marshal(mapD)
+		h.broadcastSys <- mapB
+	}
+	return err
+}
+
 func spProgramNetwork(portname string, boardname string, filePath string, authdata basicAuthData) error {
 
 	log.Println("Starting network upload")
 	log.Println("Board Name: " + boardname)
 
+	if authdata.UserName == "" {
+		authdata.UserName = "root"
+	}
+
+	if authdata.Password == "" {
+		authdata.Password = "arduino"
+	}
+
 	// Prepare a form that you will submit to that URL.
 	_url := "http://" + portname + "/data/upload_sketch_silent"
 	var b bytes.Buffer
@@ -110,23 +205,10 @@ func spProgramNetwork(portname string, boardname string, filePath string, authda
 		log.Errorf("bad status: %s", res.Status)
 		err = fmt.Errorf("bad status: %s", res.Status)
 	}
-
-	if err != nil {
-		log.Printf("Command finished with error: %v ", err)
-		mapD := map[string]string{"ProgrammerStatus": "Error", "Msg": "Could not program the board", "Output": "", "Err": "Could not program the board"}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-	} else {
-		log.Printf("Finished without error. Good stuff.")
-		mapD := map[string]string{"ProgrammerStatus": "Done", "Flash": "Ok", "Output": ""}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		// analyze stdin
-	}
 	return err
 }
 
-func spProgramLocal(portname string, boardname string, filePath string, commandline string, extraInfo boardExtraInfo) {
+func spProgramLocal(portname string, boardname string, filePath string, commandline string, extraInfo boardExtraInfo) error {
 
 	var err error
 	if extraInfo.use_1200bps_touch {
@@ -135,7 +217,7 @@ func spProgramLocal(portname string, boardname string, filePath string, commandl
 
 	if err != nil {
 		log.Println("Could not touch the port")
-		return
+		return err
 	}
 
 	log.Printf("Received commandline (unresolved):" + commandline)
@@ -152,19 +234,27 @@ func spProgramLocal(portname string, boardname string, filePath string, commandl
 	fmt.Println(runtimeVars)
 
 	for _, element := range runtimeVars {
+
+		// use string similarity to resolve a runtime var with a "similar" map element
+		if globalToolsMap[element] == "" {
+			max_similarity := 0.0
+			for i, candidate := range globalToolsMap {
+				similarity := smetrics.Jaro(element, i)
+				if similarity > 0.8 && similarity > max_similarity {
+					max_similarity = similarity
+					globalToolsMap[element] = candidate
+				}
+			}
+		}
+
 		commandline = strings.Replace(commandline, element, globalToolsMap[element], 1)
 	}
 
 	z, _ := shellwords.Parse(commandline)
-	spHandlerProgram(z[0], z[1:])
+	return spHandlerProgram(z[0], z[1:])
 }
 
-func spProgram(portname string, boardname string, filePath string, commandline string, extraInfo boardExtraInfo) {
-
-	spProgramRW(portname, boardname, "", filePath, commandline, extraInfo)
-}
-
-func spProgramRW(portname string, boardname string, boardname_rewrite string, filePath string, commandline string, extraInfo boardExtraInfo) {
+func spProgramRW(portname string, boardname string, filePath string, commandline string, extraInfo boardExtraInfo) {
 	compiling = true
 
 	defer func() {
@@ -175,24 +265,32 @@ func spProgramRW(portname string, boardname string, boardname_rewrite string, fi
 	var err error
 
 	if extraInfo.networkPort {
-		if boardname_rewrite != "" {
-			err = spProgramNetwork(portname, boardname_rewrite, filePath, extraInfo.authdata)
-		} else {
-			err = spProgramNetwork(portname, boardname, filePath, extraInfo.authdata)
-		}
+		err = spProgramNetwork(portname, boardname, filePath, extraInfo.authdata)
 		if err != nil {
-			mapD := map[string]string{"ProgrammerStatus": "Error", "Msg": "Could not program the board", "Output": "", "Err": err.Error()}
-			mapB, _ := json.Marshal(mapD)
-			h.broadcastSys <- mapB
+			// no http method available, try ssh upload
+			err = spProgramSSHNetwork(portname, boardname, filePath, commandline, extraInfo.authdata)
 		}
 	} else {
-		spProgramLocal(portname, boardname, filePath, commandline, extraInfo)
+		err = spProgramLocal(portname, boardname, filePath, commandline, extraInfo)
+	}
+
+	if err != nil {
+		log.Printf("Command finished with error: %v", err)
+		mapD := map[string]string{"ProgrammerStatus": "Error", "Msg": "Could not program the board"}
+		mapB, _ := json.Marshal(mapD)
+		h.broadcastSys <- mapB
+	} else {
+		log.Printf("Finished without error. Good stuff")
+		mapD := map[string]string{"ProgrammerStatus": "Done", "Flash": "Ok"}
+		mapB, _ := json.Marshal(mapD)
+		h.broadcastSys <- mapB
+		// analyze stdin
 	}
 }
 
 var oscmd *exec.Cmd
 
-func spHandlerProgram(flasher string, cmdString []string) {
+func spHandlerProgram(flasher string, cmdString []string) error {
 
 	// if runtime.GOOS == "darwin" {
 	// 	sh, _ := exec.LookPath("sh")
@@ -213,18 +311,18 @@ func spHandlerProgram(flasher string, cmdString []string) {
 		extension = ".exe"
 	}
 
-	oscmd = exec.Command(flasher+extension, cmdString...)
+	oscmd = exec.Command(flasher, cmdString...)
 
 	tellCommandNotToSpawnShell(oscmd)
 
 	stdout, err := oscmd.StdoutPipe()
 	if err != nil {
-		return
+		return err
 	}
 
 	stderr, err := oscmd.StderrPipe()
 	if err != nil {
-		return
+		return err
 	}
 
 	multi := io.MultiReader(stderr, stdout)
@@ -253,19 +351,7 @@ func spHandlerProgram(flasher string, cmdString []string) {
 
 	err = oscmd.Wait()
 
-	if err != nil {
-		log.Printf("Command finished with error: %v", err)
-		mapD := map[string]string{"ProgrammerStatus": "Error", "Msg": "Could not program the board"}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-	} else {
-		log.Printf("Finished without error. Good stuff")
-		mapD := map[string]string{"ProgrammerStatus": "Done", "Flash": "Ok"}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		// analyze stdin
-
-	}
+	return err
 }
 
 func spHandlerProgramKill() {
@@ -341,6 +427,11 @@ func touch_port_1200bps(portname string, wait_for_upload_port bool) (string, err
 	initialPortName := portname
 	log.Println("Restarting in bootloader mode")
 
+	before_reset_ports, _ := serial.GetPortsList()
+	log.Println(before_reset_ports)
+
+	var ports []string
+
 	mode := &serial.Mode{
 		BaudRate: 1200,
 		Vmin:     0,
@@ -356,220 +447,49 @@ func touch_port_1200bps(portname string, wait_for_upload_port bool) (string, err
 		log.Println(err)
 	}
 	port.Close()
-	time.Sleep(time.Second / 2.0)
 
 	timeout := false
 	go func() {
-		time.Sleep(2 * time.Second)
+		time.Sleep(10 * time.Second)
 		timeout = true
 	}()
 
-	// time.Sleep(time.Second / 4)
-	// wait for port to reappear
+	// wait for port to disappear
 	if wait_for_upload_port {
-		after_reset_ports, _ := serial.GetPortsList()
-		log.Println(after_reset_ports)
-		var ports []string
 		for {
 			ports, _ = serial.GetPortsList()
 			log.Println(ports)
-			time.Sleep(time.Millisecond * 200)
-			portname = findNewPortName(ports, after_reset_ports)
+			portname = findNewPortName(ports, before_reset_ports)
 			if portname != "" {
 				break
 			}
 			if timeout {
 				break
 			}
+			time.Sleep(time.Millisecond * 100)
 		}
 	}
 
-	if portname == "" {
-		portname = initialPortName
-	}
-	return portname, nil
-}
-
-func assembleCompilerCommand(boardname string, portname string, filePath string) (bool, string, []string) {
-
-	// get executable (self)path and use it as base for all other paths
-	execPath, _ := osext.Executable()
-
-	boardFields := strings.Split(boardname, ":")
-	if len(boardFields) != 3 {
-		mapD := map[string]string{"Err": "Board need to be specified in core:architecture:name format"}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		return false, "", nil
-	}
-	tempPath := (filepath.Dir(execPath) + "/" + boardFields[0] + "/hardware/" + boardFields[1] + "/boards.txt")
-	file, err := os.Open(tempPath)
-	if err != nil {
-		mapD := map[string]string{"Err": "Could not find board: " + boardname}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		log.Println("Error:", err)
-		return false, "", nil
-	}
-	scanner := bufio.NewScanner(file)
-
-	boardOptions := make(map[string]string)
-	uploadOptions := make(map[string]string)
-
-	for scanner.Scan() {
-		// map everything matching with boardname
-		if strings.Contains(scanner.Text(), boardFields[2]) {
-			arr := strings.Split(scanner.Text(), "=")
-			arr[0] = strings.Replace(arr[0], boardFields[2]+".", "", 1)
-			boardOptions[arr[0]] = arr[1]
-		}
-	}
-
-	if len(boardOptions) == 0 {
-		mapD := map[string]string{"Err": "Board " + boardFields[2] + " is not part of " + boardFields[0] + ":" + boardFields[1]}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		return false, "", nil
-	}
-
-	// filepath need special care; the project_name var is the filename minus its extension (hex or bin)
-	// if we are going to modify standard IDE files we also could pass ALL filename
-	filePath = strings.Trim(filePath, "\n")
-	boardOptions["build.path"] = filepath.Dir(filePath)
-	boardOptions["build.project_name"] = strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filepath.Base(filePath)))
-
-	file.Close()
-
-	// get infos about the programmer
-	tempPath = (filepath.Dir(execPath) + "/" + boardFields[0] + "/hardware/" + boardFields[1] + "/platform.txt")
-	file, err = os.Open(tempPath)
-	if err != nil {
-		mapD := map[string]string{"Err": "Could not find board: " + boardname}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		log.Println("Error:", err)
-		return false, "", nil
-	}
-	scanner = bufio.NewScanner(file)
-
-	tool := boardOptions["upload.tool"]
-
-	for scanner.Scan() {
-		// map everything matching with upload
-		if strings.Contains(scanner.Text(), tool) {
-			arr := strings.Split(scanner.Text(), "=")
-			uploadOptions[arr[0]] = arr[1]
-			arr[0] = strings.Replace(arr[0], "tools."+tool+".", "", 1)
-			boardOptions[arr[0]] = arr[1]
-			// we have a "=" in command line
-			if len(arr) > 2 {
-				boardOptions[arr[0]] = arr[1] + "=" + arr[2]
+	// wait for port to reappear
+	if wait_for_upload_port {
+		after_reset_ports, _ := serial.GetPortsList()
+		log.Println(after_reset_ports)
+		for {
+			ports, _ = serial.GetPortsList()
+			log.Println(ports)
+			portname = findNewPortName(ports, after_reset_ports)
+			if portname != "" {
+				break
 			}
-		}
-	}
-	file.Close()
-
-	// multiple verisons of the same programmer can be handled if "version" is specified
-	version := uploadOptions["runtime.tools."+tool+".version"]
-	path := (filepath.Dir(execPath) + "/" + boardFields[0] + "/tools/" + tool + "/" + version)
-	if err != nil {
-		mapD := map[string]string{"Err": "Could not find board: " + boardname}
-		mapB, _ := json.Marshal(mapD)
-		h.broadcastSys <- mapB
-		log.Println("Error:", err)
-		return false, "", nil
-	}
-
-	boardOptions["runtime.tools."+tool+".path"] = path
-
-	cmdline := boardOptions["upload.pattern"]
-	// remove cmd.path as it is handled differently
-	cmdline = strings.Replace(cmdline, "\"{cmd.path}\"", " ", 1)
-	cmdline = strings.Replace(cmdline, "\"{path}/{cmd}\"", " ", 1)
-	cmdline = strings.Replace(cmdline, "\"", "", -1)
-
-	initialPortName := portname
-
-	// some boards (eg. Leonardo, Yun) need a special procedure to enter bootloader
-	if boardOptions["upload.use_1200bps_touch"] == "true" {
-		// triggers bootloader mode
-		// the portname could change in this occasion (expecially on Windows) so change portname
-		// with the port which will reappear
-		log.Println("Restarting in bootloader mode")
-
-		mode := &serial.Mode{
-			BaudRate: 1200,
-			Vmin:     1,
-			Vtimeout: 0,
-		}
-		port, err := serial.OpenPort(portname, mode)
-		if err != nil {
-			log.Println(err)
-			return false, "", nil
-		}
-		//port.SetDTR(false)
-		port.Close()
-		time.Sleep(time.Second / 2.0)
-
-		timeout := false
-		go func() {
-			time.Sleep(2 * time.Second)
-			timeout = true
-		}()
-
-		// time.Sleep(time.Second / 4)
-		// wait for port to reappear
-		if boardOptions["upload.wait_for_upload_port"] == "true" {
-			after_reset_ports, _ := serial.GetPortsList()
-			log.Println(after_reset_ports)
-			var ports []string
-			for {
-				ports, _ = serial.GetPortsList()
-				log.Println(ports)
-				time.Sleep(time.Millisecond * 200)
-				portname = findNewPortName(ports, after_reset_ports)
-				if portname != "" {
-					break
-				}
-				if timeout {
-					break
-				}
+			if timeout {
+				break
 			}
+			time.Sleep(time.Millisecond * 100)
 		}
 	}
 
 	if portname == "" {
 		portname = initialPortName
 	}
-
-	boardOptions["serial.port"] = portname
-	boardOptions["serial.port.file"] = filepath.Base(portname)
-
-	// split the commandline in substrings and recursively replace mapped strings
-	cmdlineSlice := strings.Split(cmdline, " ")
-	var winded = true
-	for index, _ := range cmdlineSlice {
-		winded = true
-		for winded != false {
-			cmdlineSlice[index], winded = formatCmdline(cmdlineSlice[index], boardOptions)
-		}
-	}
-
-	tool = (filepath.Dir(execPath) + "/" + boardFields[0] + "/tools/" + tool + "/bin/" + tool)
-	// the file doesn't exist, we are on windows
-	if _, err := os.Stat(tool); err != nil {
-		tool = tool + ".exe"
-		// convert all "/" to "\"
-		tool = strings.Replace(tool, "/", "\\", -1)
-	}
-
-	// remove blanks from cmdlineSlice
-	var cmdlineSliceOut []string
-	for _, element := range cmdlineSlice {
-		if element != "" {
-			cmdlineSliceOut = append(cmdlineSliceOut, element)
-		}
-	}
-
-	return (tool != ""), tool, cmdlineSliceOut
+	return portname, nil
 }
diff --git a/update.go b/update.go
index e2ffea7b6..f6d85b882 100644
--- a/update.go
+++ b/update.go
@@ -38,11 +38,10 @@ import (
 	"errors"
 	"fmt"
 	log "github.com/Sirupsen/logrus"
-	"github.com/inconshreveable/go-update"
 	"github.com/kardianos/osext"
 	"github.com/kr/binarydist"
 	"github.com/pivotal-golang/archiver/extractor"
-	patch "github.com/sanderhahn/gozip/patchzip"
+	"gopkg.in/inconshreveable/go-update.v0"
 	"io"
 	"io/ioutil"
 	"math/rand"
@@ -69,57 +68,6 @@ func IsZip(path string) bool {
 	return false
 }
 
-func Zip(path string, dirs []string) (err error) {
-	if IsZip(path) {
-		return errors.New(path + " is already a zip file")
-	}
-
-	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
-	if err != nil {
-		return
-	}
-	defer f.Close()
-
-	startoffset, err := f.Seek(0, os.SEEK_END)
-	if err != nil {
-		return
-	}
-
-	w := patch.NewWriterAt(f, startoffset)
-
-	for _, dir := range dirs {
-		err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
-			if err != nil {
-				return err
-			}
-
-			fh, err := patch.FileInfoHeader(info)
-			if err != nil {
-				return err
-			}
-			fh.Name = path
-
-			p, err := w.CreateHeader(fh)
-			if err != nil {
-				return err
-			}
-			if !info.IsDir() {
-				content, err := ioutil.ReadFile(path)
-				if err != nil {
-					return err
-				}
-				_, err = p.Write(content)
-				if err != nil {
-					return err
-				}
-			}
-			return err
-		})
-	}
-	err = w.Close()
-	return
-}
-
 func Unzip(zippath string, destination string) (err error) {
 	r, err := zip.OpenReader(zippath)
 	if err != nil {