diff --git a/cmd/ipfs/bootstrap.go b/cmd/ipfs/bootstrap.go
new file mode 100644
index 000000000..68a1ad633
--- /dev/null
+++ b/cmd/ipfs/bootstrap.go
@@ -0,0 +1,251 @@
+package main
+
+import (
+	"errors"
+	"strings"
+
+	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
+	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
+	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
+	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
+
+	config "github.com/jbenet/go-ipfs/config"
+	peer "github.com/jbenet/go-ipfs/peer"
+	u "github.com/jbenet/go-ipfs/util"
+)
+
+var cmdIpfsBootstrap = &commander.Command{
+	UsageLine: "bootstrap",
+	Short:     "Show a list of bootstrapped addresses.",
+	Long: `ipfs bootstrap - show, or manipulate bootstrap node addresses
+
+Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'.
+
+Commands:
+
+	list               Show the boostrap list.
+	add <address>      Add a node's address to the bootstrap list.
+	remove <address>   Remove an address from the bootstrap list.
+
+` + bootstrapSecurityWarning,
+	Run: bootstrapListCmd,
+	Subcommands: []*commander.Command{
+		cmdIpfsBootstrapRemove,
+		cmdIpfsBootstrapAdd,
+		cmdIpfsBootstrapList,
+	},
+	Flag: *flag.NewFlagSet("ipfs-bootstrap", flag.ExitOnError),
+}
+
+var cmdIpfsBootstrapRemove = &commander.Command{
+	UsageLine: "remove <address | peerid>",
+	Short:     "Remove addresses from the bootstrap list.",
+	Long: `ipfs bootstrap remove - remove addresses from the bootstrap list
+` + bootstrapSecurityWarning,
+	Run:  bootstrapRemoveCmd,
+	Flag: *flag.NewFlagSet("ipfs-bootstrap-remove", flag.ExitOnError),
+}
+
+var cmdIpfsBootstrapAdd = &commander.Command{
+	UsageLine: "add <address | peerid>",
+	Short:     "Add addresses to the bootstrap list.",
+	Long: `ipfs bootstrap add - add addresses to the bootstrap list
+` + bootstrapSecurityWarning,
+	Run:  bootstrapAddCmd,
+	Flag: *flag.NewFlagSet("ipfs-bootstrap-add", flag.ExitOnError),
+}
+
+var cmdIpfsBootstrapList = &commander.Command{
+	UsageLine: "list",
+	Short:     "Show addresses in the bootstrap list.",
+	Run:       bootstrapListCmd,
+	Flag:      *flag.NewFlagSet("ipfs-bootstrap-list", flag.ExitOnError),
+}
+
+func bootstrapRemoveCmd(c *commander.Command, inp []string) error {
+
+	if len(inp) == 0 {
+		return errors.New("remove: no address or peerid specified")
+	}
+
+	toRemove, err := bootstrapInputToPeers(inp)
+	if err != nil {
+		return err
+	}
+
+	cfg, err := getConfig(c)
+	if err != nil {
+		return err
+	}
+
+	keep := []*config.BootstrapPeer{}
+	remove := []*config.BootstrapPeer{}
+
+	// function to filer what to keep
+	shouldKeep := func(bp *config.BootstrapPeer) bool {
+		for _, skipBP := range toRemove {
+
+			// IDs must match to skip.
+			if bp.PeerID != skipBP.PeerID {
+				continue
+			}
+
+			// if Addresses match, or skipBP has no addr (wildcard)
+			if skipBP.Address == bp.Address || skipBP.Address == "" {
+				return false
+			}
+		}
+		return true
+	}
+
+	// filter all the existing peers
+	for _, currBP := range cfg.Bootstrap {
+		if shouldKeep(currBP) {
+			keep = append(keep, currBP)
+		} else {
+			remove = append(remove, currBP)
+		}
+	}
+
+	// if didn't remove anyone, bail.
+	if len(keep) == len(cfg.Bootstrap) {
+		return errors.New("remove: peer given did not match any in list")
+	}
+
+	// write new config
+	cfg.Bootstrap = keep
+	if err := writeConfig(c, cfg); err != nil {
+		return err
+	}
+
+	for _, bp := range remove {
+		u.POut("removed %s\n", bp)
+	}
+	return nil
+}
+
+func bootstrapAddCmd(c *commander.Command, inp []string) error {
+
+	if len(inp) == 0 {
+		return errors.New("add: no address specified")
+	}
+
+	toAdd, err := bootstrapInputToPeers(inp)
+	if err != nil {
+		return err
+	}
+
+	cfg, err := getConfig(c)
+	if err != nil {
+		return err
+	}
+
+	// function to check whether a peer is already in the list.
+	combine := func(lists ...[]*config.BootstrapPeer) []*config.BootstrapPeer {
+
+		set := map[string]struct{}{}
+		final := []*config.BootstrapPeer{}
+
+		for _, list := range lists {
+			for _, peer := range list {
+				// if already in the set, continue
+				_, found := set[peer.String()]
+				if found {
+					continue
+				}
+
+				set[peer.String()] = struct{}{}
+				final = append(final, peer)
+			}
+		}
+		return final
+	}
+
+	// combine both lists, removing dups.
+	cfg.Bootstrap = combine(cfg.Bootstrap, toAdd)
+	if err := writeConfig(c, cfg); err != nil {
+		return err
+	}
+
+	for _, bp := range toAdd {
+		u.POut("added %s\n", bp)
+	}
+	return nil
+}
+
+func bootstrapListCmd(c *commander.Command, inp []string) error {
+
+	cfg, err := getConfig(c)
+	if err != nil {
+		return err
+	}
+
+	for _, bp := range cfg.Bootstrap {
+		u.POut("%s\n", bp)
+	}
+
+	return nil
+}
+
+func writeConfig(c *commander.Command, cfg *config.Config) error {
+
+	confdir, err := getConfigDir(c)
+	if err != nil {
+		return err
+	}
+
+	filename, err := config.Filename(confdir)
+	if err != nil {
+		return err
+	}
+
+	return config.WriteConfigFile(filename, cfg)
+}
+
+func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) {
+	split := func(addr string) (string, string) {
+		idx := strings.LastIndex(addr, "/")
+		if idx == -1 {
+			return "", addr
+		}
+		return addr[:idx], addr[idx+1:]
+	}
+
+	peers := []*config.BootstrapPeer{}
+	for _, addr := range input {
+		addrS, peeridS := split(addr)
+
+		// make sure addrS parses as a multiaddr.
+		if len(addrS) > 0 {
+			maddr, err := ma.NewMultiaddr(addrS)
+			if err != nil {
+				return nil, err
+			}
+
+			addrS = maddr.String()
+		}
+
+		// make sure idS parses as a peer.ID
+		peerid, err := mh.FromB58String(peeridS)
+		if err != nil {
+			return nil, err
+		}
+
+		// construct config entry
+		peers = append(peers, &config.BootstrapPeer{
+			Address: addrS,
+			PeerID:  peer.ID(peerid).Pretty(),
+		})
+	}
+	return peers, nil
+}
+
+const bootstrapSecurityWarning = `
+SECURITY WARNING:
+
+The bootstrap command manipulates the "bootstrap list", which contains
+the addresses of bootstrap nodes. These are the *trusted peers* from
+which to learn about other peers in the network. Only edit this list
+if you understand the risks of adding or removing nodes from this list.
+
+`
diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go
index 8031f2754..e3d362913 100644
--- a/cmd/ipfs/ipfs.go
+++ b/cmd/ipfs/ipfs.go
@@ -56,6 +56,7 @@ Use "ipfs help <command>" for more information about a command.
 		cmdIpfsServe,
 		cmdIpfsRun,
 		cmdIpfsName,
