diff --git a/go.mod b/go.mod
index a7f87f0..07e0fec 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
 	github.com/minio/sha256-simd v0.1.1
 	github.com/mr-tron/base58 v1.1.3
 	github.com/multiformats/go-multiaddr v0.2.0
+	github.com/multiformats/go-multiaddr-net v0.1.1
 	github.com/multiformats/go-multihash v0.0.10
 	github.com/multiformats/go-varint v0.0.1
 	github.com/smola/gocompat v0.2.0
diff --git a/go.sum b/go.sum
index 57e096a..0b29900 100644
--- a/go.sum
+++ b/go.sum
@@ -28,8 +28,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
 github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ipfs/go-cid v0.0.3 h1:UIAh32wymBpStoe83YCzwVQQ5Oy/H0FdxvUS6DJDzms=
-github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
 github.com/ipfs/go-cid v0.0.4 h1:UlfXKrZx1DjZoBhQHmNHLC1fK1dUJDN20Y28A7s+gJ8=
 github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
 github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
@@ -67,10 +65,11 @@ github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
 github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
 github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
 github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
-github.com/multiformats/go-multiaddr v0.1.2 h1:HWYHNSyyllbQopmVIF5K7JKJugiah+L9/kuZKHbmNdQ=
-github.com/multiformats/go-multiaddr v0.1.2/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
+github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
 github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90=
 github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
+github.com/multiformats/go-multiaddr-net v0.1.1 h1:jFFKUuXTXv+3ARyHZi3XUqQO+YWMKgBdhEvuGRfnL6s=
+github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
 github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
 github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
 github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
diff --git a/host/helpers.go b/host/helpers.go
index 276eeae..aa7ae3a 100644
--- a/host/helpers.go
+++ b/host/helpers.go
@@ -3,7 +3,10 @@ package host
 import (
 	"errors"
 	"github.com/libp2p/go-libp2p-core/peer"
+	"github.com/libp2p/go-libp2p-core/peerstore"
 	"github.com/libp2p/go-libp2p-core/routing"
+	ma "github.com/multiformats/go-multiaddr"
+	manet "github.com/multiformats/go-multiaddr-net"
 )
 
 // InfoFromHost returns a peer.AddrInfo struct with the Host's ID and all of its Addrs.
