diff --git a/public/javascripts/app/collections/stream.js b/public/javascripts/app/collections/posts.js
similarity index 53%
rename from public/javascripts/app/collections/stream.js
rename to public/javascripts/app/collections/posts.js
index b16e137e125ffef85587065098e653f1ff9ad02a..496d5a7545cdf5371e43c821d382f34f09073322 100644
--- a/public/javascripts/app/collections/stream.js
+++ b/public/javascripts/app/collections/posts.js
@@ -1,13 +1,5 @@
-app.collections.Stream = Backbone.Collection.extend({
-  url: function() {
-    var path = document.location.pathname;
-
-    if(this.models.length) {
-      path += "?max_time=" + _.last(this.models).createdAt();
-    }
-
-    return path;
-  },
+app.collections.Posts = Backbone.Collection.extend({
+  url : "/posts",
 
   model: function(attrs, options) {
     var modelClass = app.models[attrs.post_type] || app.models.Post
diff --git a/public/javascripts/app/models/stream.js b/public/javascripts/app/models/stream.js
new file mode 100644
index 0000000000000000000000000000000000000000..8611af3e28d538a4b4e2bc855ba39c45ef34e6a6
--- /dev/null
+++ b/public/javascripts/app/models/stream.js
@@ -0,0 +1,36 @@
+app.models.Stream = Backbone.Collection.extend({
+  initialize : function(){
+    this.posts = new app.collections.Posts();
+  },
+
+  url : function(){
+    return _.any(this.posts.models) ? this.timeFilteredPath() : this.basePath()
+  },
+
+  fetch: function() {
+    var self = this
+
+    this.posts
+      .fetch({
+        add : true,
+        url : self.url()
+      })
+      .done(
+        function(){ 
+          self.trigger("fetched", self);
+        }
+      )
+  },
+
+  basePath : function(){
+    return document.location.pathname;
+  },
+
+  timeFilteredPath : function(){
+   return this.basePath() + "?max_time=" + _.last(this.posts.models).createdAt();
+  },
+
+  add : function(models){
+    this.posts.add(models)
+  }
+})
diff --git a/public/javascripts/app/router.js b/public/javascripts/app/router.js
index ab7234f59fd53240e31cc6f558c2345ab4a2258f..986e2789b3013805c2094b01db36bbc6bfa2f441 100644
--- a/public/javascripts/app/router.js
+++ b/public/javascripts/app/router.js
@@ -13,10 +13,11 @@ app.Router = Backbone.Router.extend({
   },
 
   stream : function() {
-    app.stream = new app.views.Stream().render();
-    $("#main_stream").html(app.stream.el);
+    app.stream = new app.models.Stream()
+    app.page = new app.views.Stream().render();
+    $("#main_stream").html(app.page.el);
 
-    var streamFacesView = new app.views.StreamFaces({collection : app.stream.collection}).render();
+    var streamFacesView = new app.views.StreamFaces({collection : app.stream.posts}).render();
     $('#selected_aspect_contacts .content').html(streamFacesView.el);
   }
 });
diff --git a/public/javascripts/app/views/feedback_view.js b/public/javascripts/app/views/feedback_view.js
index 84c1ced12074b507a6dcb32ea81729a89df39b3e..bfa7110adfdcd6b8049cf3ce7673f9ccf23e5890 100644
--- a/public/javascripts/app/views/feedback_view.js
+++ b/public/javascripts/app/views/feedback_view.js
@@ -20,7 +20,7 @@ app.views.Feedback = app.views.StreamObject.extend({
     reshare.save({}, {
       url: this.model.createReshareUrl,
       success : function(){
-        app.stream.collection.add(reshare);
+        app.stream.add(reshare);
       }
     });
   }
diff --git a/public/javascripts/app/views/post_view.js b/public/javascripts/app/views/post_view.js
index adb46d6b8f1130c44a76b45054f0ebc69a6cfe9b..94011ecfc25bd355f665f79716e7fbd6eb03bb4d 100644
--- a/public/javascripts/app/views/post_view.js
+++ b/public/javascripts/app/views/post_view.js
@@ -63,9 +63,9 @@ app.views.Post = app.views.StreamObject.extend({
       success : function(){
         if(!app.stream) { return }
 
-        _.each(app.stream.collection.models, function(model){
+        _.each(app.stream.posts.models, function(model){
           if(model.get("author").id == personId) {
-            app.stream.collection.remove(model);
+            app.stream.posts.remove(model);
           }
         })
       }
diff --git a/public/javascripts/app/views/publisher_view.js b/public/javascripts/app/views/publisher_view.js
index 989d2ce9af2b36035ebd34c225557520abee8a0c..68e358ca048a6175828764e8fb18d74fd9316b87 100644
--- a/public/javascripts/app/views/publisher_view.js
+++ b/public/javascripts/app/views/publisher_view.js
@@ -12,7 +12,7 @@ app.views.Publisher = Backbone.View.extend({
   },
 
   initialize : function(){
-    this.collection = this.collection || new app.collections.Stream;
+    this.collection = this.collection || new app.collections.Posts;
     return this;
   },
 
@@ -33,7 +33,7 @@ app.views.Publisher = Backbone.View.extend({
     }, {
       url : "/status_messages",
       success : function() {
-        app.stream.collection.add(statusMessage.toJSON());
+        app.stream.posts.add(statusMessage.toJSON());
       }
     });
 
diff --git a/public/javascripts/app/views/stream_faces_view.js b/public/javascripts/app/views/stream_faces_view.js
index d0979cd8af9742ddb025e0d93ffe086203b00b55..ae5232fc7cef3e9364aa9d2cf8d0eb2f8469f7b7 100644
--- a/public/javascripts/app/views/stream_faces_view.js
+++ b/public/javascripts/app/views/stream_faces_view.js
@@ -8,7 +8,7 @@ app.views.StreamFaces = app.views.Base.extend({
 
   initialize : function(){
     this.updatePeople()
-    this.collection.bind("add", this.updatePeople, this)
+    app.stream.posts.bind("add", this.updatePeople, this)
   },
 
   presenter : function() {
diff --git a/public/javascripts/app/views/stream_view.js b/public/javascripts/app/views/stream_view.js
index d8c654697337ffa7892dd39c65fe02524a3b1612..8f396f39215464cc32c14248d3eeeb118004a6c1 100644
--- a/public/javascripts/app/views/stream_view.js
+++ b/public/javascripts/app/views/stream_view.js
@@ -3,46 +3,17 @@ app.views.Stream = Backbone.View.extend({
     "click #paginate": "render"
   },
 
-  initialize: function() {
-    this.collection = this.collection || new app.collections.Stream;
-    this.collection.bind("add", this.addPost, this);
-
+  initialize: function(options) {
+    this.stream = app.stream || new app.models.Stream()
+    this.collection = this.stream.posts
     this.publisher = new app.views.Publisher({collection : this.collection});
 
-    // inf scroll
-    // we're using this._loading to keep track of backbone's collection
-    //   fetching state... is there a better way to do this?
-    var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200);
-    $(window).scroll(throttledScroll);
-
-    // lightbox delegation
-    this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
-    $(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked);
-
-    return this;
-  },
-
-  infScroll : function() {
-    if(this.allContentLoaded || this.isLoading()) { return }
-
-    var $window = $(window);
-    var distFromTop = $window.height() + $window.scrollTop();
-    var distFromBottom = $(document).height() - distFromTop;
-    var bufferPx = 500;
-
-    if(distFromBottom < bufferPx) {
-      this.render();
-    }
-
-    return this;
-  },
-
-  isLoading : function(){
-    return this._loading && !this._loading.isResolved();
+    this.stream.bind("fetched", this.collectionFetched, this)
+    this.collection.bind("add", this.addPost, this);
+    this.setupInfiniteScroll()
+    this.setupLightbox()
   },
 
-  allContentLoaded : false,
-
   addPost : function(post) {
     var postView = new app.views.Post({ model: post });
 
@@ -55,9 +26,15 @@ app.views.Stream = Backbone.View.extend({
     return this;
   },
 
-  collectionFetched: function(collection, response) {
-    this.$("#paginate").remove();
+  isLoading : function(){
+    return this._loading && !this._loading.isResolved();
+  },
+
+  allContentLoaded : false,
+
 
+  collectionFetched: function(collection, response) {
+    this.removeLoader()
     if(!collection.parse(response).length || collection.parse(response).length == 0) {
       this.allContentLoaded = true;
       $(window).unbind('scroll')
@@ -65,7 +42,7 @@ app.views.Stream = Backbone.View.extend({
     }
 
     $(this.el).append($("<a>", {
-      href: this.collection.url(),
+      href: this.stream.url(),
       id: "paginate"
     }).text('Load more posts'));
   },
@@ -73,13 +50,8 @@ app.views.Stream = Backbone.View.extend({
   render : function(evt) {
     if(evt) { evt.preventDefault(); }
 
-    var self = this;
-    self.addLoader();
-
-    this._loading = self.collection.fetch({
-      add: true,
-      success: $.proxy(this.collectionFetched, self)
-    });
+    this.addLoader();
+    this._loading = this.stream.fetch();
 
     return this;
   },
@@ -95,5 +67,34 @@ app.views.Stream = Backbone.View.extend({
       src : "/images/static-loader.png",
       "class" : "loader"
     }));
-  }
+  },
+
+  removeLoader : function(){
+    this.$("#paginate").remove();
+  },
+
+  setupLightbox : function(){
+    this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
+    $(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked);
+  },
+
+  setupInfiniteScroll : function() {
+    var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200);
+    $(window).scroll(throttledScroll);
+  },
+
+  infScroll : function() {
+    if(this.allContentLoaded || this.isLoading()) { return }
+
+    var $window = $(window);
+    var distFromTop = $window.height() + $window.scrollTop();
+    var distFromBottom = $(document).height() - distFromTop;
+    var bufferPx = 500;
+
+    if(distFromBottom < bufferPx) {
+      this.render();
+    }
+
+    return this;
+  },
 });
diff --git a/spec/javascripts/app/collections/stream_spec.js b/spec/javascripts/app/collections/stream_spec.js
deleted file mode 100644
index 145c09c57c346f3ca848890388e9b4b65ffd6bf7..0000000000000000000000000000000000000000
--- a/spec/javascripts/app/collections/stream_spec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-describe("app.collections.Stream", function() {
-  describe("url", function() {
-    var stream = new app.collections.Stream(),
-        expectedPath = document.location.pathname;
-
-    it("returns the correct path", function() {
-      expect(stream.url()).toEqual(expectedPath);
-    });
-
-    it("returns the json path with max_time if the collection has models", function() {
-      var post = new app.models.Post();
-      spyOn(post, "createdAt").andReturn(1234);
-
-      stream.add(post);
-
-      expect(stream.url()).toEqual(expectedPath + "?max_time=1234");
-    });
-  });
-});
diff --git a/spec/javascripts/app/models/stream_spec.js b/spec/javascripts/app/models/stream_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..064b8d8ddbda502e783e477aa86356931c2b354f
--- /dev/null
+++ b/spec/javascripts/app/models/stream_spec.js
@@ -0,0 +1,39 @@
+describe("app.models.Stream", function() {
+  beforeEach(function(){
+    this.stream = new app.models.Stream(),
+    this.expectedPath = document.location.pathname;
+  })
+
+  describe(".fetch", function() {
+    var postFetch
+    beforeEach(function(){
+      postFetch = new $.Deferred()
+
+      spyOn(this.stream.posts, "fetch").andCallFake(function(){ 
+        return postFetch
+      })
+    })
+
+    it("it fetches posts from the window's url, and ads them to tthe collection", function() {
+      this.stream.fetch()
+      expect(this.stream.posts.fetch).toHaveBeenCalledWith({ add : true, url : this.expectedPath});
+    });
+
+    it("returns the json path with max_time if the collection has models", function() {
+      var post = new app.models.Post();
+      spyOn(post, "createdAt").andReturn(1234);
+      this.stream.add(post);
+
+      this.stream.fetch()
+      expect(this.stream.posts.fetch).toHaveBeenCalledWith({ add : true, url : this.expectedPath + "?max_time=1234"});
+    });
+
+    it("triggers fetched on the stream when it is fetched", function(){
+      var fetchedSpy = jasmine.createSpy()
+      this.stream.bind('fetched', fetchedSpy)
+      this.stream.fetch()
+      postFetch.resolve()
+      expect(fetchedSpy).toHaveBeenCalled()
+    })
+  });
+});
diff --git a/spec/javascripts/app/views/post_view_spec.js b/spec/javascripts/app/views/post_view_spec.js
index 790913afd9d1fd35422920651c8bf614d62465a3..aedea512806a0a6a5c54dd40cc6e4a844c8d535b 100644
--- a/spec/javascripts/app/views/post_view_spec.js
+++ b/spec/javascripts/app/views/post_view_spec.js
@@ -13,7 +13,7 @@ describe("app.views.Post", function(){
 
       var posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"];
 
-      this.collection = new app.collections.Stream(posts);
+      this.collection = new app.collections.Posts(posts);
       this.statusMessage = this.collection.models[0];
       this.reshare = this.collection.models[1];
     })
diff --git a/spec/javascripts/app/views/stream_faces_view_spec.js b/spec/javascripts/app/views/stream_faces_view_spec.js
index d94a8bc9935292d38e3bb241f8f6707226e30a13..8cdf4eb6cb70d6b0371838e87adc9d194e6daa43 100644
--- a/spec/javascripts/app/views/stream_faces_view_spec.js
+++ b/spec/javascripts/app/views/stream_faces_view_spec.js
@@ -9,8 +9,11 @@ describe("app.views.StreamFaces", function(){
     this.post6 = factory.post({author : rebeccaBlack})
     this.post7 = factory.post({author : rebeccaBlack})
 
-    this.stream = new app.collections.Stream([this.post1, this.post2, this.post3, this.post4, this.post5, this.post6, this.post7]);
-    this.view = new app.views.StreamFaces({collection : this.stream})
+    app.stream = new app.models.Stream()
+    app.stream.add([this.post1, this.post2, this.post3, this.post4, this.post5, this.post6, this.post7]);
+    this.posts = app.stream.posts
+
+    this.view = new app.views.StreamFaces({collection : this.posts})
   })
 
   it("should take them unique", function(){
@@ -19,7 +22,7 @@ describe("app.views.StreamFaces", function(){
   })
 
   it("findsPeople when the collection changes", function(){
-    this.stream.add(factory.post({author : factory.author({name : "Harriet Tubman"})}))
+    this.posts.add(factory.post({author : factory.author({name : "Harriet Tubman"})}))
     expect(this.view.people.length).toBe(6)
   })
 
@@ -39,8 +42,8 @@ describe("app.views.StreamFaces", function(){
 
     it("rerenders when people are added, but caps to 15 people", function(){
       var posts = _.map(_.range(20), function(){ return factory.post()})
-      this.stream.reset(posts) //add 20 posts silently to the collection
-      this.stream.add(factory.post) //trigger an update
+      this.posts.reset(posts) //add 20 posts silently to the collection
+      this.posts.add(factory.post) //trigger an update
       expect(this.view.$("img").length).toBe(15)
     })
   })
diff --git a/spec/javascripts/app/views/stream_view_spec.js b/spec/javascripts/app/views/stream_view_spec.js
index 0ef2cdd8b43bf25a59be51486ed8c47d30dcea22..38160b033cb3a921d4884fc8a625cf20130e0dc5 100644
--- a/spec/javascripts/app/views/stream_view_spec.js
+++ b/spec/javascripts/app/views/stream_view_spec.js
@@ -4,14 +4,17 @@ describe("app.views.Stream", function(){
 
     this.posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"];
 
-    this.collection = new app.collections.Stream(this.posts);
+    app.stream = new app.models.Stream()
+    app.stream.add(this.posts);
+
+    this.collection = app.stream.posts
     this.view = new app.views.Stream({collection : this.collection});
 
+    app.stream.bind("fetched", this.collectionFetched, this) //untested
+
     // do this manually because we've moved loadMore into render??
     this.view.render();
-    _.each(this.view.collection.models, function(post){
-      this.view.addPost(post);
-    }, this);
+    _.each(this.view.collection.models, function(post){ this.view.addPost(post); }, this);
   })
 
   describe("initialize", function(){
@@ -42,7 +45,7 @@ describe("app.views.Stream", function(){
     // NOTE: inf scroll happens at 500px
 
     beforeEach(function(){
-      spyOn(this.view.collection, "fetch")
+      spyOn(this.view.collection, "fetch").andReturn($.Deferred())
     })
 
     context("when the user is at the bottom of the page", function(){