diff --git a/app/controllers/public/projects_controller.rb b/app/controllers/public/projects_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e53c1a48485b4111d6ff061e58fb364aace0d879
--- /dev/null
+++ b/app/controllers/public/projects_controller.rb
@@ -0,0 +1,11 @@
+class Public::ProjectsController < ApplicationController
+  skip_before_filter :authenticate_user!,
+    :reject_blocked, :set_current_user_for_observers,
+    :add_abilities
+
+  layout 'public'
+
+  def index
+    @projects = Project.where(public: true)
+  end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 32b951349a949eb90561ea47cf31fad3fe576148..ece7da26e75cf75cfe0807a8e4c235fc3dd709ac 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -28,7 +28,7 @@ class Project < ActiveRecord::Base
   attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
                   :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
 
-  attr_accessible :namespace_id, :creator_id, as: :admin
+  attr_accessible :namespace_id, :creator_id, :public, as: :admin
 
   attr_accessor :error_code
 
diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml
index 36c0c655caea45ba5a49e6679ca3f2ddd0c8a884..0c7cf68ef439334b80834e96481eb842d978345a 100644
--- a/app/views/admin/projects/_form.html.haml
+++ b/app/views/admin/projects/_form.html.haml
@@ -43,6 +43,13 @@
       = f.label :wiki_enabled, "Wiki"
       .input= f.check_box :wiki_enabled
 
+  %fieldset.features
+    %legend Public mode:
+    .clearfix
+      = f.label :public do
+        %span Allow public http clone
+      .input= f.check_box :public
+
   %fieldset.features
     %legend Transfer:
     .control-group
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 5a745f58fe350da17b5509f6f56f31d2537bd99b..8e0d82328dfb6e25265658407212cdfaf735dd9e 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -77,6 +77,13 @@
           SSH:
       %td
         = link_to @project.ssh_url_to_repo
+  - if @project.public
+    %tr.bgred
+      %td
+        %b
+          Public Read-Only Code access:
+      %td
+        = check_box_tag 'public', nil, @project.public
 
 - if @repository
   %table.zebra-striped
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e82459c1e29e3c41f2d724c2228408400486053c
--- /dev/null
+++ b/app/views/layouts/public.html.haml
@@ -0,0 +1,17 @@
+!!! 5
+%html{ lang: "en"}
+  = render "layouts/head", title: "Error"
+  %body{class: "#{app_theme} application"}
+    %header.navbar.navbar-static-top.navbar-gitlab
+      .navbar-inner
+        .container
+          %div.app_logo
+            %span.separator
+            = link_to public_root_path, class: "home" do
+              %h1 GITLAB
+            %span.separator
+          %h1.project_name Public
+    .container
+      .content
+        .prepend-top-20
+          = yield
diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..ba4fd72628f8a4c48ebeaf28e65a07e6fc1db61e
--- /dev/null
+++ b/app/views/public/projects/index.html.haml
@@ -0,0 +1,14 @@
+%h3.page_title
+  Projects
+  %small Read-Only Access
+%hr
+
+%ul.well-list
+  - @projects.each do |project|
+    %li.clearfix
+      %h5
+        %i.icon-star.cgreen
+        = project.name_with_namespace
+        .right
+          %span.monospace.tiny
+            git clone #{project.http_url_to_repo}
diff --git a/config/routes.rb b/config/routes.rb
index ffcfd1ee6f55fce75cc4c6f32be9435ce308bda2..eeba31d5ae4636e36f820d5b746e9056ca076a78 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -36,6 +36,14 @@ Gitlab::Application.routes.draw do
   get 'help/ssh'          => 'help#ssh'
   get 'help/raketasks'    => 'help#raketasks'
 
+  #
+  # Public namespace
+  #
+  namespace :public do
+    resources :projects, only: [:index]
+    root to: "projects#index"
+  end
+
   #
   # Admin Area
   #
diff --git a/db/migrate/20130110172407_add_public_to_project.rb b/db/migrate/20130110172407_add_public_to_project.rb
new file mode 100644
index 0000000000000000000000000000000000000000..45edba48152aabbd93949b25da9cd50d82f55569
--- /dev/null
+++ b/db/migrate/20130110172407_add_public_to_project.rb
@@ -0,0 +1,5 @@
+class AddPublicToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :public, :boolean, default: false, null: false
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b1cf0ccbdb2de6dbddbdecb24538a8b24cda9acc..4b3a224360948f006c9ba83bda8cceda11171bb1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20130102143055) do
+ActiveRecord::Schema.define(:version => 20130110172407) do
 
   create_table "events", :force => true do |t|
     t.string   "target_type"
@@ -145,16 +145,17 @@ ActiveRecord::Schema.define(:version => 20130102143055) do
     t.string   "name"
     t.string   "path"
     t.text     "description"
-    t.datetime "created_at",                               :null => false
-    t.datetime "updated_at",                               :null => false
-    t.boolean  "private_flag",           :default => true, :null => false
+    t.datetime "created_at",                                :null => false
+    t.datetime "updated_at",                                :null => false
+    t.boolean  "private_flag",           :default => true,  :null => false
     t.integer  "creator_id"
     t.string   "default_branch"
-    t.boolean  "issues_enabled",         :default => true, :null => false
-    t.boolean  "wall_enabled",           :default => true, :null => false
-    t.boolean  "merge_requests_enabled", :default => true, :null => false
-    t.boolean  "wiki_enabled",           :default => true, :null => false
+    t.boolean  "issues_enabled",         :default => true,  :null => false
+    t.boolean  "wall_enabled",           :default => true,  :null => false
+    t.boolean  "merge_requests_enabled", :default => true,  :null => false
+    t.boolean  "wiki_enabled",           :default => true,  :null => false
     t.integer  "namespace_id"
+    t.boolean  "public",                 :default => false, :null => false
   end
 
   add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index cfad532a06ca478c6042d86700a1f9993cf5d3db..0defee2b11cb86e57fa9d88b603417aa6ba99c73 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -3,6 +3,16 @@ module Grack
     attr_accessor :user, :project
 
     def valid?
+      # Find project by PATH_INFO from env
+      if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
+        self.project = Project.find_with_namespace(m.last)
+        return false unless project
+      end
+
+      if @request.get? && project.public
+        return true
+      end
+
       # Authentication with username and password
       login, password = @auth.credentials
 
@@ -17,12 +27,6 @@ module Grack
       # Pass Gitolite update hook
       ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
 
-      # Find project by PATH_INFO from env
-      if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
-        self.project = Project.find_with_namespace(m.last)
-        return false unless project
-      end
-
       # Git upload and receive
       if @request.get?
         validate_get_request