diff --git a/ee/app/policies/ee/global_policy.rb b/ee/app/policies/ee/global_policy.rb
index 3e9396cdcfe91bbc1acfae01975067dce4632403..064ddb202176f98ce4e0b617ac5c95acd7a6a439 100644
--- a/ee/app/policies/ee/global_policy.rb
+++ b/ee/app/policies/ee/global_policy.rb
@@ -198,6 +198,28 @@ module GlobalPolicy
       rule { security_policy_bot }.policy do
         enable :access_git
       end
+
+      condition(:generate_commit_message_licensed) do
+        next false unless ::Feature.enabled?(:generate_commit_message_flag, @user)
+
+        ::License.feature_available?(:generate_commit_message)
+      end
+
+      condition(:user_allowed_to_use_generate_commit_message) do
+        if generate_commit_message_data.free_access?
+          user.any_group_with_ai_available?
+        else
+          generate_commit_message_data.allowed_for?(@user)
+        end
+      end
+
+      rule { generate_commit_message_licensed & user_allowed_to_use_generate_commit_message }.policy do
+        enable :access_generate_commit_message
+      end
+    end
+
+    def generate_commit_message_data
+      CloudConnector::AvailableServices.find_by_name(:generate_commit_message)
     end
 
     def duo_chat_free_access_was_cut_off?
diff --git a/ee/app/services/llm/generate_commit_message_service.rb b/ee/app/services/llm/generate_commit_message_service.rb
index 38c479028f64269e6afd835a2d78cce85df1c7a5..828a351e44adcfa9a3a78d7e02076e12d1e28036 100644
--- a/ee/app/services/llm/generate_commit_message_service.rb
+++ b/ee/app/services/llm/generate_commit_message_service.rb
@@ -4,9 +4,8 @@ module Llm
   class GenerateCommitMessageService < BaseService
     def valid?
       super &&
-        Feature.enabled?(:generate_commit_message_flag, user) &&
-        resource.resource_parent.root_ancestor.licensed_feature_available?(:generate_commit_message) &&
-        Gitlab::Llm::StageCheck.available?(resource.resource_parent, :generate_commit_message)
+        Gitlab::Llm::StageCheck.available?(resource.resource_parent, :generate_commit_message) &&
+        user.can?(:access_generate_commit_message)
     end
 
     private
diff --git a/ee/config/cloud_connector/access_data.yml b/ee/config/cloud_connector/access_data.yml
index 3f04657c8d6d3407b26b63fbd2c50ff6919ea909..c18b6443dd6b57caca2767288e2ede132afc9d5a 100644
--- a/ee/config/cloud_connector/access_data.yml
+++ b/ee/config/cloud_connector/access_data.yml
@@ -72,3 +72,9 @@ services: # Cloud connector features (i.e. code_suggestions, duo_chat...)
         unit_primitives:
           - code_suggestions
           - duo_chat
+  generate_commit_message:
+    backend: 'gitlab-ai-gateway'
+    bundled_with:
+      duo_enterprise:
+        unit_primitives:
+          - generate_commit_message
diff --git a/ee/spec/lib/cloud_connector/self_signed/access_data_reader_spec.rb b/ee/spec/lib/cloud_connector/self_signed/access_data_reader_spec.rb
index d8c94c6fe2ab693665bf305cb373652d95c4a329..6ede0ae383b6a7fcc67ce9bd5bff27e7e3eec854 100644
--- a/ee/spec/lib/cloud_connector/self_signed/access_data_reader_spec.rb
+++ b/ee/spec/lib/cloud_connector/self_signed/access_data_reader_spec.rb
@@ -56,6 +56,14 @@
       }
     end
 
+    let_it_be(:generate_commit_message_bundled_with) do
+      {
+        "duo_enterprise" => %i[
+          generate_commit_message
+        ]
+      }
+    end
+
     include_examples 'access data reader' do
       let_it_be(:available_service_data_class) { CloudConnector::SelfSigned::AvailableServiceData }
       let_it_be(:arguments_map) do
@@ -65,7 +73,8 @@
           anthropic_proxy: [nil, anthropic_proxy_bundled_with, backend],
           vertex_ai_proxy: [nil, vertex_ai_proxy_bundled_with, backend],
           resolve_vulnerability: [nil, resolve_vulnerability_bundled_with, backend],
-          self_hosted_models: [self_hosted_models_cut_off_date, self_hosted_models_bundled_with, backend]
+          self_hosted_models: [self_hosted_models_cut_off_date, self_hosted_models_bundled_with, backend],
+          generate_commit_message: [nil, generate_commit_message_bundled_with, backend]
         }
       end
     end
diff --git a/ee/spec/policies/global_policy_spec.rb b/ee/spec/policies/global_policy_spec.rb
index b6b82283277e942d208d27b814d1c3ddeb68af2e..43dcff22f7c5704729ae0343d9ba2c6468b7e7a2 100644
--- a/ee/spec/policies/global_policy_spec.rb
+++ b/ee/spec/policies/global_policy_spec.rb
@@ -833,4 +833,36 @@
       it { is_expected.to be_disallowed(:manage_ai_settings) }
     end
   end
