diff --git a/app/models/ci/catalog/verified_namespace.rb b/app/models/ci/catalog/verified_namespace.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b5bb28c04cf68c62cc414d0dc86faec247389449
--- /dev/null
+++ b/app/models/ci/catalog/verified_namespace.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Ci
+  module Catalog
+    class VerifiedNamespace < ::ApplicationRecord
+      self.table_name = 'catalog_verified_namespaces'
+
+      belongs_to :namespace
+
+      enum verification_level: { gitlab_maintained: 100, partner: 50, verified_creator: 10, unverified: 0 }
+
+      validates :namespace_id, presence: true, uniqueness: true
+    end
+  end
+end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 9020f90fd3c48e2b44b6e3174958b38b8cd52452..bb1f77680ff4c9e33198b7f70d9c04f37b7ad902 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -53,6 +53,8 @@ class Namespace < ApplicationRecord
   has_one :namespace_details, inverse_of: :namespace, class_name: 'Namespace::Detail', autosave: true
   has_one :namespace_statistics
   has_one :namespace_route, foreign_key: :namespace_id, autosave: false, inverse_of: :namespace, class_name: 'Route'
+  has_one :catalog_verified_namespace, class_name: 'Ci::Catalog::VerifiedNamespace', inverse_of: :namespace
+
   has_many :namespace_members, foreign_key: :member_namespace_id, inverse_of: :member_namespace, class_name: 'Member'
 
   has_one :namespace_ldap_settings, inverse_of: :namespace, class_name: 'Namespaces::LdapSetting', autosave: true
diff --git a/db/docs/catalog_verified_namespaces.yml b/db/docs/catalog_verified_namespaces.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f29985385f0c952ca0ee0843e459283df72b2204
--- /dev/null
+++ b/db/docs/catalog_verified_namespaces.yml
@@ -0,0 +1,10 @@
+---
+table_name: catalog_verified_namespaces
+feature_categories:
+- pipeline_composition
+description: Verified namespaces in the CI catalog.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143003
+milestone: '16.9'
+gitlab_schema: gitlab_main_cell
+sharding_key:
+  namespace_id: namespaces
diff --git a/db/migrate/20240131052824_create_catalog_verified_namespaces.rb b/db/migrate/20240131052824_create_catalog_verified_namespaces.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f4ea2680be1a2e015dab37a3d3dbcda10f90729f
--- /dev/null
+++ b/db/migrate/20240131052824_create_catalog_verified_namespaces.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class CreateCatalogVerifiedNamespaces < Gitlab::Database::Migration[2.2]
+  milestone '16.9'
+
+  def change
+    create_table :catalog_verified_namespaces do |t|
+      t.references :namespace, index: { unique: true }, null: false, foreign_key: { on_delete: :cascade }
+      t.datetime_with_timezone :created_at, null: false
+      t.integer :verification_level, null: false, limit: 2, default: 0
+    end
+  end
+end
diff --git a/db/schema_migrations/20240131052824 b/db/schema_migrations/20240131052824
new file mode 100644
index 0000000000000000000000000000000000000000..06a9ed151b673cebb6b091bd36335f4801fefcb4
--- /dev/null
+++ b/db/schema_migrations/20240131052824
@@ -0,0 +1 @@
+8cd70cd24b3e578afdac5c3962335777fb6daa66fa84903b08e5c2e6edbb2b76
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index f46c5208840a65929a72bbe77fde9a4651c763a4..a1abb5092b7cff3580381d94b2349463ba2169a2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -14069,6 +14069,22 @@ CREATE SEQUENCE catalog_resources_id_seq
 
 ALTER SEQUENCE catalog_resources_id_seq OWNED BY catalog_resources.id;
 
