diff --git a/internal/applier/helm.go b/internal/applier/helm.go
index beb778a85..1da49cad9 100644
--- a/internal/applier/helm.go
+++ b/internal/applier/helm.go
@@ -24,6 +24,7 @@ import (
 	helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
 
 	ocv1 "github.com/operator-framework/operator-controller/api/v1"
+	"github.com/operator-framework/operator-controller/internal/features"
 	"github.com/operator-framework/operator-controller/internal/rukpak/convert"
 	"github.com/operator-framework/operator-controller/internal/rukpak/preflights/crdupgradesafety"
 	"github.com/operator-framework/operator-controller/internal/rukpak/util"
@@ -160,6 +161,10 @@ func (h *Helm) getReleaseState(cl helmclient.ActionInterface, ext *ocv1.ClusterE
 			return nil
 		}, helmclient.AppendInstallPostRenderer(post))
 		if err != nil {
+			if features.OperatorControllerFeatureGate.Enabled(features.PreflightPermissions) {
+				_ = struct{}{} // minimal no-op to satisfy linter
+				// probably need to break out this error as it's the one for helm dry-run as opposed to any returned later
+			}
 			return nil, nil, StateError, err
 		}
 		return nil, desiredRelease, StateNeedsInstall, nil
diff --git a/internal/applier/helm_test.go b/internal/applier/helm_test.go
index 3220fc42b..dd0bc2943 100644
--- a/internal/applier/helm_test.go
+++ b/internal/applier/helm_test.go
@@ -13,12 +13,14 @@ import (
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/release"
 	"helm.sh/helm/v3/pkg/storage/driver"
+	featuregatetesting "k8s.io/component-base/featuregate/testing"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
 
 	v1 "github.com/operator-framework/operator-controller/api/v1"
 	"github.com/operator-framework/operator-controller/internal/applier"
+	"github.com/operator-framework/operator-controller/internal/features"
 )
 
 type mockPreflight struct {
@@ -226,6 +228,71 @@ func TestApply_Installation(t *testing.T) {
 	})
 }
 
+func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) {
+	featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.PreflightPermissions, true)
+
+	t.Run("fails during dry-run installation", func(t *testing.T) {
+		mockAcg := &mockActionGetter{
+			getClientErr:     driver.ErrReleaseNotFound,
+			dryRunInstallErr: errors.New("failed attempting to dry-run install chart"),
+		}
+		helmApplier := applier.Helm{ActionClientGetter: mockAcg}
+
+		objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
+		require.Error(t, err)
+		require.ErrorContains(t, err, "attempting to dry-run install chart")
+		require.Nil(t, objs)
+		require.Empty(t, state)
+	})
+
+	t.Run("fails during pre-flight installation", func(t *testing.T) {
+		mockAcg := &mockActionGetter{
+			getClientErr: driver.ErrReleaseNotFound,
+			installErr:   errors.New("failed installing chart"),
+		}
+		mockPf := &mockPreflight{installErr: errors.New("failed during install pre-flight check")}
+		helmApplier := applier.Helm{ActionClientGetter: mockAcg, Preflights: []applier.Preflight{mockPf}}
+
+		objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
+		require.Error(t, err)
+		require.ErrorContains(t, err, "install pre-flight check")
+		require.Equal(t, applier.StateNeedsInstall, state)
+		require.Nil(t, objs)
+	})
+
+	t.Run("fails during installation", func(t *testing.T) {
+		mockAcg := &mockActionGetter{
+			getClientErr: driver.ErrReleaseNotFound,
+			installErr:   errors.New("failed installing chart"),
+		}
+		helmApplier := applier.Helm{ActionClientGetter: mockAcg}
+
+		objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
+		require.Error(t, err)
+		require.ErrorContains(t, err, "installing chart")
+		require.Equal(t, applier.StateNeedsInstall, state)
+		require.Nil(t, objs)
+	})
+
+	t.Run("successful installation", func(t *testing.T) {
+		mockAcg := &mockActionGetter{
+			getClientErr: driver.ErrReleaseNotFound,
+			desiredRel: &release.Release{
+				Info:     &release.Info{Status: release.StatusDeployed},
+				Manifest: validManifest,
+			},
+		}
+		helmApplier := applier.Helm{ActionClientGetter: mockAcg}
+
+		objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
+		require.NoError(t, err)
+		require.Equal(t, applier.StateNeedsInstall, state)
+		require.NotNil(t, objs)
+		assert.Equal(t, "service-a", objs[0].GetName())
+		assert.Equal(t, "service-b", objs[1].GetName())
+	})
+}
+
 func TestApply_Upgrade(t *testing.T) {
 	testCurrentRelease := &release.Release{
 		Info: &release.Info{Status: release.StatusDeployed},
diff --git a/internal/features/features.go b/internal/features/features.go
index 399bc7356..7b308dae0 100644
--- a/internal/features/features.go
+++ b/internal/features/features.go
@@ -6,13 +6,19 @@ import (
 )
 
 const (
-// Add new feature gates constants (strings)
-// Ex: SomeFeature featuregate.Feature = "SomeFeature"
+	// Add new feature gates constants (strings)
+	// Ex: SomeFeature featuregate.Feature = "SomeFeature"
+	PreflightPermissions featuregate.Feature = "PreflightPermissions"
 )
 
 var operatorControllerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
 	// Add new feature gate definitions
 	// Ex: SomeFeature: {...}
+	PreflightPermissions: {
+		Default:       false,
+		PreRelease:    featuregate.Alpha,
+		LockToDefault: false,
+	},
 }
 
 var OperatorControllerFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()