@@ -16,11 +19,51 @@ func InfoFromHost(h Host) *peer.AddrInfo {
 
 // SignedRoutingStateFromHost returns a SignedRoutingState record containing
 // the Host's listen addresses, signed with the Host's private key.
-func SignedRoutingStateFromHost(h Host) (*routing.SignedRoutingState, error) {
+//
+// By default, only publicly routable addresses will be included.
+// To include loopback and LAN addresses, pass in the IncludeLocalAddrs option:
+//
+//    state := SignedRoutingStateFromHost(h, IncludeLocalAddrs)
+func SignedRoutingStateFromHost(h minimalHost, opts ...Option) (*routing.SignedRoutingState, error) {
+	cfg := config{}
+	for _, opt := range opts {
+		opt(&cfg)
+	}
+
 	privKey := h.Peerstore().PrivKey(h.ID())
 	if privKey == nil {
 		return nil, errors.New("unable to find host's private key in peerstore")
 	}
 
-	return routing.MakeSignedRoutingState(privKey, h.Addrs())
+	var addrs []ma.Multiaddr
+	if cfg.includeLocalAddrs {
+		addrs = h.Addrs()
+	} else {
+		for _, a := range h.Addrs() {
+			if manet.IsPublicAddr(a) {
+				addrs = append(addrs, a)
+			}
+		}
+	}
+
+	return routing.MakeSignedRoutingState(privKey, addrs)
+}
+
+// IncludeLocalAddrs can be passed into SignedRoutingStateFromHost to
+// produce a routing record with LAN and loopback addresses included.
+func IncludeLocalAddrs(cfg *config) {
+	cfg.includeLocalAddrs = true
+}
+
+// minimalHost is the subset of the Host interface that's required by
+// SignedRoutingStateFromHost.
+type minimalHost interface {
+	ID() peer.ID
+	Peerstore() peerstore.Peerstore
+	Addrs() []ma.Multiaddr
+}
+
+type Option func(cfg *config)
+type config struct {
+	includeLocalAddrs bool
 }
diff --git a/host/helpers_test.go b/host/helpers_test.go
new file mode 100644
index 0000000..52cf261
--- /dev/null
+++ b/host/helpers_test.go
@@ -0,0 +1,131 @@
+package host
+
+import (
+	"context"
+	"github.com/libp2p/go-libp2p-core/crypto"
+	"github.com/libp2p/go-libp2p-core/peer"
+	"github.com/libp2p/go-libp2p-core/peerstore"
+	"github.com/libp2p/go-libp2p-core/routing"
+	"github.com/libp2p/go-libp2p-core/test"
+	ma "github.com/multiformats/go-multiaddr"
+	"testing"
+	"time"
+)
+
+type mockHost struct {
+	fixedPrivKey crypto.PrivKey
+	addrs        []ma.Multiaddr
+}
+
+func (h *mockHost) Addrs() []ma.Multiaddr {
+	return h.addrs
+}
+
+func (h *mockHost) Peerstore() peerstore.Peerstore {
+	return mockPeerstore{fixedPrivKey: h.fixedPrivKey}
+}
+
+func (*mockHost) ID() peer.ID { return "" }
+
+type mockPeerstore struct {
+	fixedPrivKey crypto.PrivKey
+}
+
+// the one method I care about...
+func (m mockPeerstore) PrivKey(peer.ID) crypto.PrivKey {
+	return m.fixedPrivKey
+}
+
+// so many other things in the Peerstore interface...
+func (m mockPeerstore) Close() error                                                { return nil }
+func (m mockPeerstore) AddAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration)     {}
+func (m mockPeerstore) AddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration) {}
+func (m mockPeerstore) AddCertifiedAddrs(s *routing.SignedRoutingState, ttl time.Duration) error {
+	return nil
+}
+func (m mockPeerstore) SetAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration)           {}
+func (m mockPeerstore) SetAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration)       {}
+func (m mockPeerstore) UpdateAddrs(p peer.ID, oldTTL time.Duration, newTTL time.Duration) {}
+func (m mockPeerstore) Addrs(p peer.ID) []ma.Multiaddr                                    { return nil }
+func (m mockPeerstore) CertifiedAddrs(p peer.ID) []ma.Multiaddr                           { return nil }
+func (m mockPeerstore) AddrStream(context.Context, peer.ID) <-chan ma.Multiaddr           { return nil }
+func (m mockPeerstore) ClearAddrs(p peer.ID)                                              {}
+func (m mockPeerstore) PeersWithAddrs() peer.IDSlice                                      { return nil }
+func (m mockPeerstore) SignedRoutingState(p peer.ID) *routing.SignedRoutingState          { return nil }
+func (m mockPeerstore) Get(p peer.ID, key string) (interface{}, error)                    { return nil, nil }
+func (m mockPeerstore) Put(p peer.ID, key string, val interface{}) error                  { return nil }
+func (m mockPeerstore) RecordLatency(peer.ID, time.Duration)                              {}
+func (m mockPeerstore) LatencyEWMA(peer.ID) time.Duration                                 { return 0 }
+func (m mockPeerstore) GetProtocols(peer.ID) ([]string, error)                            { return nil, nil }
+func (m mockPeerstore) AddProtocols(peer.ID, ...string) error                             { return nil }
+func (m mockPeerstore) SetProtocols(peer.ID, ...string) error                             { return nil }
+func (m mockPeerstore) RemoveProtocols(peer.ID, ...string) error                          { return nil }
+func (m mockPeerstore) SupportsProtocols(peer.ID, ...string) ([]string, error)            { return nil, nil }
+func (m mockPeerstore) PeerInfo(peer.ID) peer.AddrInfo                                    { return peer.AddrInfo{} }
+func (m mockPeerstore) Peers() peer.IDSlice                                               { return nil }
+func (m mockPeerstore) PubKey(peer.ID) crypto.PubKey                                      { return nil }
+func (m mockPeerstore) AddPubKey(peer.ID, crypto.PubKey) error                            { return nil }
+func (m mockPeerstore) AddPrivKey(peer.ID, crypto.PrivKey) error                          { return nil }
+func (m mockPeerstore) PeersWithKeys() peer.IDSlice                                       { return nil }
+
+func TestSignedRoutingStateFromHost_FailsIfPrivKeyIsNil(t *testing.T) {
+	_, err := SignedRoutingStateFromHost(&mockHost{})
+	test.ExpectError(t, err, "expected generating signed routing state to fail when host private key is nil")
+}
+
+func TestSignedRoutingStateFromHost_AddrFiltering(t *testing.T) {
+	localAddrs := parseAddrs(t,
+		// loopback
+		"/ip4/127.0.0.1/tcp/42",
+		"/ip6/::1/tcp/9999",
+
+		// ip4 LAN reserved
+		"/ip4/10.0.0.1/tcp/1234",
+		"/ip4/100.64.0.123/udp/10101",
+		"/ip4/172.16.0.254/tcp/2345",
+		"/ip4/192.168.1.4/udp/1600",
+
+		// link local
+		"/ip4/169.254.0.1/udp/1234",
+		"/ip6/fe80::c001:37ff:fe6c:0/tcp/42",
+	)
+
+	wanAddrs := parseAddrs(t,
+		"/ip4/1.2.3.4/tcp/42",
+		"/ip4/8.8.8.8/udp/1234",
+		"/ip6/2607:f8b0:4002:c02::8a/udp/1234",
+		"/ip6/2a03:2880:f111:83:face:b00c:0:25de/udp/2345/quic",
+	)
+
+	priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	host := &mockHost{
+		fixedPrivKey: priv,
+		addrs:        append(localAddrs, wanAddrs...),
+	}
+
+	// test with local addrs
+	state, err := SignedRoutingStateFromHost(host, IncludeLocalAddrs)
+	if err != nil {
+		t.Fatalf("error generating routing state: %v", err)
+	}
+	test.AssertAddressesEqual(t, host.addrs, state.Addrs)
+
+	// test filtering out local addrs
+	state, err = SignedRoutingStateFromHost(host)
+	if err != nil {
+		t.Fatalf("error generating routing state: %v", err)
+	}
+	test.AssertAddressesEqual(t, wanAddrs, state.Addrs)
+}
+
+func parseAddrs(t *testing.T, addrStrings ...string) (out []ma.Multiaddr) {
+	t.Helper()
+	for _, s := range addrStrings {
+		out = append(out, ma.StringCast(s))
+	}
+	return out
+}