diff --git a/app/controllers/api/v0/users_controller.rb b/app/controllers/api/v0/users_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..882a46162021306ace2417e298723132d36ee2e8
--- /dev/null
+++ b/app/controllers/api/v0/users_controller.rb
@@ -0,0 +1,9 @@
+class Api::V0::UsersController < ApplicationController
+  def show
+    if user = User.find_by_username(params[:username])
+      render :json => Api::V0::Serializers::User.new(user)
+    else
+      head :not_found
+    end
+  end
+end
diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb
index 192b2891aab4151bfabbe038559a5719fb4ef99a..3b90b80ac7574b2925b59a15b1b7663e52c08d84 100644
--- a/app/helpers/comments_helper.rb
+++ b/app/helpers/comments_helper.rb
@@ -30,15 +30,4 @@ module CommentsHelper
       nil
     end
   end
-
-  def commenting_disabled?(post)
-    return true unless user_signed_in?
-    if defined?(@commenting_disabled)
-      @commenting_disabled
-    elsif defined?(@stream)
-      !@stream.can_comment?(post)
-    else 
-      false
-    end
-  end
 end
diff --git a/app/helpers/interim_stream_hackiness_helper.rb b/app/helpers/interim_stream_hackiness_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..22186c9b8816d958f3148d42ada706cbc478a0a0
--- /dev/null
+++ b/app/helpers/interim_stream_hackiness_helper.rb
@@ -0,0 +1,22 @@
+module InterimStreamHackinessHelper
+  def commenting_disabled?(post)
+    return true unless user_signed_in?
+    if defined?(@commenting_disabled)
+      @commenting_disabled
+    elsif defined?(@stream)
+      !@stream.can_comment?(post)
+    else 
+      false
+    end
+  end
+
+  def publisher_prefill_text
+    if params[:prefill].present?
+      params[:prefill]
+    elsif defined?(@stream)
+      @stream.publisher_prefill_text
+    else
+      nil
+    end
+  end
+end
diff --git a/app/models/api/v0/serializers/user.rb b/app/models/api/v0/serializers/user.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7bbaa259d127f42f0c51b1fd8c4171f30143c1b0
--- /dev/null
+++ b/app/models/api/v0/serializers/user.rb
@@ -0,0 +1,19 @@
+class Api::V0::Serializers::User
+  attr_accessor :user
+
+  def initialize(user)
+    @user = user
+    @person = user.person
+    @profile = @person.profile
+  end
+
+  def as_json(opts={})
+    {
+      "diaspora_id" => @person.diaspora_handle,
+      "first_name" => @profile.first_name,
+      "last_name" => @profile.last_name,
+      "image_url" => @profile.image_url,
+      "searchable" => @profile.searchable
+    }
+  end
+end
diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml
index f5b8742b563d1ad2783e1c78d571025e5ac7cdb9..19b10c305750948be027cc78791eab9722c019b9 100644
--- a/app/views/shared/_publisher.html.haml
+++ b/app/views/shared/_publisher.html.haml
@@ -18,7 +18,7 @@
           #publisher_textarea_wrapper
             = link_to( image_tag('deletelabel.png'), "#", :id => "hide_publisher", :title => t('.discard_post'))
             %ul#photodropzone
-            = status.text_area :fake_text, :rows => 2, :value => h(params[:prefill]), :tabindex => 1, :placeholder => t('.whats_on_your_mind')
+            = status.text_area :fake_text, :rows => 2, :value => h(publisher_prefill_text), :tabindex => 1, :placeholder => t('.whats_on_your_mind')
             = status.hidden_field :text, :value => '', :class => 'clear_on_submit'
 
             #file-upload{:title => t('.upload_photos')}
diff --git a/features/api.feature b/features/api.feature
new file mode 100644
index 0000000000000000000000000000000000000000..b051c72d372e8ad132e648619a6c14aa2754f51c
--- /dev/null
+++ b/features/api.feature
@@ -0,0 +1,11 @@
+Feature: API
+  In order to use a client application
+  as an epic developer
+  I need to get user's info
+
+  Scenario: Getting a users public profile 
+    Given a user named "Maxwell S" with email "maxwell@example.com"
+    And I send and accept JSON
+    When I send a GET request to "/api/v0/users/maxwell_s"
+    Then the response status should be "200"
+    And the JSON response should have "first_name" with the text "Maxwell"
diff --git a/lib/stream/base.rb b/lib/stream/base.rb
index 0f8f31085c91ca00739f8ca5d5b68c18c11bae2e..81f78e39e4f38f902c8c9645e72f7ae13cdb4916 100644
--- a/lib/stream/base.rb
+++ b/lib/stream/base.rb
@@ -39,6 +39,11 @@ class Stream::Base
     []
   end
 