+CREATE TABLE catalog_verified_namespaces (
+    id bigint NOT NULL,
+    namespace_id bigint NOT NULL,
+    created_at timestamp with time zone NOT NULL,
+    verification_level smallint DEFAULT 0 NOT NULL
+);
+
+CREATE SEQUENCE catalog_verified_namespaces_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+ALTER SEQUENCE catalog_verified_namespaces_id_seq OWNED BY catalog_verified_namespaces.id;
+
 CREATE TABLE chat_names (
     id integer NOT NULL,
     user_id integer NOT NULL,
@@ -27061,6 +27077,8 @@ ALTER TABLE ONLY catalog_resource_versions ALTER COLUMN id SET DEFAULT nextval('
 
 ALTER TABLE ONLY catalog_resources ALTER COLUMN id SET DEFAULT nextval('catalog_resources_id_seq'::regclass);
 
+ALTER TABLE ONLY catalog_verified_namespaces ALTER COLUMN id SET DEFAULT nextval('catalog_verified_namespaces_id_seq'::regclass);
+
 ALTER TABLE ONLY chat_names ALTER COLUMN id SET DEFAULT nextval('chat_names_id_seq'::regclass);
 
 ALTER TABLE ONLY chat_teams ALTER COLUMN id SET DEFAULT nextval('chat_teams_id_seq'::regclass);
@@ -29078,6 +29096,9 @@ ALTER TABLE ONLY catalog_resource_versions
 ALTER TABLE ONLY catalog_resources
     ADD CONSTRAINT catalog_resources_pkey PRIMARY KEY (id);
 
+ALTER TABLE ONLY catalog_verified_namespaces
+    ADD CONSTRAINT catalog_verified_namespaces_pkey PRIMARY KEY (id);
+
 ALTER TABLE ONLY chat_names
     ADD CONSTRAINT chat_names_pkey PRIMARY KEY (id);
 
@@ -32896,6 +32917,8 @@ CREATE INDEX index_catalog_resources_on_search_vector ON catalog_resources USING
 
 CREATE INDEX index_catalog_resources_on_state ON catalog_resources USING btree (state);
 
+CREATE UNIQUE INDEX index_catalog_verified_namespaces_on_namespace_id ON catalog_verified_namespaces USING btree (namespace_id);
+
 CREATE INDEX index_chat_names_on_team_id_and_chat_id ON chat_names USING btree (team_id, chat_id);
 
 CREATE INDEX index_chat_names_on_user_id ON chat_names USING btree (user_id);
@@ -39600,6 +39623,9 @@ ALTER TABLE ONLY vulnerability_user_mentions
 ALTER TABLE ONLY packages_debian_file_metadata
     ADD CONSTRAINT fk_rails_1ae85be112 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE;
 
+ALTER TABLE ONLY catalog_verified_namespaces
+    ADD CONSTRAINT fk_rails_1b6bb852c0 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
 ALTER TABLE ONLY issuable_slas
     ADD CONSTRAINT fk_rails_1b8768cd63 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
 
diff --git a/spec/factories/ci/catalog/verified_namspaces.rb b/spec/factories/ci/catalog/verified_namspaces.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e5d554de142764199fa15700508cce8e3a58018c
--- /dev/null
+++ b/spec/factories/ci/catalog/verified_namspaces.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+  factory :catalog_verified_namespace, class: 'Ci::Catalog::VerifiedNamespace' do
+    namespace factory: :namespace
+
+    trait :gitlab_maintained do
+      verification_level { :gitlab_maintained }
+    end
+
+    trait :partner do
+      verification_level { :partner }
+    end
+
+    trait :verified_creator do
+      verification_level { :verified_creator }
+    end
+  end
+end
diff --git a/spec/models/ci/catalog/verified_namespace_spec.rb b/spec/models/ci/catalog/verified_namespace_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ac1d0dca9790a361724245ce821778f55c5f4e9f
--- /dev/null
+++ b/spec/models/ci/catalog/verified_namespace_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Catalog::VerifiedNamespace, feature_category: :pipeline_composition do
+  context 'when gitlab maintained namespace is created' do
+    let_it_be(:gitlab_catalog_namespace) { create(:catalog_verified_namespace, :gitlab_maintained) }
+
+    it 'sets verification level to gitlab maintained' do
+      expect(gitlab_catalog_namespace.verification_level).to eq('gitlab_maintained')
+    end
+  end
+
+  context 'when partner namespace is created' do
+    let_it_be(:partner_catalog_namespace) { create(:catalog_verified_namespace, :partner) }
+
+    it 'sets verification level to partner' do
+      expect(partner_catalog_namespace.verification_level).to eq('partner')
+    end
+  end
+
+  context 'when verfied creator namespace is created' do
+    let_it_be(:verified_creator_catalog_namespace) { create(:catalog_verified_namespace, :verified_creator) }
+
+    it 'sets verification level to verified_creator' do
+      expect(verified_creator_catalog_namespace.verification_level).to eq('verified_creator')
+    end
+  end
+
+  it do
+    is_expected.to define_enum_for(:verification_level)
+      .with_values({ gitlab_maintained: 100, partner: 50, verified_creator: 10, unverified: 0 })
+  end
+
+  describe 'validations' do
+    let(:verified_namespace) { subject }
+
+    it { is_expected.to validate_presence_of(:namespace_id) }
+    it { is_expected.to belong_to(:namespace) }
+
+    it do
+      verified_namespace.namespace_id = create(:namespace).id
+      is_expected.to validate_uniqueness_of(:namespace_id)
+    end
+  end
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 67b8931f0c5220815e524a546176297af622215c..a28e8b42d67076c08c09b70a27bc9bf06d0e2ab2 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -25,6 +25,7 @@
     it { is_expected.to have_one :namespace_settings }
     it { is_expected.to have_one :namespace_details }
     it { is_expected.to have_one(:namespace_statistics) }
+    it { is_expected.to have_one(:catalog_verified_namespace) }
     it { is_expected.to have_many :custom_emoji }
     it { is_expected.to have_one :package_setting_relation }
     it { is_expected.to have_one :onboarding_progress }