diff --git a/gc/gc.go b/gc/gc.go
new file mode 100644
index 0000000000000000000000000000000000000000..3e2b850498b8014cb096b1cd282ef55e0a5e0154
--- /dev/null
+++ b/gc/gc.go
@@ -0,0 +1,99 @@
+package gc
+
+import (
+	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
+	key "github.com/ipfs/go-ipfs/blocks/key"
+	bserv "github.com/ipfs/go-ipfs/blockservice"
+	offline "github.com/ipfs/go-ipfs/exchange/offline"
+	dag "github.com/ipfs/go-ipfs/merkledag"
+	pin "github.com/ipfs/go-ipfs/pin"
+
+	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
+	logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
+)
+
+var log = logging.Logger("gc")
+
+// GC performs a mark and sweep garbage collection of the blocks in the blockstore
+// first, it creates a 'marked' set and adds to it the following:
+// - all recursively pinned blocks, plus all of their descendants (recursively)
+// - all directly pinned blocks
+// - all blocks utilized internally by the pinner
+//
+// The routine then iterates over every block in the blockstore and
+// deletes any block that is not found in the marked set.
+func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key.Key, error) {
+	unlock := bs.GCLock()
+	defer unlock()
+
+	bsrv := bserv.New(bs, offline.Exchange(bs))
+	ds := dag.NewDAGService(bsrv)
+
+	// KeySet currently implemented in memory, in the future, may be bloom filter or
+	// disk backed to conserve memory.
+	gcs := key.NewKeySet()
+	for _, k := range pn.RecursiveKeys() {
+		gcs.Add(k)
+		nd, err := ds.Get(ctx, k)
+		if err != nil {
+			return nil, err
+		}
+
+		// EnumerateChildren recursively walks the dag and adds the keys to the given set
+		err = dag.EnumerateChildren(ctx, ds, nd, gcs)
+		if err != nil {
+			return nil, err
+		}
+	}
+	for _, k := range pn.DirectKeys() {
+		gcs.Add(k)
+	}
+	for _, k := range pn.InternalPins() {
+		gcs.Add(k)
+
+		nd, err := ds.Get(ctx, k)
+		if err != nil {
+			return nil, err
+		}
+
+		// EnumerateChildren recursively walks the dag and adds the keys to the given set
+		err = dag.EnumerateChildren(ctx, ds, nd, gcs)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	keychan, err := bs.AllKeysChan(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	output := make(chan key.Key)
+	go func() {
+		defer close(output)
+		for {
+			select {
+			case k, ok := <-keychan:
+				if !ok {
+					return
+				}
+				if !gcs.Has(k) {
+					err := bs.DeleteBlock(k)
+					if err != nil {
+						log.Debugf("Error removing key from blockstore: %s", err)
+						return
+					}
+					select {
+					case output <- k:
+					case <-ctx.Done():
+						return
+					}
+				}
+			case <-ctx.Done():
+				return
+			}
+		}
+	}()
+
+	return output, nil
+}
diff --git a/pin.go b/pin.go
index 4d17138ab8a1b93513993fd4f2e069b055f7178c..4221fae591707fba1022e5ae27f4bf4624e70f5f 100644
--- a/pin.go
+++ b/pin.go
@@ -24,7 +24,6 @@ var emptyKey = key.B58KeyDecode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
 const (
 	linkDirect    = "direct"
 	linkRecursive = "recursive"
-	linkIndirect  = "indirect"
 )
 
 type PinMode int
@@ -32,7 +31,6 @@ type PinMode int
 const (
 	Recursive PinMode = iota
 	Direct
-	Indirect
 	NotPinned
 )
 
@@ -52,8 +50,8 @@ type Pinner interface {
 
 	Flush() error
 	DirectKeys() []key.Key
-	IndirectKeys() map[key.Key]uint64
 	RecursiveKeys() []key.Key
+	InternalPins() []key.Key
 }
 
 // pinner implements the Pinner interface
@@ -61,7 +59,7 @@ type pinner struct {
 	lock       sync.RWMutex
 	recursePin set.BlockSet
 	directPin  set.BlockSet
-	indirPin   *indirectPin
+
 	// Track the keys used for storing the pinning state, so gc does
 	// not delete them.
 	internalPin map[key.Key]struct{}
@@ -80,7 +78,6 @@ func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner {
 	return &pinner{
 		recursePin: rcset,
 		directPin:  dirset,
-		indirPin:   newIndirectPin(),
 		dserv:      serv,
 		dstore:     dstore,
 	}
@@ -104,7 +101,8 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error {
 			p.directPin.RemoveBlock(k)
 		}
 
-		err := p.pinLinks(ctx, node)
+		// fetch entire graph
+		err := mdag.FetchGraph(ctx, node, p.dserv)
 		if err != nil {
 			return err
 		}
@@ -131,72 +129,18 @@ func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error {
 	if p.recursePin.HasKey(k) {
 		if recursive {
 			p.recursePin.RemoveBlock(k)
-			node, err := p.dserv.Get(ctx, k)
-			if err != nil {
-				return err
-			}
-
-			return p.unpinLinks(ctx, node)
+			return nil
 		} else {
 			return fmt.Errorf("%s is pinned recursively", k)
 		}
 	} else if p.directPin.HasKey(k) {
 		p.directPin.RemoveBlock(k)
 		return nil
-	} else if p.indirPin.HasKey(k) {
-		return fmt.Errorf("%s is pinned indirectly. indirect pins cannot be removed directly", k)
 	} else {
 		return fmt.Errorf("%s is not pinned", k)
 	}
 }
 
-func (p *pinner) unpinLinks(ctx context.Context, node *mdag.Node) error {
-	for _, l := range node.Links {
-		node, err := l.GetNode(ctx, p.dserv)
-		if err != nil {
-			return err
-		}
-
-		k, err := node.Key()
-		if err != nil {
-			return err
-		}
-
-		p.indirPin.Decrement(k)
-
-		err = p.unpinLinks(ctx, node)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (p *pinner) pinIndirectRecurse(ctx context.Context, node *mdag.Node) error {
-	k, err := node.Key()
-	if err != nil {
-		return err
-	}
-
-	p.indirPin.Increment(k)
-	return p.pinLinks(ctx, node)
-}
-
-func (p *pinner) pinLinks(ctx context.Context, node *mdag.Node) error {
-	for _, ng := range p.dserv.GetDAG(ctx, node) {
-		subnode, err := ng.Get(ctx)
-		if err != nil {
-			// TODO: Maybe just log and continue?
-			return err
-		}
-		err = p.pinIndirectRecurse(ctx, subnode)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
 func (p *pinner) isInternalPin(key key.Key) bool {
 	_, ok := p.internalPin[key]
 	return ok
@@ -208,7 +152,6 @@ func (p *pinner) IsPinned(key key.Key) bool {
 	defer p.lock.RUnlock()
 	return p.recursePin.HasKey(key) ||
 		p.directPin.HasKey(key) ||
-		p.indirPin.HasKey(key) ||
 		p.isInternalPin(key)
 }
 
@@ -218,8 +161,6 @@ func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) {
 	switch mode {
 	case Direct:
 		p.directPin.RemoveBlock(key)
-	case Indirect:
-		p.indirPin.Decrement(key)
 	case Recursive:
 		p.recursePin.RemoveBlock(key)
 	default:
@@ -274,14 +215,6 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error)
 		p.directPin = set.SimpleSetFromKeys(directKeys)
 	}
 
-	{ // load indirect set
-		refcnt, err := loadMultiset(ctx, dserv, root, linkIndirect, recordInternal)
-		if err != nil {
-			return nil, fmt.Errorf("cannot load indirect pins: %v", err)
-		}
-		p.indirPin = &indirectPin{refCounts: refcnt}
-	}
-
 	p.internalPin = internalPin
 
 	// assign services
@@ -296,11 +229,6 @@ func (p *pinner) DirectKeys() []key.Key {
 	return p.directPin.GetKeys()
 }
 
-// IndirectKeys returns a slice containing the indirectly pinned keys
-func (p *pinner) IndirectKeys() map[key.Key]uint64 {
-	return p.indirPin.GetRefs()
-}
-
 // RecursiveKeys returns a slice containing the recursively pinned keys
 func (p *pinner) RecursiveKeys() []key.Key {
 	return p.recursePin.GetKeys()
@@ -339,20 +267,17 @@ func (p *pinner) Flush() error {
 		}
 	}
 
-	{
-		n, err := storeMultiset(ctx, p.dserv, p.indirPin.GetRefs(), recordInternal)
-		if err != nil {
-			return err
-		}
-		if err := root.AddNodeLink(linkIndirect, n); err != nil {
-			return err
-		}
+	// add the empty node, its referenced by the pin sets but never created
+	_, err := p.dserv.Add(new(mdag.Node))
+	if err != nil {
+		return err
 	}
 
 	k, err := p.dserv.Add(root)
 	if err != nil {
 		return err
 	}
+
 	internalPin[k] = struct{}{}
 	if err := p.dstore.Put(pinDatastoreKey, []byte(k)); err != nil {
 		return fmt.Errorf("cannot store pin state: %v", err)
@@ -361,6 +286,16 @@ func (p *pinner) Flush() error {
 	return nil
 }
 
+func (p *pinner) InternalPins() []key.Key {
+	p.lock.Lock()
+	defer p.lock.Unlock()
+	var out []key.Key
+	for k, _ := range p.internalPin {
+		out = append(out, k)
+	}
+	return out
+}
+
 // PinWithMode allows the user to have fine grained control over pin
 // counts
 func (p *pinner) PinWithMode(k key.Key, mode PinMode) {
@@ -371,7 +306,5 @@ func (p *pinner) PinWithMode(k key.Key, mode PinMode) {
 		p.recursePin.AddBlock(k)
 	case Direct:
 		p.directPin.AddBlock(k)
-	case Indirect:
-		p.indirPin.Increment(k)
 	}
 }
diff --git a/pin_test.go b/pin_test.go
index 69f84f5319ae363b249a54bf8c8275f1210c1628..15fd0a2f928d1732a5bbff2b2241d29b3d93580b 100644
--- a/pin_test.go
+++ b/pin_test.go
@@ -53,7 +53,7 @@ func TestPinnerBasic(t *testing.T) {
 	}
 
 	// create new node c, to be indirectly pinned through b
-	c, ck := randNode()
+	c, _ := randNode()
 	_, err = dserv.Add(c)
 	if err != nil {
 		t.Fatal(err)
@@ -82,10 +82,6 @@ func TestPinnerBasic(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if !p.IsPinned(ck) {
-		t.Fatal("Child of recursively pinned node not found")
-	}
-
 	bk, _ := b.Key()
 	if !p.IsPinned(bk) {
 		t.Fatal("Recursively pinned node not found..")
@@ -95,7 +91,7 @@ func TestPinnerBasic(t *testing.T) {
 	d.AddNodeLink("a", a)
 	d.AddNodeLink("c", c)
 
-	e, ek := randNode()
+	e, _ := randNode()
 	d.AddNodeLink("e", e)
 
 	// Must be in dagserv for unpin to work
@@ -110,10 +106,6 @@ func TestPinnerBasic(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if !p.IsPinned(ek) {
-		t.Fatal(err)
-	}
-
 	dk, _ := d.Key()
 	if !p.IsPinned(dk) {
 		t.Fatal("pinned node not found.")
@@ -125,11 +117,6 @@ func TestPinnerBasic(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	// c should still be pinned under b
-	if !p.IsPinned(ck) {
-		t.Fatal("Recursive / indirect unpin fail.")
-	}
-
 	err = p.Flush()
 	if err != nil {
 		t.Fatal(err)
@@ -145,11 +132,6 @@ func TestPinnerBasic(t *testing.T) {
 		t.Fatal("Could not find pinned node!")
 	}
 
-	// Test indirectly pinned
-	if !np.IsPinned(ck) {
-		t.Fatal("could not find indirectly pinned node")
-	}
-
 	// Test recursively pinned
 	if !np.IsPinned(bk) {
 		t.Fatal("could not find recursively pinned node")
@@ -201,7 +183,7 @@ func TestFlush(t *testing.T) {
 	p := NewPinner(dstore, dserv)
 	_, k := randNode()
 
-	p.PinWithMode(k, Indirect)
+	p.PinWithMode(k, Recursive)
 	if err := p.Flush(); err != nil {
 		t.Fatal(err)
 	}