diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go
index c50551f27bbf86cf55e9f4b45e76ce0822ded897..a49282949e69bf7089bef3d7fcfe07bc45ed88fa 100644
--- a/cmd/ipfs/daemon.go
+++ b/cmd/ipfs/daemon.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	_ "net/http/pprof"
 	"os"
+	"sort"
 	"strings"
 	"sync"
 
@@ -18,6 +19,7 @@ import (
 	commands "github.com/ipfs/go-ipfs/core/commands"
 	corehttp "github.com/ipfs/go-ipfs/core/corehttp"
 	"github.com/ipfs/go-ipfs/core/corerouting"
+	conn "github.com/ipfs/go-ipfs/p2p/net/conn"
 	peer "github.com/ipfs/go-ipfs/p2p/peer"
 	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
 	util "github.com/ipfs/go-ipfs/util"
@@ -31,7 +33,8 @@ const (
 	writableKwd               = "writable"
 	ipfsMountKwd              = "mount-ipfs"
 	ipnsMountKwd              = "mount-ipns"
-	unrestrictedApiAccess     = "unrestricted-api"
+	unrestrictedApiAccessKwd  = "unrestricted-api"
+	unencryptTransportKwd     = "disable-transport-encryption"
 	// apiAddrKwd    = "address-api"
 	// swarmAddrKwd  = "address-swarm"
 )
@@ -75,7 +78,8 @@ the port as you would other services or database (firewall, authenticated proxy,
 		cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
 		cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount)"),
 		cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount)"),
-		cmds.BoolOption(unrestrictedApiAccess, "Allow API access to unlisted hashes"),
+		cmds.BoolOption(unrestrictedApiAccessKwd, "Allow API access to unlisted hashes"),
+		cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
 
 		// TODO: add way to override addresses. tricky part: updating the config if also --init.
 		// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
@@ -109,6 +113,14 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
 		}
 	}()
 
+	// check transport encryption flag.
+	unencrypted, _, _ := req.Option(unencryptTransportKwd).Bool()
+	if unencrypted {
+		log.Warningf(`Running with --%s: All connections are UNENCRYPTED.
+		You will not be able to connect to regular encrypted networks.`, unencryptTransportKwd)
+		conn.EncryptConnections = false
+	}
+
 	// first, whether user has provided the initialization flag. we may be
 	// running in an uninitialized state.
 	initialize, _, err := req.Option(initOptionKwd).Bool()
@@ -179,6 +191,8 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
 		return
 	}
 
+	printSwarmAddrs(node)
+
 	defer func() {
 		// We wait for the node to close first, as the node has children
 		// that it will wait for before closing, such as the API server.
@@ -256,9 +270,9 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
 	apiMaddr = apiLis.Multiaddr()
 	fmt.Printf("API server listening on %s\n", apiMaddr)
 
-	unrestricted, _, err := req.Option(unrestrictedApiAccess).Bool()
+	unrestricted, _, err := req.Option(unrestrictedApiAccessKwd).Bool()
 	if err != nil {
-		return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccess, err), nil
+		return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccessKwd, err), nil
 	}
 
 	apiGw := corehttp.NewGateway(corehttp.GatewayConfig{
@@ -305,6 +319,19 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
 	return nil, errc
 }
 
+// printSwarmAddrs prints the addresses of the host
+func printSwarmAddrs(node *core.IpfsNode) {
+	var addrs []string
+	for _, addr := range node.PeerHost.Addrs() {
+		addrs = append(addrs, addr.String())
+	}
+	sort.Sort(sort.StringSlice(addrs))
+
+	for _, addr := range addrs {
+		fmt.Printf("Swarm listening on %s\n", addr)
+	}
+}
+
 // serveHTTPGateway collects options, creates listener, prints status message and starts serving requests
 func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
 	cfg, err := req.Context().GetConfig()
diff --git a/core/commands/id.go b/core/commands/id.go
index ea5e4cd549a02b784a972a23f8f4ab29fb80ce66..31af08067735fe8317fc01805e8bf2bd0449035d 100644
--- a/core/commands/id.go
+++ b/core/commands/id.go
@@ -46,6 +46,7 @@ ipfs id supports the format option for output with the following keys:
 <aver>: agent version
 <pver>: protocol version
 <pubkey>: public key
+<addrs>: addresses (newline delimited)
 `,
 	},
 	Arguments: []cmds.Argument{
@@ -119,6 +120,9 @@ ipfs id supports the format option for output with the following keys:
 				output = strings.Replace(output, "<aver>", val.AgentVersion, -1)
 				output = strings.Replace(output, "<pver>", val.ProtocolVersion, -1)
 				output = strings.Replace(output, "<pubkey>", val.PublicKey, -1)
+				output = strings.Replace(output, "<addrs>", strings.Join(val.Addresses, "\n"), -1)
+				output = strings.Replace(output, "\\n", "\n", -1)
+				output = strings.Replace(output, "\\t", "\t", -1)
 				return strings.NewReader(output), nil
 			} else {
 
diff --git a/core/commands/swarm.go b/core/commands/swarm.go
index bd7b943243a461c652256fbde64e68875a30a4cf..efb6fc1e7a6083110032b960fb91e9c0b7b9fbc7 100644
--- a/core/commands/swarm.go
+++ b/core/commands/swarm.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"path"
 	"sort"
 
 	cmds "github.com/ipfs/go-ipfs/commands"
@@ -91,6 +92,9 @@ var swarmAddrsCmd = &cmds.Command{
 ipfs swarm addrs lists all addresses this node is aware of.
 `,
 	},