+  # @return [String]
+  def publisher_prefill_text
+    ''
+  end
+
   # @return [ActiveRecord::Association<Person>] AR association of people within stream's given aspects
   def people
     people_ids = posts.map{|x| x.author_id}
diff --git a/lib/stream/tag.rb b/lib/stream/tag.rb
new file mode 100644
index 0000000000000000000000000000000000000000..312a3b58f9186a1818bb861b93ef6ab497b7faa3
--- /dev/null
+++ b/lib/stream/tag.rb
@@ -0,0 +1,53 @@
+class Stream::Tag < Stream::Base
+  attr_accessor :tag_name, :people_page
+
+  def initialize(user, tag_name, opts={}) 
+    super(user, opts)
+    self.tag_name = tag_name
+    @people_page = opts[:page] || 1
+  end
+
+  def tag
+    @tag ||= ActsAsTaggableOn::Tag.find_by_name(tag_name)
+  end
+
+  def tag_follow_count
+    @tag_follow_count ||= tag.try(:followed_count).to_i
+  end
+
+  def display_tag_name
+    @display_tag_name ||= "##{tag_name}"
+  end
+
+  def people
+    @people ||= Person.profile_tagged_with(tag_name).paginate(:page => people_page, :per_page => 15)
+  end
+
+  def people_count
+    @people_count ||= Person.profile_tagged_with(tag_name).count
+  end
+
+  def posts
+    @posts ||= construct_post_query
+  end
+
+  def publisher_prefill_text
+    display_tag_name + ' '
+  end
+
+  def tag_name=(tag_name)
+    @tag_name = tag_name.downcase.gsub('#', '')
+  end
+
+  private
+
+  def construct_post_query
+    posts = StatusMessage
+    if user.present? 
+      posts = posts.owned_or_visible_by_user(user)
+    else
+      posts = posts.all_public
+    end
+    posts.tagged_with(tag_name).for_a_stream(max_time, 'created_at')
+  end
+end
diff --git a/spec/controllers/api/v0/users_controller_spec.rb b/spec/controllers/api/v0/users_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1fc3e8e7b13b5aec8c5405d8c23c20d92d27e030
--- /dev/null
+++ b/spec/controllers/api/v0/users_controller_spec.rb
@@ -0,0 +1,23 @@
+#   Copyright (c) 2010-2011, Diaspora Inc.  This file is
+#   licensed under the Affero General Public License version 3 or later.  See
+#   the COPYRIGHT file.
+
+require 'spec_helper'
+
+describe Api::V0::UsersController do
+  describe '#show' do
+    it 'succeeds' do
+      get :show, :username => 'alice'
+      response.should be_success
+    end
+    it "404s if there's no such user" do
+      get :show, :username => "*****"
+      response.should be_not_found
+    end
+    it "returns the public profile data" do
+      get :show, :username => 'alice'
+      parsed_json = JSON.parse(response.body)
+      parsed_json.keys.should =~ %w( diaspora_id first_name last_name image_url searchable )
+    end
+  end
+end
diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb
index d79ccec7d20821e8bc1c29b6ab1d5cbe320969f0..69c128c1bf07b34a9352f75f9ebf516dea561740 100644
--- a/spec/controllers/tags_controller_spec.rb
+++ b/spec/controllers/tags_controller_spec.rb
@@ -41,89 +41,26 @@ describe TagsController do
         sign_in :user, alice
       end
 
-      it 'displays your own post' do
-        my_post = alice.post(:status_message, :text => "#what", :to => 'all')
-        get :show, :name => 'what'
-        assigns(:posts).should == [my_post]
-        response.status.should == 200
-      end
-
-      it "displays a friend's post" do
-        other_post = bob.post(:status_message, :text => "#hello", :to => 'all')
-        get :show, :name => 'hello'
-        assigns(:posts).should == [other_post]
-        response.status.should == 200
-      end
-
-      it 'displays a public post' do
-        other_post = eve.post(:status_message, :text => "#hello", :public => true, :to => 'all')
-        get :show, :name => 'hello'
-        assigns(:posts).should == [other_post]
-        response.status.should == 200
+      it 'assigns a Stream::Tag object with the current_user' do
+        get :show, :name => 'yes'
+        assigns[:stream].user.should == alice
       end
 