+		cmdIpfsBootstrap,
 	},
 	Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
 }
@@ -122,23 +123,32 @@ func localNode(confdir string, online bool) (*core.IpfsNode, error) {
 // Gets the config "-c" flag from the command, or returns
 // the default configuration root directory
 func getConfigDir(c *commander.Command) (string, error) {
-	root := c
-	for root.Parent != nil {
-		root = root.Parent
-	}
-	conf := root.Flag.Lookup("c").Value.Get()
-	if conf == nil {
-		return config.PathRoot()
-	}
-	confStr, ok := conf.(string)
-	if !ok {
-		return "", errors.New("failed to retrieve config flag value")
-	}
-	if len(confStr) == 0 {
-		return config.PathRoot()
+
+	// use the root cmd (that's where config is specified)
+	for ; c.Parent != nil; c = c.Parent {
 	}
 
-	return u.TildeExpansion(confStr)
+	// flag should be defined on root.
+	param := c.Flag.Lookup("c").Value.Get().(string)
+	if param != "" {
+		return u.TildeExpansion(param)
+	}
+
+	return config.PathRoot()
+}
+
+func getConfig(c *commander.Command) (*config.Config, error) {
+	confdir, err := getConfigDir(c)
+	if err != nil {
+		return nil, err
+	}
+
+	filename, err := config.Filename(confdir)
+	if err != nil {
+		return nil, err
+	}
+
+	return config.Load(filename)
 }
 
 // cmdContext is a wrapper structure that keeps a node, a daemonlistener, and
diff --git a/config/config.go b/config/config.go
index 5a25118bd..5acc23c4d 100644
--- a/config/config.go
+++ b/config/config.go
@@ -41,6 +41,10 @@ type BootstrapPeer struct {
 	PeerID  string // until multiaddr supports ipfs, use another field.
 }
 
+func (bp *BootstrapPeer) String() string {
+	return bp.Address + "/" + bp.PeerID
+}
+
 // Config is used to load IPFS config files.
 type Config struct {
 	Identity  Identity         // local node's peer identity