+
+  describe 'access_generate_commit_message' do
+    let(:policy) { :access_generate_commit_message }
+
+    where(:flag_enabled, :licensed, :free_access, :any_group_with_ai_available, :allowed_for,
+      :enabled_for_user) do
+      false | false | false | false | false | be_disallowed(:access_generate_commit_message)
+      true  | false | false | false | false | be_disallowed(:access_generate_commit_message)
+      true  | true  | true  | false | false | be_disallowed(:access_generate_commit_message)
+      true  | true  | false | false | false | be_disallowed(:access_generate_commit_message)
+      true  | true  | false | false | true  | be_allowed(:access_generate_commit_message)
+      true  | true  | true  | true  | false | be_allowed(:access_generate_commit_message)
+    end
+
+    with_them do
+      before do
+        stub_licensed_features(generate_commit_message: licensed)
+        stub_feature_flags(generate_commit_message_flag: flag_enabled)
+
+        service_data = CloudConnector::SelfManaged::AvailableServiceData.new(:generate_commit_message, nil, nil)
+        allow(CloudConnector::AvailableServices).to receive(:find_by_name)
+                                                      .with(:generate_commit_message)
+                                                      .and_return(service_data)
+        allow(service_data).to receive(:allowed_for?).with(current_user).and_return(allowed_for)
+        allow(service_data).to receive(:free_access?).and_return(free_access)
+        allow(current_user).to receive(:any_group_with_ai_available?)
+                                   .and_return(any_group_with_ai_available)
+      end
+
+      it { is_expected.to enabled_for_user }
+    end
+  end
 end
diff --git a/ee/spec/requests/api/graphql/mutations/projects/generate_commit_message_spec.rb b/ee/spec/requests/api/graphql/mutations/projects/generate_commit_message_spec.rb
index ecb051f493a8405ddd55f92bcbc57d1b79cc353c..18e00e2a5ea595c120b60b61c5484b71a738ce91 100644
--- a/ee/spec/requests/api/graphql/mutations/projects/generate_commit_message_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/projects/generate_commit_message_spec.rb
@@ -25,6 +25,12 @@
     stub_ee_application_setting(should_check_namespace_plan: true)
     stub_licensed_features(generate_commit_message: true, ai_features: true, experimental_features: true)
     group.namespace_settings.update!(experiment_features_enabled: true)
+
+    service_data = CloudConnector::SelfManaged::AvailableServiceData.new(:generate_commit_message, nil, nil)
+    allow(CloudConnector::AvailableServices).to receive(:find_by_name)
+                                                  .with(:generate_commit_message)
+                                                  .and_return(service_data)
+    allow(service_data).to receive(:allowed_for?).with(current_user).and_return(true)
   end
 
   it 'successfully performs an generate commit message request' do
diff --git a/ee/spec/services/llm/generate_commit_message_service_spec.rb b/ee/spec/services/llm/generate_commit_message_service_spec.rb
index f2bca2f4146d602338f5498f37f665be3924e299..421cfdcaed542bf558678509d803ffe163eeb27d 100644
--- a/ee/spec/services/llm/generate_commit_message_service_spec.rb
+++ b/ee/spec/services/llm/generate_commit_message_service_spec.rb
@@ -14,19 +14,24 @@
   before do
     stub_ee_application_setting(should_check_namespace_plan: true)
     stub_licensed_features(generate_commit_message: true, ai_features: true, experimental_features: true)
+
+    allow(user).to receive(:can?).with("read_merge_request", resource).and_call_original
+    allow(user).to receive(:can?).with(:access_duo_features, resource.project).and_call_original
+    allow(user).to receive(:can?).with(:admin_all_resources).and_call_original
+
+    group.namespace_settings.update!(experiment_features_enabled: true)
   end
 
   describe '#execute' do
     before do
-      project.root_ancestor.namespace_settings.update!(
-        experiment_features_enabled: true
-      )
       allow(Llm::CompletionWorker).to receive(:perform_for)
     end
 
     context 'when the user is permitted to view the merge request' do
       before do
         group.add_developer(user)
+
+        allow(user).to receive(:can?).with(:access_generate_commit_message).and_return(true)
       end
 
       it_behaves_like 'schedules completion worker' do
@@ -62,7 +67,7 @@
   describe '#valid?' do
     using RSpec::Parameterized::TableSyntax
 
-    where(:experiment_features_enabled, :result) do
+    where(:access_generate_commit_message, :result) do
       true   | true
       false  | false
     end
@@ -70,9 +75,8 @@
     with_them do
       before do
         group.add_maintainer(user)
-        project.root_ancestor.namespace_settings.update!(
-          experiment_features_enabled: experiment_features_enabled
-        )
+
+        allow(user).to receive(:can?).with(:access_generate_commit_message).and_return(access_generate_commit_message)
       end
 
       subject { described_class.new(user, resource, options) }