+	Subcommands: map[string]*cmds.Command{
+		"local": swarmAddrsLocalCmd,
+	},
 	Run: func(req cmds.Request, res cmds.Response) {
 
 		n, err := req.Context().GetNode()
@@ -144,6 +148,50 @@ ipfs swarm addrs lists all addresses this node is aware of.
 	Type: addrMap{},
 }
 
+var swarmAddrsLocalCmd = &cmds.Command{
+	Helptext: cmds.HelpText{
+		Tagline: "List local addresses.",
+		ShortDescription: `
+ipfs swarm addrs local lists all local addresses the node is listening on.
+`,
+	},
+	Options: []cmds.Option{
+		cmds.BoolOption("id", "Show peer ID in addresses"),
+	},
+	Run: func(req cmds.Request, res cmds.Response) {
+
+		n, err := req.Context().GetNode()
+		if err != nil {
+			res.SetError(err, cmds.ErrNormal)
+			return
+		}
+
+		if n.PeerHost == nil {
+			res.SetError(errNotOnline, cmds.ErrClient)
+			return
+		}
+
+		showid, _, _ := req.Option("id").Bool()
+		id := n.Identity.Pretty()
+
+		var addrs []string
+		for _, addr := range n.PeerHost.Addrs() {
+			saddr := addr.String()
+			if showid {
+				saddr = path.Join(saddr, "ipfs", id)
+			}
+			addrs = append(addrs, saddr)
+		}
+		sort.Sort(sort.StringSlice(addrs))
+
+		res.SetOutput(&stringList{addrs})
+	},
+	Type: stringList{},
+	Marshalers: cmds.MarshalerMap{
+		cmds.Text: stringListMarshaler,
+	},
+}
+
 var swarmConnectCmd = &cmds.Command{
 	Helptext: cmds.HelpText{
 		Tagline: "Open connection to a given address",
diff --git a/p2p/net/conn/dial.go b/p2p/net/conn/dial.go
index 43831c3efef74c759575b4fbb24d30f3d46c4ae1..a9a1a7aaf939a33be42a252addcd27e00ae9d68b 100644
--- a/p2p/net/conn/dial.go
+++ b/p2p/net/conn/dial.go
@@ -60,7 +60,7 @@ func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (
 			return
 		}
 
-		if d.PrivateKey == nil {
+		if d.PrivateKey == nil || EncryptConnections == false {
 			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
 			connOut = c
 			return
diff --git a/p2p/net/conn/interface.go b/p2p/net/conn/interface.go
index 3a61911af82250e2dceb4f7b58af522d5d10a0f2..82008593057dd919124472696b270b43078e377d 100644
--- a/p2p/net/conn/interface.go
+++ b/p2p/net/conn/interface.go
@@ -93,3 +93,11 @@ type Listener interface {
 	// Any blocked Accept operations will be unblocked and return errors.
 	Close() error
 }
+
+// EncryptConnections is a global parameter because it should either be
+// enabled or _completely disabled_. I.e. a node should only be able to talk
+// to proper (encrypted) networks if it is encrypting all its transports.
+// Running a node with disabled transport encryption is useful to debug the
+// protocols, achieve implementation interop, or for private networks which
+// -- for whatever reason -- _must_ run unencrypted.
+var EncryptConnections = true
diff --git a/p2p/net/conn/listen.go b/p2p/net/conn/listen.go
index ea91e5a56d4ccfaf2abc606bdb9ece3876c1a43b..71b89d76703eddb30e86aa8553e37be970717436 100644
--- a/p2p/net/conn/listen.go
+++ b/p2p/net/conn/listen.go
@@ -107,7 +107,7 @@ func (l *listener) Accept() (net.Conn, error) {
 			return nil, err
 		}
 
-		if l.privk == nil {
+		if l.privk == nil || EncryptConnections == false {
 			log.Warning("listener %s listening INSECURELY!", l)
 			return c, nil
 		}
diff --git a/p2p/net/mock/mock_notif_test.go b/p2p/net/mock/mock_notif_test.go
index 1ef9fc6aaf3c681e686b6c4f54fb9909ffe99b02..d91403f8b2c76f2c95032b4495b0c046a013f53c 100644
--- a/p2p/net/mock/mock_notif_test.go
+++ b/p2p/net/mock/mock_notif_test.go
@@ -43,24 +43,30 @@ func TestNotifications(t *testing.T) {
 	for i, s := range nets {
 		n := notifiees[i]
 		for _, s2 := range nets {
-			cos := s.ConnsToPeer(s2.LocalPeer())
-			func() {
-				for i := 0; i < len(cos); i++ {
-					var c inet.Conn
-					select {
-					case c = <-n.connected:
-					case <-time.After(timeout):
-						t.Fatal("timeout")
-					}
-					for _, c2 := range cos {
-						if c == c2 {
-							t.Log("got notif for conn")
-							return
-						}
+			var actual []inet.Conn
+			for len(s.ConnsToPeer(s2.LocalPeer())) != len(actual) {
+				select {
+				case c := <-n.connected:
+					actual = append(actual, c)
+				case <-time.After(timeout):
+					t.Fatal("timeout")
+				}
+			}
+
+			expect := s.ConnsToPeer(s2.LocalPeer())
+			for _, c1 := range actual {
+				found := false
+				for _, c2 := range expect {
+					if c1 == c2 {
+						found = true
+						break
 					}
+				}
+				if !found {
 					t.Error("connection not found")
 				}
-			}()
+			}
+
 		}
 	}
 
diff --git a/p2p/net/swarm/swarm_notif_test.go b/p2p/net/swarm/swarm_notif_test.go
index d8b9bc2d2b78ffafd952aaa15a45917109ca0d86..b44b030d80fc16a579467906451891bcf299f867 100644
--- a/p2p/net/swarm/swarm_notif_test.go
+++ b/p2p/net/swarm/swarm_notif_test.go
@@ -11,8 +11,6 @@ import (
 )
 
 func TestNotifications(t *testing.T) {
-	t.Parallel()
-
 	ctx := context.Background()
 	swarms := makeSwarms(ctx, t, 5)
 	defer func() {
@@ -44,24 +42,30 @@ func TestNotifications(t *testing.T) {
 				continue
 			}
 
-			cos := s.ConnectionsToPeer(s2.LocalPeer())
-			func() {
-				for i := 0; i < len(cos); i++ {
-					var c inet.Conn
-					select {
-					case c = <-n.connected:
-					case <-time.After(timeout):
-						t.Fatal("timeout")
-					}
-					for _, c2 := range cos {
-						if c == c2 {
-							t.Log("got notif for conn", c)
-							return
-						}
+			var actual []inet.Conn
+			for len(s.ConnectionsToPeer(s2.LocalPeer())) != len(actual) {
+				select {
+				case c := <-n.connected:
+					actual = append(actual, c)
+				case <-time.After(timeout):
+					t.Fatal("timeout")
+				}
+			}
+
+			expect := s.ConnectionsToPeer(s2.LocalPeer())
+			for _, c1 := range actual {
+				found := false
+				for _, c2 := range expect {
+					if c1 == c2 {
+						found = true
+						break
 					}
-					t.Error("connection not found", c)
 				}
-			}()
+				if !found {
+					t.Error("connection not found")
+				}
+			}
+
 		}
 	}
 
diff --git a/p2p/net/swarm/swarm_test.go b/p2p/net/swarm/swarm_test.go
index 4c12a1b5d3b4ac42950164babc8ca02ee0e85732..6e53cc339ed696579f02675ea986fc067006864e 100644
--- a/p2p/net/swarm/swarm_test.go
+++ b/p2p/net/swarm/swarm_test.go
@@ -325,6 +325,6 @@ func TestFilterBounds(t *testing.T) {
 	case <-time.After(time.Second):
 		t.Fatal("should have gotten connection")
 	case <-conns:
-		fmt.Println("got connect")
+		t.Log("got connect")
 	}
 }
diff --git a/test/sharness/lib/test-lib.sh b/test/sharness/lib/test-lib.sh
index 73b55b343c8a37031a91eddcd797d56000395757..e257ba1e5c8a8c251c696cd7cf86fe240ddd3caf 100644
--- a/test/sharness/lib/test-lib.sh
+++ b/test/sharness/lib/test-lib.sh
@@ -193,8 +193,10 @@ test_config_ipfs_gateway_writable() {
 
 test_launch_ipfs_daemon() {
 
+	args=$1
+
 	test_expect_success "'ipfs daemon' succeeds" '
-		ipfs daemon >actual_daemon 2>daemon_err &
+		ipfs daemon $args >actual_daemon 2>daemon_err &
 	'
 
 	# we say the daemon is ready when the API server is ready.
diff --git a/test/sharness/t0060-daemon.sh b/test/sharness/t0060-daemon.sh
index e0aff7b5edf6b8bab7fe6ee45b7cb1ab30042cea..13a56fc1c28162f879d36a927d6dfc5049aa4a86 100755
--- a/test/sharness/t0060-daemon.sh
+++ b/test/sharness/t0060-daemon.sh
@@ -8,6 +8,8 @@ test_description="Test daemon command"
 
 . lib/test-lib.sh
 
+# TODO: randomize ports here once we add --config to ipfs daemon
+
 # this needs to be in a different test than "ipfs daemon --init" below
 test_expect_success "setup IPFS_PATH" '
   IPFS_PATH="$(pwd)/.ipfs" &&
@@ -17,7 +19,7 @@ test_expect_success "setup IPFS_PATH" '
 # NOTE: this should remove bootstrap peers (needs a flag)
 # TODO(cryptix):
 #  - we won't see daemon startup failure because we put the daemon in the background - fix: fork with exit code after api listen
-#  - also default ports: might clash with local clients. Failure in that case isn't clear as well because pollEndpoint just uses the already running node 
+#  - also default ports: might clash with local clients. Failure in that case isn't clear as well because pollEndpoint just uses the already running node
 test_expect_success "ipfs daemon --init launches" '
   ipfs daemon --init >actual_daemon 2>daemon_err &
 '
@@ -34,6 +36,11 @@ test_expect_success "'ipfs config Identity.PeerID' works" '
   ipfs config Identity.PeerID >config_peerId
 '
 
+test_expect_success "'ipfs swarm addrs local' works" '
+  ipfs swarm addrs local >local_addrs
+'
+
+
 # this is lifted straight from t0020-init.sh
 test_expect_success "ipfs peer id looks good" '
   PEERID=$(cat config_peerId) &&
@@ -58,6 +65,7 @@ test_expect_success "ipfs daemon output looks good" '
   echo "peer identity: $PEERID" >>expected_daemon &&
   echo "to get started, enter:" >>expected_daemon &&
   printf "\\n\\t$STARTFILE\\n\\n" >>expected_daemon &&
+  cat local_addrs | sed "s/^/Swarm listening on /" >>expected_daemon &&
   echo "API server listening on /ip4/127.0.0.1/tcp/5001" >>expected_daemon &&
   echo "Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080" >>expected_daemon &&
   test_cmp expected_daemon actual_daemon
@@ -91,6 +99,15 @@ test_expect_success "ipfs help output looks good" '
 	test_fsh cat help.txt
 '
 
+# check transport is encrypted
+
+test_expect_success 'transport should be encrypted' '
+  nc -w 5 localhost 4001 >swarmnc &&
+  grep -q "AES-256,AES-128" swarmnc &&
+  test_must_fail grep -q "/ipfs/identify" swarmnc ||
+	test_fsh cat swarmnc
+'
+
 # end same as in t0010
 
 test_expect_success "daemon is still running" '
diff --git a/test/sharness/t0061-daemon-opts.sh b/test/sharness/t0061-daemon-opts.sh
new file mode 100755
index 0000000000000000000000000000000000000000..549fa3e8fd3925a6120687045134d67dbe9edc70
--- /dev/null
+++ b/test/sharness/t0061-daemon-opts.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Juan Batiz-Benet
+# MIT Licensed; see the LICENSE file in this repository.
+#
+
+test_description="Test daemon command"
+
+. lib/test-lib.sh
+
+
+test_init_ipfs
+
+test_launch_ipfs_daemon '--unrestricted-api --disable-transport-encryption'
+
+gwyport=$PORT_GWAY
+apiport=$PORT_API
+
+test_expect_success 'api gateway should be unrestricted' '
+  echo "hello mars :$gwyport :$apiport" >expected &&
+  HASH=$(ipfs add -q expected) &&
+  curl -sfo actual1 "http://127.0.0.1:$gwyport/ipfs/$HASH" &&
+  curl -sfo actual2 "http://127.0.0.1:$apiport/ipfs/$HASH" &&
+  test_cmp expected actual1 &&
+  test_cmp expected actual2
+'
+
+# Odd. this fails here, but the inverse works on t0060-daemon.
+test_expect_success 'transport should be unencrypted' '
+  go-sleep 0.5s | nc localhost "$PORT_SWARM" >swarmnc &&
+  test_must_fail grep -q "AES-256,AES-128" swarmnc &&
+  grep -q "/ipfs/identify" swarmnc ||
+  test_fsh cat swarmnc
+'
+
+test_kill_ipfs_daemon
+
+test_done
diff --git a/test/sharness/t0110-gateway.sh b/test/sharness/t0110-gateway.sh
index 36a94c671c1eea9bdea19df3b41382cf4fd479e0..fbaa4cd2e7a96bb285753f670080bdf771673c32 100755
--- a/test/sharness/t0110-gateway.sh
+++ b/test/sharness/t0110-gateway.sh
@@ -33,6 +33,10 @@ test_expect_success "GET IPFS path output looks good" '
   rm actual
 '
 
+test_expect_success "GET IPFS path on API forbidden" '
+  test_curl_resp_http_code "http://127.0.0.1:$apiport/ipfs/$HASH" "HTTP/1.1 403 Forbidden"
+'
+
 test_expect_success "GET IPFS directory path succeeds" '
   mkdir dir &&
   echo "12345" >dir/test &&
diff --git a/test/sharness/t0140-swarm.sh b/test/sharness/t0140-swarm.sh
new file mode 100755
index 0000000000000000000000000000000000000000..229ba1676bb867cb2c1fcb77104b7c6729374c25
--- /dev/null
+++ b/test/sharness/t0140-swarm.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Jeromy Johnson
+# MIT Licensed; see the LICENSE file in this repository.
+#
+
+test_description="Test ipfs swarm command"
+
+. lib/test-lib.sh
+
+test_init_ipfs
+
+test_launch_ipfs_daemon
+
+test_expect_success 'disconnected: peers is empty' '
+	ipfs swarm peers >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'disconnected: addrs local has localhost' '
+	ipfs swarm addrs local >actual &&
+	grep "/ip4/127.0.0.1" actual
+'
+
+test_expect_success 'disconnected: addrs local matches ipfs id' '
+	ipfs id -f="<addrs>\\n" | sort >expected &&
+	ipfs swarm addrs local --id | sort >actual &&
+	test_cmp expected actual
+'
+
+test_kill_ipfs_daemon
+
+test_done