-      it 'displays a public post that was sent to no one' do
-        stranger = Factory(:user_with_aspect)
-        stranger_post = stranger.post(:status_message, :text => "#hello", :public => true, :to => 'all')
-        get :show, :name => 'hello'
-        assigns(:posts).should == [stranger_post]
-      end
-
-      it 'displays a post with a comment containing the tag search' do
-        pending "toooo slow"
-        bob.post(:status_message, :text => "other post y'all", :to => 'all')
-        other_post = bob.post(:status_message, :text => "sup y'all", :to => 'all')
-        Factory(:comment, :text => "#hello", :post => other_post)
-        get :show, :name => 'hello'
-        assigns(:posts).should == [other_post]
-        response.status.should == 200
-      end
-
-      it 'succeeds without posts' do
+      it 'succeeds' do
         get :show, :name => 'hellyes'
         response.status.should == 200
       end
     end
 
     context "not signed in" do
-      context "when there are people to display" do
-        before do
-          alice.profile.tag_string = "#whatevs"
-          alice.profile.build_tags
-          alice.profile.save!
-          get :show, :name => "whatevs"
-        end
-
-        it "succeeds" do
-          response.should be_success
-        end
-
-        it "assigns the right set of people" do
-          assigns(:people).should == [alice.person]
-        end
+      it 'assigns a Stream::Tag object with no user' do
+        get :show, :name => 'yes'
+        assigns[:stream].user.should be_nil
       end
 
-      context "when there are posts to display" do
-        before do
-          @post = alice.post(:status_message, :text => "#what", :public => true, :to => 'all')
-          alice.post(:status_message, :text => "#hello", :public => true, :to => 'all')
-        end
-
-        it "succeeds" do
-          get :show, :name => 'what'
-          response.should be_success
-        end
-
-        it "assigns the right set of posts" do
-          get :show, :name => 'what'
-          assigns[:posts].should == [@post]
-        end
-
-        it 'succeeds with comments' do
-          alice.comment('what WHAT!', :post => @post)
-          get :show, :name => 'what'
-          response.should be_success
-        end
+      it 'succeeds' do
+        get :show, :name => 'hellyes'
+        response.status.should == 200
       end
     end
   end
diff --git a/spec/lib/stream/tag_spec.rb b/spec/lib/stream/tag_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..edc9d4e7e63a1f7341839a05afaf7c26b211a8ce
--- /dev/null
+++ b/spec/lib/stream/tag_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+require File.join(Rails.root, 'spec', 'shared_behaviors', 'stream')
+
+describe Stream::Tag do
+  context 'with a user' do
+    before do
+      @stream = Stream::Tag.new(alice, "what")
+      bob.post(:status_message, :text => "#not_what", :to => 'all')
+    end
+
+   it 'displays your own post' do
+     my_post = alice.post(:status_message, :text => "#what", :to => 'all')
+     @stream.posts.should == [my_post]
+   end
+
+   it "displays a friend's post" do
+     other_post = bob.post(:status_message, :text => "#what", :to => 'all')
+     @stream.posts.should == [other_post]
+   end
+
+   it 'displays a public post' do
+     other_post = eve.post(:status_message, :text => "#what", :public => true, :to => 'all')
+     @stream.posts.should == [other_post]
+   end
+
+   it 'displays a public post that was sent to no one' do
+     stranger = Factory(:user_with_aspect)
+     stranger_post = stranger.post(:status_message, :text => "#what", :public => true, :to => 'all')
+     @stream.posts.should == [stranger_post]
+   end
+
+    it 'displays a post with a comment containing the tag search' do
+      pending "toooo slow"
+      other_post = bob.post(:status_message, :text => "sup y'all", :to => 'all')
+      Factory(:comment, :text => "#what", :post => other_post)
+      @stream.posts.should == [other_post]
+    end
+
+  end
+
+  context 'without a user' do
+    before do
+      @post = alice.post(:status_message, :text => "#what", :public => true, :to => 'all')
+      alice.post(:status_message, :text => "#tagwhat", :public => true, :to => 'all')
+      alice.post(:status_message, :text => "#what", :public => false, :to => 'all')
+    end
+
+    it "displays only public posts with the tag" do
+      stream = Stream::Tag.new(nil, "what")
+      stream.posts.should == [@post]
+    end
+  end
+
+  describe 'people' do
+    it "assigns the right set of people" do
+      stream = Stream::Tag.new(bob, "whatevs")
+      alice.profile.tag_string = "#whatevs"
+      alice.profile.build_tags
+      alice.profile.save!
+      stream.people.should == [alice.person]
+    end
+  end
+
+  describe 'shared behaviors' do
+    before do
+      @stream = Stream::Tag.new(Factory(:user), "test")
+    end
+    it_should_behave_like 'it is a stream'
+  end
+
+  describe '#tag_name=' do
+    it 'downcases the tag' do
+      stream = Stream::Tag.new(nil, "WHAT")
+      stream.tag_name.should == 'what'
+    end
+
+    it 'removes #es' do
+      stream = Stream::Tag.new(nil, "#WHAT")
+      stream.tag_name.should == 'what'
+    end
+  end
+end