Repository: ratify-project/ratify
Pull request #1743 (2024-08-25 08:34:01)

chore: automated PR to main 2024-08-25
Automated Pull Request to main branch


Patch:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 43d4615..74475b6 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -14,7 +14,7 @@
 # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/go/.devcontainer/base.Dockerfile
 
 # [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1.22-bullseye, 1.21-bullseye, 1, 1.19, 1.18, 1-bullseye, 1.19-bullseye, 1.18-bullseye, 1-buster, 1.19-buster, 1.18-buster
-FROM mcr.microsoft.com/vscode/devcontainers/go:1.22-bullseye@sha256:8cb4ef68d402b85b52c955ff16410e499cf7604527634d99a2a1922a93751795
+FROM mcr.microsoft.com/vscode/devcontainers/go:1.22-bullseye@sha256:fdc107cfc1c9423d56a0c633239576ac3f7ca503453e07976efa2a0ae9e0cb65
 
 # [Choice] Node.js version: none, lts/*, 18, 16, 14
 ARG NODE_VERSION="none"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 89ef00a..47aeb46 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -38,7 +38,7 @@ jobs:
         with:
           go-version: "1.22"
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@429e1977040da7a23b6822b13c129cd1ba93dbb2 # tag=v3.26.2
+        uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # tag=v3.26.5
         with:
           languages: go
       - name: Run tidy
@@ -46,4 +46,4 @@ jobs:
       - name: Build CLI
         run: make build
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@429e1977040da7a23b6822b13c129cd1ba93dbb2 # tag=v3.26.2
+        uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # tag=v3.26.5
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 16a1dfb..80e317a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,7 +26,7 @@ jobs:
           fetch-depth: 0
     
     - name: Install Syft
-      uses: anchore/sbom-action/download-syft@ab9d16d4b419c9d1a02df5213fa0ebe965ca5a57 # v0.17.1
+      uses: anchore/sbom-action/download-syft@61119d458adab75f756bc0b9e4bde25725f86a7a # v0.17.2
           
     - name: Set up Go
       uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index d998d78..1c28269 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -55,6 +55,6 @@ jobs:
           retention-days: 5
       
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # tag=v3.26.2
+        uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # tag=v3.26.5
         with:
           sarif_file: results.sarif
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fffc7b8..406f4f3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,15 +10,33 @@ Welcome! We are very happy to accept community contributions to Ratify, whether
 * Checkout the repo locally with `git clone git@github.com:{your_username}/ratify.git`.
 * Build the Ratify CLI with `go build -o ./bin/ratify ./cmd/ratify` or if on Mac/Linux/WSL `make build-cli`.
 
+## Feature Enhancements
+For non-trivial enhancements or bug fixes, please start by raising a document PR. You can refer to the example [here](https://github.com/ratify-project/ratify/blame/dev/docs/proposals/Release-Supply-Chain-Metadata.md).
+
+Major user experience updates should be documented in [/doc/proposals](https://github.com/ratify-project/ratify/tree/dev/docs/proposals). Changes to technical implementation should be added to [/doc/design](https://github.com/ratify-project/ratify/tree/dev/docs/design).  
+
+Consider adding the following section where applicable:
+- Proposed changes
+- Proposed feature flag
+- Impacted code paths
+- Required test coverage
+- Backward compatibility
+- Performance impact
+- Security consideration
+- Open questions
+  
+This approach ensures that the changes are well-documented and reviewed before implementation.
+
 ## Pull Requests
 
 If you'd like to start contributing to Ratify, you can search for issues tagged as "good first issue" [here](https://github.com/ratify-project/ratify/labels/good%20first%20issue).
 
 We use the `dev` branch as the our default branch. PRs passing the basic set of validation can be merged to the `dev` branch, we then run the full suite of validation including cloud specific tests on `dev` before changes can be merged into `main`. All ratify release are cut from the `main` branch. A sample PR process is outlined below:
 1. Fork this repo and create your dev branch from default `dev` branch.
-2. Create a PR against default branch
-3. Maintainer approval and e2e test validation is required for completing the PR.
-4. On PR complete, the `push` event will trigger an automated PR targeting the `main` branch where we run a full suite validation including cloud specific tests.
+2. Create a PR against default branch.
+3. Add new unit test and [e2e test](https://github.com/ratify-project/ratify/tree/dev/test/bats) where approriate.
+4. Maintainer approval and e2e test validation is required for completing the PR.
+5. On PR complete, the `push` event will trigger an automated PR targeting the `main` branch where we run a full suite validation including cloud specific tests.
 6. Manual merge is required to complete the PR. (**Please keep individual commits to maintain commit history**)
 
 If the PR contains a regression that could not pass the full validation, please revert the change to unblock others:
@@ -54,7 +72,9 @@ The Ratify project is composed of the following main components:
 
 Ratify can run through cli command or run as a http server. Create a [launch.json](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) file in the .vscode directory, then hit F5 to debug. Note the first debug session may take a few minutes to load, subsequent session will be much faster.
 
-Sample json for cli:
+Here is a sample json for cli. Note that for the following sample json to successfully work, you need to make sure that `verificationCerts` attribute of the verifier in your config file points to the notation verifier's certificate file. In order to do that, you can download the cert file with the following command:
+`curl -sSLO https://raw.githubusercontent.com/deislabs/ratify/main/test/testdata/notation.crt`, 
+and then modify the config file by setting the `verificationCerts` attribute in the notation verifier to the downloaded cert file path.
 
 ```json
 {
@@ -65,7 +85,11 @@ Sample json for cli:
       "request": "launch",
       "mode": "debug",
       "program": "${workspaceFolder}/cmd/ratify",
-      "args": ["verify", "-s", "ratify.azurecr.io/testimage@sha256:9515b691095051d68b4409a30c4819c98bd6f4355d5993a7487687cdc6d47cc3"]
+      "args": [
+                "verify", 
+                "-s", "ghcr.io/deislabs/ratify/notary-image:signed", 
+                "-c", "${workspaceFolder}/test/bats/tests/config/config_cli.json"
+      ]
     }]
 }
 ```
diff --git a/api/v1beta1/keymanagementproviders_types.go b/api/v1beta1/keymanagementproviders_types.go
index d957f38..2797cca 100644
--- a/api/v1beta1/keymanagementproviders_types.go
+++ b/api/v1beta1/keymanagementproviders_types.go
@@ -31,6 +31,10 @@ type KeyManagementProviderSpec struct {
 	// Name of the key management provider
 	Type string `json:"type,omitempty"`
 
+	// Refresh interval for fetching the certificate/key files from the provider. Only for providers that are refreshable. The value is in the format of "1h30m" where "h" means hour and "m" means minute. Valid time units are units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+	// +kubebuilder:default=""
+	RefreshInterval string `json:"refreshInterval,omitempty"`
+
 	// +kubebuilder:pruning:PreserveUnknownFields
 	// Parameters of the key management provider
 	Parameters runtime.RawExtension `json:"parameters,omitempty"`
diff --git a/api/v1beta1/namespacedkeymanagementprovider_types.go b/api/v1beta1/namespacedkeymanagementprovider_types.go
index f4d3e58..d124e88 100644
--- a/api/v1beta1/namespacedkeymanagementprovider_types.go
+++ b/api/v1beta1/namespacedkeymanagementprovider_types.go
@@ -32,6 +32,10 @@ type NamespacedKeyManagementProviderSpec struct {
 	// Name of the key management provider
 	Type string `json:"type,omitempty"`
 
+	// Refresh interval for the key management provider. Only used if the key management provider is refreshable. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+	// +kubebuilder:default=""
+	RefreshInterval string `json:"refreshInterval,omitempty"`
+
 	// +kubebuilder:pruning:PreserveUnknownFields
 	// Parameters of the key management provider
 	Parameters runtime.RawExtension `json:"parameters,omitempty"`
diff --git a/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml b/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
index ec41a63..bbb7a9c 100644
--- a/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
+++ b/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
@@ -14,73 +14,87 @@ spec:
     singular: keymanagementprovider
   scope: Cluster
   versions:
-  - additionalPrinterColumns:
-    - jsonPath: .status.issuccess
-      name: IsSuccess
-      type: boolean
-    - jsonPath: .status.brieferror
-      name: Error
-      type: string
-    - jsonPath: .status.lastfetchedtime
-      name: LastFetchedTime
-      type: date
-    name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: KeyManagementProvider is the Schema for the keymanagementproviders
-          API
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: KeyManagementProviderSpec defines the desired state of KeyManagementProvider
-            properties:
-              parameters:
-                description: Parameters of the key management provider
-                type: object
-                x-kubernetes-preserve-unknown-fields: true
-              type:
-                description: Name of the key management provider
-                type: string
-            type: object
-          status:
-            description: KeyManagementProviderStatus defines the observed state of
-              KeyManagementProvider
-            properties:
-              brieferror:
-                description: Truncated error message if the message is too long
-                type: string
-              error:
-                description: Error message if operation was unsuccessful
-                type: string
-              issuccess:
-                description: Is successful in loading certificate/key files
-                type: boolean
-              lastfetchedtime:
-                description: The time stamp of last successful certificate/key fetch
-                  operation. If operation failed, last fetched time shows the time
-                  of error
-                format: date-time
-                type: string
+    - additionalPrinterColumns:
+        - jsonPath: .status.issuccess
+          name: IsSuccess
+          type: boolean
+        - jsonPath: .status.brieferror
+          name: Error
+          type: string
+        - jsonPath: .status.lastfetchedtime
+          name: LastFetchedTime
+          type: date
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description:
+            KeyManagementProvider is the Schema for the keymanagementproviders
+            API
+          properties:
+            apiVersion:
+              description:
+                "APIVersion defines the versioned schema of this representation
+                of an object. Servers should convert recognized schemas to the latest
+                internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
+              type: string
+            kind:
+              description:
+                "Kind is a string value representing the REST resource this
+                object represents. Servers may infer this from the endpoint the client
+                submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: KeyManagementProviderSpec defines the desired state of KeyManagementProvider
               properties:
-                description: provider specific properties of the each individual certificate/key
-                type: object
-                x-kubernetes-preserve-unknown-fields: true
-            required:
-            - issuccess
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
+                refreshInterval:
+                  default: ""
+                  description:
+                    Refresh interval for fetching the certificate/key files
+                    from the provider. Only for providers that are refreshable. The
+                    value is in the format of "1h30m" where "h" means hour and "m" means
+                    minute. Valid time units are units are "ns", "us" (or "µs"), "ms",
+                    "s", "m", "h".
+                  type: string
+                parameters:
+                  description: Parameters of the key management provider
+                  type: object
+                  x-kubernetes-preserve-unknown-fields: true
+                type:
+                  description: Name of the key management provider
+                  type: string
+              type: object
+            status:
+              description:
+                KeyManagementProviderStatus defines the observed state of
+                KeyManagementProvider
+              properties:
+                brieferror:
+                  description: Truncated error message if the message is too long
+                  type: string
+                error:
+                  description: Error message if operation was unsuccessful
+                  type: string
+                issuccess:
+                  description: Is successful in loading certificate/key files
+                  type: boolean
+                lastfetchedtime:
+                  description:
+                    The time stamp of last successful certificate/key fetch
+                    operation. If operation failed, last fetched time shows the time
+                    of error
+                  format: date-time
+                  type: string
+                properties:
+                  description: provider specific properties of the each individual certificate/key
+                  type: object
+                  x-kubernetes-preserve-unknown-fields: true
+              required:
+                - issuccess
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml b/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml
index ef70f27..4055178 100644
--- a/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml
+++ b/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml
@@ -14,74 +14,89 @@ spec:
     singular: namespacedkeymanagementprovider
   scope: Namespaced
   versions:
-  - additionalPrinterColumns:
-    - jsonPath: .status.issuccess
-      name: IsSuccess
-      type: boolean
-    - jsonPath: .status.brieferror
-      name: Error
-      type: string
-    - jsonPath: .status.lastfetchedtime
-      name: LastFetchedTime
-      type: date
-    name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders
-          API
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: NamespacedKeyManagementProviderSpec defines the desired state
-              of NamespacedKeyManagementProvider
-            properties:
-              parameters:
-                description: Parameters of the key management provider
-                type: object
-                x-kubernetes-preserve-unknown-fields: true
-              type:
-                description: Name of the key management provider
-                type: string
-            type: object
-          status:
-            description: NamespacedKeyManagementProviderStatus defines the observed
-              state of NamespacedKeyManagementProvider
-            properties:
-              brieferror:
-                description: Truncated error message if the message is too long
-                type: string
-              error:
-                description: Error message if operation was unsuccessful
-                type: string
-              issuccess:
-                description: Is successful in loading certificate/key files
-                type: boolean
-              lastfetchedtime:
-                description: The time stamp of last successful certificate/key fetch
-                  operation. If operation failed, last fetched time shows the time
-                  of error
-                format: date-time
-                type: string
+    - additionalPrinterColumns:
+        - jsonPath: .status.issuccess
+          name: IsSuccess
+          type: boolean
+        - jsonPath: .status.brieferror
+          name: Error
+          type: string
+        - jsonPath: .status.lastfetchedtime
+          name: LastFetchedTime
+          type: date
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description:
+            NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders
+            API
+          properties:
+            apiVersion:
+              description:
+                "APIVersion defines the versioned schema of this representation
+                of an object. Servers should convert recognized schemas to the latest
+                internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
+              type: string
+            kind:
+              description:
+                "Kind is a string value representing the REST resource this
+                object represents. Servers may infer this from the endpoint the client
+                submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
+              type: string
+            metadata:
+              type: object
+            spec:
+              description:
+                NamespacedKeyManagementProviderSpec defines the desired state
+                of NamespacedKeyManagementProvider
               properties:
-                description: provider specific properties of the each individual certificate/key
-                type: object
-                x-kubernetes-preserve-unknown-fields: true
-            required:
-            - issuccess
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
+                refreshInterval:
+                  default: ""
+                  description:
+                    Refresh interval for fetching the certificate/key files
+                    from the provider. Only for providers that are refreshable. The
+                    value is in the format of "1h30m" where "h" means hour and "m" means
+                    minute. Valid time units are units are "ns", "us" (or "µs"), "ms",
+                    "s", "m", "h".
+                  type: string
+                parameters:
+                  description: Parameters of the key management provider
+                  type: object
+                  x-kubernetes-preserve-unknown-fields: true
+                type:
+                  description: Name of the key management provider
+                  type: string
+              type: object
+            status:
+              description:
+                NamespacedKeyManagementProviderStatus defines the observed
+                state of NamespacedKeyManagementProvider
+              properties:
+                brieferror:
+                  description: Truncated error message if the message is too long
+                  type: string
+                error:
+                  description: Error message if operation was unsuccessful
+                  type: string
+                issuccess:
+                  description: Is successful in loading certificate/key files
+                  type: boolean
+                lastfetchedtime:
+                  description:
+                    The time stamp of last successful certificate/key fetch
+                    operation. If operation failed, last fetched time shows the time
+                    of error
+                  format: date-time
+                  type: string
+                properties:
+                  description: provider specific properties of the each individual certificate/key
+                  type: object
+                  x-kubernetes-preserve-unknown-fields: true
+              required:
+                - issuccess
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/charts/ratify/values.yaml b/charts/ratify/values.yaml
index a2604f3..7e501bc 100644
--- a/charts/ratify/values.yaml
+++ b/charts/ratify/values.yaml
@@ -1,7 +1,7 @@
 image:
   repository: ghcr.io/ratify-project/ratify
   crdRepository: ghcr.io/ratify-project/ratify-crds
-  tag: v1.2.1
+  tag: v1.2.2
   pullPolicy: IfNotPresent
 
 nameOverride: ""
diff --git a/config/config_cli.json b/config/config_cli.json
new file mode 100644
index 0000000..04170bf
--- /dev/null
+++ b/config/config_cli.json
@@ -0,0 +1,55 @@
+{
+    "store": {
+        "version": "1.0.0",
+        "plugins": [
+            {
+                "name": "oras",
+                "cosignEnabled": true,
+                "useHttp": true
+            }
+        ]
+    },
+    "policy": {
+        "version": "1.0.0",
+        "plugin": {
+            "name": "configPolicy"
+        }
+    },
+    "verifier": {
+        "version": "1.0.0",
+        "plugins": [
+            {
+                "name": "cosign",
+                "artifactTypes": "application/vnd.dev.cosign.artifact.sig.v1+json",
+                "key": ".staging/cosign/cosign.pub"
+            },
+            {
+                "name": "notation",
+                "artifactTypes": "application/vnd.cncf.notary.signature",
+                "verificationCerts": [
+                    "~/.config/notation/localkeys/notation.crt"
+                ],
+                "trustPolicyDoc": {
+                    "version": "1.0",
+                    "trustPolicies": [
+                        {
+                            "name": "default",
+                            "registryScopes": [
+                                "*"
+                            ],
+                            "signatureVerification": {
+                                "level": "strict"
+                            },
+                            "trustStores": [
+                                "ca:certs"
+                            ],
+                            "trustedIdentities": [
+                                "*"
+                            ]
+                        }
+                    ]
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml b/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
index d924d7d..2a3658d 100644
--- a/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
@@ -54,6 +54,14 @@ spec:
                 description: Parameters of the key management provider
                 type: object
                 x-kubernetes-preserve-unknown-fields: true
+              refreshInterval:
+                default: ""
+                description: Refresh interval for fetching the certificate/key files
+                  from the provider. Only for providers that are refreshable. The
+                  value is in the format of "1h30m" where "h" means hour and "m" means
+                  minute. Valid time units are units are "ns", "us" (or "µs"), "ms",
+                  "s", "m", "h".
+                type: string
               type:
                 description: Name of the key management provider
                 type: string
diff --git a/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml b/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
index 188294d..0054b1e 100644
--- a/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
@@ -55,6 +55,12 @@ spec:
                 description: Parameters of the key management provider
                 type: object
                 x-kubernetes-preserve-unknown-fields: true
+              refreshInterval:
+                default: ""
+                description: Refresh interval for the key management provider. Only
+                  used if the key management provider is refreshable. Valid time units
+                  are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+                type: string
               type:
                 description: Name of the key management provider
                 type: string
diff --git a/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml
index ae25f7f..7f92290 100644
--- a/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml
+++ b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml
@@ -1,13 +1,13 @@
 apiVersion: config.ratify.deislabs.io/v1beta1
 kind: KeyManagementProvider
 metadata:
-  name: keymanagementprovider-inline
+  name: keymanagementprovider-akv
 spec:
   type: azurekeyvault
   parameters:
     vaultURI: https://yourkeyvault.vault.azure.net/
     certificates:
       - name: yourCertName
-        version: yourCertVersion # Optional, fetch latest version if empty 
+        version: yourCertVersion # Optional, fetch latest version if empty
     tenantID:
-    clientID: 
+    clientID:
diff --git a/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml
new file mode 100644
index 0000000..3b820f6
--- /dev/null
+++ b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml
@@ -0,0 +1,14 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: KeyManagementProvider
+metadata:
+  name: keymanagementprovider-akv
+spec:
+  type: azurekeyvault
+  refreshInterval: 1m
+  parameters:
+    vaultURI: https://yourkeyvault.vault.azure.net/
+    certificates:
+      - name: yourCertName
+        version: yourCertVersion # Optional, fetch latest version if empty
+    tenantID:
+    clientID:
diff --git a/config/samples/clustered/verifier/config_v1beta1_verifier_notation_kmprovider.yaml b/config/samples/clustered/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
index 407f08f..36caa15 100644
--- a/config/samples/clustered/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
+++ b/config/samples/clustered/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
@@ -9,10 +9,8 @@ spec:
     verificationCertStores:
       certs:
         - kmprovider-akv
-        - kmprovider-akv1
       certs1:
-        - kmprovider-akv2
-        - kmprovider-akv3
+        - kmprovider-akv1
     trustPolicyDoc:
       version: "1.0"
       trustPolicies:
diff --git a/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml
index e8971e1..7b9afff 100644
--- a/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml
+++ b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml
@@ -1,13 +1,13 @@
 apiVersion: config.ratify.deislabs.io/v1beta1
 kind: NamespacedKeyManagementProvider
 metadata:
-  name: keymanagementprovider-inline
+  name: keymanagementprovider-akv
 spec:
   type: azurekeyvault
   parameters:
     vaultURI: https://yourkeyvault.vault.azure.net/
     certificates:
       - name: yourCertName
-        version: yourCertVersion # Optional, fetch latest version if empty 
+        version: yourCertVersion # Optional, fetch latest version if empty
     tenantID:
-    clientID: 
+    clientID:
diff --git a/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml
new file mode 100644
index 0000000..28aeaac
--- /dev/null
+++ b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv_refresh_enabled.yaml
@@ -0,0 +1,14 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: NamespacedKeyManagementProvider
+metadata:
+  name: keymanagementprovider-akv
+spec:
+  type: azurekeyvault
+  refreshInterval: 1m
+  parameters:
+    vaultURI: https://yourkeyvault.vault.azure.net/
+    certificates:
+      - name: yourCertName
+        version: yourCertVersion # Optional, fetch latest version if empty
+    tenantID:
+    clientID:
diff --git a/config/samples/namespaced/verifier/config_v1beta1_verifier_notation_kmprovider.yaml b/config/samples/namespaced/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
index a9a8d7d..447f959 100644
--- a/config/samples/namespaced/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
+++ b/config/samples/namespaced/verifier/config_v1beta1_verifier_notation_kmprovider.yaml
@@ -9,10 +9,8 @@ spec:
     verificationCertStores:
       certs:
         - kmprovider-akv
-        - kmprovider-akv1
       certs1:
-        - kmprovider-akv2
-        - kmprovider-akv3
+        - kmprovider-akv1
     trustPolicyDoc:
       version: "1.0"
       trustPolicies:
diff --git a/errors/errors.go b/errors/errors.go
index 8f98e6d..9305f79 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -118,4 +118,18 @@ var (
 		Message:     "data encoding failure",
 		Description: "Failed to encode data. Please verify the encoding data.",
 	})
+
+	// ErrorCodeNotFound is returned when the requested resource is not found.
+	ErrorCodeNotFound = Register("errcode", ErrorDescriptor{
+		Value:       "RESOURCE_NOT_FOUND",
+		Message:     "resource not found",
+		Description: "The requested resource is not found. Please verify the resource exists.",
+	})
+
+	// ErrorCodeForbidden is returned when the requested operation is forbidden.
+	ErrorCodeForbidden = Register("errcode", ErrorDescriptor{
+		Value:       "OPERATION_FORBIDDEN",
+		Message:     "operation forbidden",
+		Description: "The requested operation is forbidden. Please verify the permission to the requested resource.",
+	})
 )
diff --git a/go.mod b/go.mod
index 1e53a83..3b59e5b 100644
--- a/go.mod
+++ b/go.mod
@@ -14,14 +14,14 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2
 	github.com/aws/aws-sdk-go-v2 v1.30.4
-	github.com/aws/aws-sdk-go-v2/config v1.27.28
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.28
+	github.com/aws/aws-sdk-go-v2/config v1.27.30
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.29
 	github.com/aws/aws-sdk-go-v2/service/ecr v1.28.6
 	github.com/cespare/xxhash/v2 v2.3.0
 	github.com/dapr/go-sdk v1.8.0
 	github.com/dgraph-io/ristretto v0.1.1
 	github.com/distribution/reference v0.5.0
-	github.com/docker/cli v27.1.1+incompatible
+	github.com/docker/cli v27.1.2+incompatible
 	github.com/docker/distribution v2.8.3+incompatible
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/go-jose/go-jose/v3 v3.0.3
@@ -143,7 +143,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect
 	github.com/aws/smithy-go v1.20.4 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
diff --git a/go.sum b/go.sum
index 0978a85..619fba4 100644
--- a/go.sum
+++ b/go.sum
@@ -127,10 +127,10 @@ github.com/aws/aws-sdk-go v1.51.6 h1:Ld36dn9r7P9IjU8WZSaswQ8Y/XUCRpewim5980DwYiU
 github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
 github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
 github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
-github.com/aws/aws-sdk-go-v2/config v1.27.28 h1:OTxWGW/91C61QlneCtnD62NLb4W616/NM1jA8LhJqbg=
-github.com/aws/aws-sdk-go-v2/config v1.27.28/go.mod h1:uzVRVtJSU5EFv6Fu82AoVFKozJi2ZCY6WRCXj06rbvs=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.28 h1:m8+AHY/ND8CMHJnPoH7PJIRakWGa4gbfbxuY9TGTUXM=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.28/go.mod h1:6TF7dSc78ehD1SL6KpRIPKMA1GyyWflIkjqg+qmf4+c=
+github.com/aws/aws-sdk-go-v2/config v1.27.30 h1:AQF3/+rOgeJBQP3iI4vojlPib5X6eeOYoa/af7OxAYg=
+github.com/aws/aws-sdk-go-v2/config v1.27.30/go.mod h1:yxqvuubha9Vw8stEgNiStO+yZpP68Wm9hLmcm+R/Qk4=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.29 h1:CwGsupsXIlAFYuDVHv1nnK0wnxO0wZ/g1L8DSK/xiIw=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.29/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
@@ -153,8 +153,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/
 github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac=
-github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 h1:iAckBT2OeEK/kBDyN/jDtpEExhjeeA/Im2q4X0rJZT8=
-github.com/aws/aws-sdk-go-v2/service/sts v1.30.4/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
+github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8=
+github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
 github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
 github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M=
@@ -238,8 +238,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
 github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
-github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v27.1.2+incompatible h1:nYviRv5Y+YAKx3dFrTvS1ErkyVVunKOhoweCTE1BsnI=
+github.com/docker/cli v27.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
diff --git a/library/multi-tenancy-validation/template.yaml b/library/multi-tenancy-validation/template.yaml
index 577c709..5037c65 100644
--- a/library/multi-tenancy-validation/template.yaml
+++ b/library/multi-tenancy-validation/template.yaml
@@ -44,5 +44,5 @@ spec:
         general_violation[{"result": result}] {
           subject_validation := remote_data.responses[_]
           subject_validation[1].isSuccess == false
-          result := sprintf("Failed to verify the artifact: %s", [subject_validation[0]])
+          result := sprintf("Time=%s, failed to verify the artifact: %s, trace-id: %s", [subject_validation[1].timestamp, subject_validation[0], subject_validation[1].traceID])
         }
diff --git a/pkg/certificateprovider/azurekeyvault/provider.go b/pkg/certificateprovider/azurekeyvault/provider.go
index 17bbd12..6565bca 100644
--- a/pkg/certificateprovider/azurekeyvault/provider.go
+++ b/pkg/certificateprovider/azurekeyvault/provider.go
@@ -90,7 +90,7 @@ func (s *akvCertProvider) GetCertificates(ctx context.Context, attrib map[string
 	}
 
 	if len(keyVaultCerts) == 0 {
-		return nil, nil, re.ErrorCodeConfigInvalid.NewError(re.CertProvider, providerName, re.EmptyLink, nil, "no keyvault certificate configured", re.PrintStackTrace)
+		return nil, nil, re.ErrorCodeConfigInvalid.NewError(re.CertProvider, providerName, re.EmptyLink, nil, "no keyvault certificate configured", re.HideStackTrace)
 	}
 
 	logger.GetLogger(ctx, logOpt).Debugf("vaultURI %s", keyvaultURI)
@@ -155,7 +155,7 @@ func getKeyvaultRequestObj(ctx context.Context, attrib map[string]string) ([]typ
 	for i, object := range objects.Array {
 		var keyVaultCert types.KeyVaultCertificate
 		if err = yaml.Unmarshal([]byte(object), &keyVaultCert); err != nil {
-			return nil, re.ErrorCodeDataDecodingFailure.NewError(re.CertProvider, providerName, re.EmptyLink, err, fmt.Sprintf("unmarshal failed for keyVaultCerts at index: %d", i), re.PrintStackTrace)
+			return nil, re.ErrorCodeDataDecodingFailure.NewError(re.CertProvider, providerName, re.EmptyLink, err, fmt.Sprintf("unmarshal failed for keyVaultCerts at index: %d", i), re.HideStackTrace)
 		}
 		// remove whitespace from all fields in keyVaultCert
 		formatKeyVaultCertificate(&keyVaultCert)
@@ -213,12 +213,12 @@ func initializeKvClient(ctx context.Context, keyVaultEndpoint, tenantID, clientI
 
 	err := kvClient.AddToUserAgent("ratify")
 	if err != nil {
-		return nil, re.ErrorCodeConfigInvalid.NewError(re.CertProvider, providerName, re.AKVLink, err, "failed to add user agent to keyvault client", re.PrintStackTrace)
+		return nil, re.ErrorCodeConfigInvalid.NewError(re.CertProvider, providerName, re.AKVLink, err, "failed to add user agent to keyvault client", re.HideStackTrace)
 	}
 
 	kvClient.Authorizer, err = getAuthorizerForWorkloadIdentity(ctx, tenantID, clientID, kvEndpoint)
 	if err != nil {
-		return nil, re.ErrorCodeAuthDenied.NewError(re.CertProvider, providerName, re.AKVLink, err, "failed to get authorizer for keyvault client", re.PrintStackTrace)
+		return nil, re.ErrorCodeAuthDenied.NewError(re.CertProvider, providerName, re.AKVLink, err, "failed to get authorizer for keyvault client", re.HideStackTrace)
 	}
 	return &kvClient, nil
 }
diff --git a/pkg/controllers/clusterresource/keymanagementprovider_controller.go b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
index 11a32ed..93dda98 100644
--- a/pkg/controllers/clusterresource/keymanagementprovider_controller.go
+++ b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
@@ -18,24 +18,17 @@ package clusterresource
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"maps"
 
-	"github.com/ratify-project/ratify/internal/constants"
 	_ "github.com/ratify-project/ratify/pkg/keymanagementprovider/azurekeyvault" // register azure key vault key management provider
 	_ "github.com/ratify-project/ratify/pkg/keymanagementprovider/inline"        // register inline key management provider
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/refresh"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/predicate"
 
 	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
-	cutils "github.com/ratify-project/ratify/pkg/controllers/utils"
-	kmp "github.com/ratify-project/ratify/pkg/keymanagementprovider"
-	"github.com/sirupsen/logrus"
 )
 
 // KeyManagementProviderReconciler reconciles a KeyManagementProvider object
@@ -44,76 +37,33 @@ type KeyManagementProviderReconciler struct {
 	Scheme *runtime.Scheme
 }
 
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders/status,verbs=get;update;patch
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders/finalizers,verbs=update
-func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	logger := logrus.WithContext(ctx)
-
-	var resource = req.Name
-	var keyManagementProvider configv1beta1.KeyManagementProvider
-
-	logger.Infof("reconciling cluster key management provider '%v'", resource)
-
-	if err := r.Get(ctx, req.NamespacedName, &keyManagementProvider); err != nil {
-		if apierrors.IsNotFound(err) {
-			logger.Infof("deletion detected, removing key management provider %v", resource)
-			kmp.DeleteCertificatesFromMap(resource)
-			kmp.DeleteKeysFromMap(resource)
-		} else {
-			logger.Error(err, "unable to fetch key management provider")
-		}
-
-		return ctrl.Result{}, client.IgnoreNotFound(err)
-	}
-
-	lastFetchedTime := metav1.Now()
-	isFetchSuccessful := false
-
-	// get certificate store list to check if certificate store is configured
-	// TODO: remove check in v2.0.0+
-	var certificateStoreList configv1beta1.CertificateStoreList
-	if err := r.List(ctx, &certificateStoreList); err != nil {
-		logger.Error(err, "unable to list certificate stores")
+func (r *KeyManagementProviderReconciler) ReconcileWithConfig(ctx context.Context, config map[string]interface{}) (ctrl.Result, error) {
+	refresher, err := refresh.CreateRefresherFromConfig(config)
+	if err != nil {
 		return ctrl.Result{}, err
 	}
-	// if certificate store is configured, return error. Only one of certificate store and key management provider can be configured
-	if len(certificateStoreList.Items) > 0 {
-		// Note: for backwards compatibility in upgrade scenarios, Ratify will only log a warning statement.
-		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
-	}
-
-	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	err = refresher.Refresh(ctx)
 	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
 		return ctrl.Result{}, err
 	}
 
-	// fetch certificates and store in map
-	certificates, certAttributes, err := provider.GetCertificates(ctx)
-	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
-		return ctrl.Result{}, fmt.Errorf("Error fetching certificates in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	result, ok := refresher.GetResult().(ctrl.Result)
+	if !ok {
+		return ctrl.Result{}, fmt.Errorf("unexpected type returned from GetResult: %T", refresher.GetResult())
 	}
+	return result, nil
+}
 
-	// fetch keys and store in map
-	keys, keyAttributes, err := provider.GetKeys(ctx)
-	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
-		return ctrl.Result{}, fmt.Errorf("Error fetching keys in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=keymanagementproviders/finalizers,verbs=update
+func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	refresherConfig := map[string]interface{}{
+		"type":    refresh.KubeRefresherType,
+		"client":  r.Client,
+		"request": req,
 	}
-	kmp.SetCertificatesInMap(resource, certificates)
-	kmp.SetKeysInMap(resource, keyManagementProvider.Spec.Type, keys)
-	// merge certificates and keys status into one
-	maps.Copy(keyAttributes, certAttributes)
-	isFetchSuccessful = true
-	emptyErrorString := ""
-	writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, keyAttributes)
-
-	logger.Infof("%v certificate(s) & %v key(s) fetched for key management provider %v", len(certificates), len(keys), resource)
-
-	// returning empty result and no error to indicate we’ve successfully reconciled this object
-	return ctrl.Result{}, nil
+	return r.ReconcileWithConfig(ctx, refresherConfig)
 }
 
 // SetupWithManager sets up the controller with the Manager.
@@ -127,46 +77,3 @@ func (r *KeyManagementProviderReconciler) SetupWithManager(mgr ctrl.Manager) err
 		For(&configv1beta1.KeyManagementProvider{}).WithEventFilter(pred).
 		Complete(r)
 }
-
-// writeKMProviderStatus updates the status of the key management provider resource
-func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.KeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
-	if isSuccess {
-		updateKMProviderSuccessStatus(keyManagementProvider, &operationTime, kmProviderStatus)
-	} else {
-		updateKMProviderErrorStatus(keyManagementProvider, errorString, &operationTime)
-	}
-	if statusErr := r.Status().Update(ctx, keyManagementProvider); statusErr != nil {
-		logger.Error(statusErr, ",unable to update key management provider error status")
-	}
-}
-
-// updateKMProviderErrorStatus updates the key management provider status with error, brief error and last fetched time
-func updateKMProviderErrorStatus(keyManagementProvider *configv1beta1.KeyManagementProvider, errorString string, operationTime *metav1.Time) {
-	// truncate brief error string to maxBriefErrLength
-	briefErr := errorString
-	if len(errorString) > constants.MaxBriefErrLength {
-		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
-	}
-	keyManagementProvider.Status.IsSuccess = false
-	keyManagementProvider.Status.Error = errorString
-	keyManagementProvider.Status.BriefError = briefErr
-	keyManagementProvider.Status.LastFetchedTime = operationTime
-}
-
-// updateKMProviderSuccessStatus updates the key management provider status if status argument is non nil
-// Success status includes last fetched time and other provider-specific properties
-func updateKMProviderSuccessStatus(keyManagementProvider *configv1beta1.KeyManagementProvider, lastOperationTime *metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
-	keyManagementProvider.Status.IsSuccess = true
-	keyManagementProvider.Status.Error = ""
-	keyManagementProvider.Status.BriefError = ""
-	keyManagementProvider.Status.LastFetchedTime = lastOperationTime
-
-	if kmProviderStatus != nil {
-		jsonString, _ := json.Marshal(kmProviderStatus)
-
-		raw := runtime.RawExtension{
-			Raw: jsonString,
-		}
-		keyManagementProvider.Status.Properties = raw
-	}
-}
diff --git a/pkg/controllers/namespaceresource/keymanagementprovider_controller.go b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
index 8216dd9..ba439a2 100644
--- a/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
+++ b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
@@ -18,24 +18,17 @@ package namespaceresource
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"maps"
 
-	"github.com/ratify-project/ratify/internal/constants"
 	_ "github.com/ratify-project/ratify/pkg/keymanagementprovider/azurekeyvault" // register azure key vault key management provider
 	_ "github.com/ratify-project/ratify/pkg/keymanagementprovider/inline"        // register inline key management provider
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/refresh"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/predicate"
 
 	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
-	cutils "github.com/ratify-project/ratify/pkg/controllers/utils"
-	kmp "github.com/ratify-project/ratify/pkg/keymanagementprovider"
-	"github.com/sirupsen/logrus"
 )
 
 // KeyManagementProviderReconciler reconciles a KeyManagementProvider object
@@ -44,76 +37,33 @@ type KeyManagementProviderReconciler struct {
 	Scheme *runtime.Scheme
 }
 
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/status,verbs=get;update;patch
-// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/finalizers,verbs=update
-func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	logger := logrus.WithContext(ctx)
-
-	var resource = req.NamespacedName.String()
-	var keyManagementProvider configv1beta1.NamespacedKeyManagementProvider
-
-	logger.Infof("reconciling namespaced key management provider '%v'", resource)
-
-	if err := r.Get(ctx, req.NamespacedName, &keyManagementProvider); err != nil {
-		if apierrors.IsNotFound(err) {
-			logger.Infof("deletion detected, removing key management provider %v", resource)
-			kmp.DeleteCertificatesFromMap(resource)
-			kmp.DeleteKeysFromMap(resource)
-		} else {
-			logger.Error(err, "unable to fetch key management provider")
-		}
-
-		return ctrl.Result{}, client.IgnoreNotFound(err)
-	}
-
-	lastFetchedTime := metav1.Now()
-	isFetchSuccessful := false
-
-	// get certificate store list to check if certificate store is configured
-	// TODO: remove check in v2.0.0+
-	var certificateStoreList configv1beta1.CertificateStoreList
-	if err := r.List(ctx, &certificateStoreList); err != nil {
-		logger.Error(err, "unable to list certificate stores")
+func (r *KeyManagementProviderReconciler) ReconcileWithConfig(ctx context.Context, config map[string]interface{}) (ctrl.Result, error) {
+	refresher, err := refresh.CreateRefresherFromConfig(config)
+	if err != nil {
 		return ctrl.Result{}, err
 	}
-	// if certificate store is configured, return error. Only one of certificate store and key management provider can be configured
-	if len(certificateStoreList.Items) > 0 {
-		// Note: for backwards compatibility in upgrade scenarios, Ratify will only log a warning statement.
-		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
-	}
-
-	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	err = refresher.Refresh(ctx)
 	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
 		return ctrl.Result{}, err
 	}
 
-	// fetch certificates and store in map
-	certificates, certAttributes, err := provider.GetCertificates(ctx)
-	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
-		return ctrl.Result{}, fmt.Errorf("Error fetching certificates in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	result, ok := refresher.GetResult().(ctrl.Result)
+	if !ok {
+		return ctrl.Result{}, fmt.Errorf("unexpected type returned from GetResult: %T", refresher.GetResult())
 	}
+	return result, nil
+}
 
-	// fetch keys and store in map
-	keys, keyAttributes, err := provider.GetKeys(ctx)
-	if err != nil {
-		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
-		return ctrl.Result{}, fmt.Errorf("Error fetching keys in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/finalizers,verbs=update
+func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	refresherConfig := map[string]interface{}{
+		"type":    refresh.KubeRefresherNamespacedType,
+		"client":  r.Client,
+		"request": req,
 	}
-	kmp.SetCertificatesInMap(resource, certificates)
-	kmp.SetKeysInMap(resource, keyManagementProvider.Spec.Type, keys)
-	// merge certificates and keys status into one
-	maps.Copy(keyAttributes, certAttributes)
-	isFetchSuccessful = true
-	emptyErrorString := ""
-	writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, keyAttributes)
-
-	logger.Infof("%v certificate(s) & %v key(s) fetched for key management provider %v", len(certificates), len(keys), resource)
-
-	// returning empty result and no error to indicate we’ve successfully reconciled this object
-	return ctrl.Result{}, nil
+	return r.ReconcileWithConfig(ctx, refresherConfig)
 }
 
 // SetupWithManager sets up the controller with the Manager.
@@ -127,46 +77,3 @@ func (r *KeyManagementProviderReconciler) SetupWithManager(mgr ctrl.Manager) err
 		For(&configv1beta1.NamespacedKeyManagementProvider{}).WithEventFilter(pred).
 		Complete(r)
 }
-
-// writeKMProviderStatus updates the status of the key management provider resource
-func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
-	if isSuccess {
-		updateKMProviderSuccessStatus(keyManagementProvider, &operationTime, kmProviderStatus)
-	} else {
-		updateKMProviderErrorStatus(keyManagementProvider, errorString, &operationTime)
-	}
-	if statusErr := r.Status().Update(ctx, keyManagementProvider); statusErr != nil {
-		logger.Error(statusErr, ",unable to update key management provider error status")
-	}
-}
-
-// updateKMProviderErrorStatus updates the key management provider status with error, brief error and last fetched time
-func updateKMProviderErrorStatus(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, errorString string, operationTime *metav1.Time) {
-	// truncate brief error string to maxBriefErrLength
-	briefErr := errorString
-	if len(errorString) > constants.MaxBriefErrLength {
-		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
-	}
-	keyManagementProvider.Status.IsSuccess = false
-	keyManagementProvider.Status.Error = errorString
-	keyManagementProvider.Status.BriefError = briefErr
-	keyManagementProvider.Status.LastFetchedTime = operationTime
-}
-
-// updateKMProviderSuccessStatus updates the key management provider status if status argument is non nil
-// Success status includes last fetched time and other provider-specific properties
-func updateKMProviderSuccessStatus(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, lastOperationTime *metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
-	keyManagementProvider.Status.IsSuccess = true
-	keyManagementProvider.Status.Error = ""
-	keyManagementProvider.Status.BriefError = ""
-	keyManagementProvider.Status.LastFetchedTime = lastOperationTime
-
-	if kmProviderStatus != nil {
-		jsonString, _ := json.Marshal(kmProviderStatus)
-
-		raw := runtime.RawExtension{
-			Raw: jsonString,
-		}
-		keyManagementProvider.Status.Properties = raw
-	}
-}
diff --git a/pkg/executor/core/executor.go b/pkg/executor/core/executor.go
index 2cc90b0..e5d8ec2 100644
--- a/pkg/executor/core/executor.go
+++ b/pkg/executor/core/executor.go
@@ -176,13 +176,8 @@ func (executor Executor) verifyReferenceForJSONPolicy(ctx context.Context, subje
 			verifierStartTime := time.Now()
 			verifyResult, err := verifier.Verify(ctx, subjectRef, referenceDesc, referrerStore)
 			if err != nil {
-				verifyResult = vr.VerifierResult{
-					IsSuccess:    false,
-					Name:         verifier.Name(),
-					Type:         verifier.Type(),
-					VerifierName: verifier.Name(),
-					VerifierType: verifier.Type(),
-					Message:      errors.ErrorCodeVerifyReferenceFailure.NewError(errors.Verifier, verifier.Name(), errors.EmptyLink, err, nil, errors.HideStackTrace).Error()}
+				verifierErr := errors.ErrorCodeVerifyReferenceFailure.NewError(errors.Verifier, verifier.Name(), errors.EmptyLink, err, nil, errors.HideStackTrace)
+				verifyResult = vr.NewVerifierResult("", verifier.Name(), verifier.Type(), "", false, &verifierErr, nil)
 			}
 
 			if len(verifier.GetNestedReferences()) > 0 {
@@ -229,13 +224,8 @@ func (executor Executor) verifyReferenceForRegoPolicy(ctx context.Context, subje
 			verifierStartTime := time.Now()
 			verifierResult, err := verifier.Verify(errCtx, subjectRef, referenceDesc, referrerStore)
 			if err != nil {
-				verifierReport = vt.VerifierResult{
-					IsSuccess:    false,
-					Name:         verifier.Name(), // Deprecating Name in next release, reference to VerifierName instead.
-					Type:         verifier.Type(), // Deprecating Type in next release, reference to VerifierType instead.
-					VerifierName: verifier.Name(),
-					VerifierType: verifier.Type(),
-					Message:      errors.ErrorCodeVerifyReferenceFailure.NewError(errors.Verifier, verifier.Name(), errors.EmptyLink, err, nil, errors.HideStackTrace).Error()}
+				verifierErr := errors.ErrorCodeVerifyReferenceFailure.NewError(errors.Verifier, verifier.Name(), errors.EmptyLink, err, nil, errors.HideStackTrace)
+				verifierReport = vt.CreateVerifierResult(verifier.Name(), verifier.Type(), "", false, &verifierErr)
 			} else {
 				verifierReport = vt.NewVerifierResult(verifierResult)
 			}
diff --git a/pkg/executor/types/types.go b/pkg/executor/types/types.go
index ccf78c3..30bac9a 100644
--- a/pkg/executor/types/types.go
+++ b/pkg/executor/types/types.go
@@ -29,6 +29,7 @@ type VerifyResult struct {
 
 // NestedVerifierReport describes the results of verifying an artifact and its
 // nested artifacts by available verifiers.
+// Note: NestedVerifierReport is used for verification results in v1.
 type NestedVerifierReport struct {
 	Subject         string                 `json:"subject"`
 	ReferenceDigest string                 `json:"referenceDigest"`
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider.go b/pkg/keymanagementprovider/azurekeyvault/provider.go
index f241147..da885af 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider.go
@@ -31,6 +31,7 @@ import (
 	"github.com/go-jose/go-jose/v3"
 	re "github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/internal/logger"
+	"github.com/ratify-project/ratify/internal/version"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/azurekeyvault/types"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/config"
@@ -122,7 +123,7 @@ func (f *akvKMProviderFactory) Create(_ string, keyManagementProviderConfig conf
 
 	logger.GetLogger(context.Background(), logOpt).Debugf("vaultURI %s", provider.vaultURI)
 
-	kvClient, err := initKVClient(context.Background(), provider.cloudEnv.KeyVaultEndpoint, provider.tenantID, provider.clientID)
+	kvClient, err := initKVClient(context.Background(), provider.cloudEnv.KeyVaultEndpoint, provider.tenantID, provider.clientID, version.UserAgent)
 	if err != nil {
 		return nil, re.ErrorCodePluginInitFailure.NewError(re.KeyManagementProvider, ProviderName, re.AKVLink, err, "failed to create keyvault client", re.HideStackTrace)
 	}
@@ -193,6 +194,10 @@ func (s *akvKMProvider) GetKeys(ctx context.Context) (map[keymanagementprovider.
 	return keysMap, getStatusMap(keysStatus, types.KeysStatus), nil
 }
 
+func (s *akvKMProvider) IsRefreshable() bool {
+	return true
+}
+
 // azure keyvault provider certificate/key status is a map from "certificates" key or "keys" key to an array of key management provider status
 func getStatusMap(statusMap []map[string]string, contentType string) keymanagementprovider.KeyManagementProviderStatus {
 	status := keymanagementprovider.KeyManagementProviderStatus{}
@@ -221,18 +226,18 @@ func parseAzureEnvironment(cloudName string) (*azure.Environment, error) {
 	return &env, err
 }
 
-func initializeKvClient(ctx context.Context, keyVaultEndpoint, tenantID, clientID string) (*kv.BaseClient, error) {
+func initializeKvClient(ctx context.Context, keyVaultEndpoint, tenantID, clientID, userAgent string) (*kv.BaseClient, error) {
 	kvClient := kv.New()
 	kvEndpoint := strings.TrimSuffix(keyVaultEndpoint, "/")
 
-	err := kvClient.AddToUserAgent("ratify")
+	err := kvClient.AddToUserAgent(userAgent)
 	if err != nil {
-		return nil, re.ErrorCodeConfigInvalid.NewError(re.KeyManagementProvider, ProviderName, re.AKVLink, err, "failed to add user agent to keyvault client", re.PrintStackTrace)
+		return nil, re.ErrorCodeConfigInvalid.WithDetail("Failed to add user agent to keyvault client.").WithRemediation(re.AKVLink).WithError(err)
 	}
 
 	kvClient.Authorizer, err = getAuthorizerForWorkloadIdentity(ctx, tenantID, clientID, kvEndpoint)
 	if err != nil {
-		return nil, re.ErrorCodeAuthDenied.NewError(re.KeyManagementProvider, ProviderName, re.AKVLink, err, "failed to get authorizer for keyvault client", re.PrintStackTrace)
+		return nil, re.ErrorCodeAuthDenied.WithDetail("failed to get authorizer for keyvault client").WithRemediation(re.AKVLink).WithError(err)
 	}
 	return &kvClient, nil
 }
diff --git a/pkg/keymanagementprovider/inline/provider.go b/pkg/keymanagementprovider/inline/provider.go
index 2bd6b83..81740a7 100644
--- a/pkg/keymanagementprovider/inline/provider.go
+++ b/pkg/keymanagementprovider/inline/provider.go
@@ -114,3 +114,7 @@ func (s *inlineKMProvider) GetCertificates(_ context.Context) (map[keymanagement
 func (s *inlineKMProvider) GetKeys(_ context.Context) (map[keymanagementprovider.KMPMapKey]crypto.PublicKey, keymanagementprovider.KeyManagementProviderStatus, error) {
 	return s.keys, nil, nil
 }
+
+func (s *inlineKMProvider) IsRefreshable() bool {
+	return false
+}
diff --git a/pkg/keymanagementprovider/keymanagementprovider.go b/pkg/keymanagementprovider/keymanagementprovider.go
index 714deda..bf77c2c 100644
--- a/pkg/keymanagementprovider/keymanagementprovider.go
+++ b/pkg/keymanagementprovider/keymanagementprovider.go
@@ -54,6 +54,8 @@ type KeyManagementProvider interface {
 	GetCertificates(ctx context.Context) (map[KMPMapKey][]*x509.Certificate, KeyManagementProviderStatus, error)
 	// Returns an array of keys and the provider specific key attributes
 	GetKeys(ctx context.Context) (map[KMPMapKey]crypto.PublicKey, KeyManagementProviderStatus, error)
+	// Returns if the provider supports refreshing of certificates/keys
+	IsRefreshable() bool
 }
 
 // static concurrency-safe map to store certificates fetched from key management provider
@@ -72,6 +74,18 @@ var certificatesMap sync.Map
 //	 where PublicKey is a struct containing the public key and the provider type
 var keyMap sync.Map
 
+// static concurrency-safe map to store errors while fetching certificates from key management provider.
+// layout:
+//
+//	map["<namespace>/<name>"] = error
+var certificateErrMap sync.Map
+
+// static concurrency-safe map to store errors while fetching keys from key management provider.
+// layout:
+//
+//	map["<namespace>/<name>"] = error
+var keyErrMap sync.Map
+
 // DecodeCertificates decodes PEM-encoded bytes into an x509.Certificate chain.
 func DecodeCertificates(value []byte) ([]*x509.Certificate, error) {
 	var certs []*x509.Certificate
@@ -112,27 +126,34 @@ func DecodeKey(value []byte) (crypto.PublicKey, error) {
 	return pk, nil
 }
 
-// SetCertificatesInMap sets the certificates in the map
+// setCertificatesInMap sets the certificates in the map
 // it is concurrency-safe
-func SetCertificatesInMap(resource string, certs map[KMPMapKey][]*x509.Certificate) {
+func setCertificatesInMap(resource string, certs map[KMPMapKey][]*x509.Certificate) {
 	certificatesMap.Store(resource, certs)
+	certificateErrMap.Delete(resource)
 }
 
 // GetCertificatesFromMap gets the certificates from the map and returns an empty map of certificate arrays if not found or an error happened.
 func GetCertificatesFromMap(ctx context.Context, resource string) (map[KMPMapKey][]*x509.Certificate, error) {
 	if !hasAccessToProvider(ctx, resource) {
-		return map[KMPMapKey][]*x509.Certificate{}, fmt.Errorf("namespace: [%s] does not have access to key management provider: %s", ctxUtils.GetNamespace(ctx), resource)
+		return map[KMPMapKey][]*x509.Certificate{}, errors.ErrorCodeForbidden.WithDetail(fmt.Sprintf("namespace: [%s] does not have access to key management provider: %s", ctxUtils.GetNamespace(ctx), resource)).WithRemediation(fmt.Sprintf("Ensure the key management provider: %s is created under namespace: [%s] or as a cluster-wide resource.", resource, ctxUtils.GetNamespace(ctx)))
+	}
+	if err, ok := certificateErrMap.Load(resource); ok && err != nil {
+		return map[KMPMapKey][]*x509.Certificate{}, err.(error)
 	}
 	if certs, ok := certificatesMap.Load(resource); ok {
 		return certs.(map[KMPMapKey][]*x509.Certificate), nil
 	}
-	return map[KMPMapKey][]*x509.Certificate{}, fmt.Errorf("failed to access non-existent key management provider: %s", resource)
+	return map[KMPMapKey][]*x509.Certificate{}, errors.ErrorCodeNotFound.WithDetail(fmt.Sprintf("failed to access non-existent key management provider: %s", resource))
 }
 
-// DeleteCertificatesFromMap deletes the certificates from the map
+// DeleteResourceFromMap deletes the certificates, keys and errors from the map
 // it is concurrency-safe
-func DeleteCertificatesFromMap(resource string) {
+func DeleteResourceFromMap(resource string) {
 	certificatesMap.Delete(resource)
+	keyMap.Delete(resource)
+	certificateErrMap.Delete(resource)
+	keyErrMap.Delete(resource)
 }
 
 // FlattenKMPMap flattens the map of certificates fetched for a single key management provider resource and returns a single array
@@ -144,31 +165,46 @@ func FlattenKMPMap(certMap map[KMPMapKey][]*x509.Certificate) []*x509.Certificat
 	return items
 }
 
-// SetKeysInMap sets the keys in the map
-func SetKeysInMap(resource string, providerType string, keys map[KMPMapKey]crypto.PublicKey) {
+// setKeysInMap sets the keys in the map
+func setKeysInMap(resource string, providerType string, keys map[KMPMapKey]crypto.PublicKey) {
 	typedMap := make(map[KMPMapKey]PublicKey)
 	for key, value := range keys {
 		typedMap[key] = PublicKey{Key: value, ProviderType: providerType}
 	}
 	keyMap.Store(resource, typedMap)
+	keyErrMap.Delete(resource)
+}
+
+// SaveSecrets saves the keys and certificates in the map.
+func SaveSecrets(resource, providerType string, keys map[KMPMapKey]crypto.PublicKey, certs map[KMPMapKey][]*x509.Certificate) {
+	setKeysInMap(resource, providerType, keys)
+	setCertificatesInMap(resource, certs)
 }
 
 // GetKeysFromMap gets the keys from the map and returns an empty map if not found or an error happened.
 func GetKeysFromMap(ctx context.Context, resource string) (map[KMPMapKey]PublicKey, error) {
-	// A cluster-wide operation can cluster-wide provider
+	// A cluster-wide operation can access cluster-wide provider
 	// A namespaced operation can only fetch the provider in the same namespace or cluster-wide provider.
 	if !hasAccessToProvider(ctx, resource) {
 		return map[KMPMapKey]PublicKey{}, fmt.Errorf("namespace: [%s] does not have access to key management provider: %s", ctxUtils.GetNamespace(ctx), resource)
 	}
+	if err, ok := keyErrMap.Load(resource); ok && err != nil {
+		return map[KMPMapKey]PublicKey{}, err.(error)
+	}
 	if keys, ok := keyMap.Load(resource); ok {
 		return keys.(map[KMPMapKey]PublicKey), nil
 	}
 	return map[KMPMapKey]PublicKey{}, fmt.Errorf("failed to access non-existent key management provider: %s", resource)
 }
 
-// DeleteKeysFromMap deletes the keys from the map
-func DeleteKeysFromMap(resource string) {
-	keyMap.Delete(resource)
+// SetCertificateError sets the error while fetching certificates from key management provider.
+func SetCertificateError(resource string, err error) {
+	certificateErrMap.Store(resource, err)
+}
+
+// SetKeyError sets the error while fetching keys from key management provider.
+func SetKeyError(resource string, err error) {
+	keyErrMap.Store(resource, err)
 }
 
 // A namespaced verification request could access KMP in the same namespace or cluster-wide KMP.
diff --git a/pkg/keymanagementprovider/mocks/client.go b/pkg/keymanagementprovider/mocks/client.go
new file mode 100644
index 0000000..f24c190
--- /dev/null
+++ b/pkg/keymanagementprovider/mocks/client.go
@@ -0,0 +1,32 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package mocks
+
+import (
+	"context"
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type TestClient struct {
+	client.Client
+}
+
+func (m TestClient) Get(_ context.Context, _ types.NamespacedName, _ client.Object, _ ...client.GetOption) error {
+	return fmt.Errorf("error")
+}
diff --git a/pkg/keymanagementprovider/mocks/factory.go b/pkg/keymanagementprovider/mocks/factory.go
new file mode 100644
index 0000000..0230582
--- /dev/null
+++ b/pkg/keymanagementprovider/mocks/factory.go
@@ -0,0 +1,33 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package mocks
+
+import (
+	"crypto"
+	"crypto/x509"
+
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/config"
+)
+
+type TestKeyManagementProviderFactory struct {
+}
+
+func (f *TestKeyManagementProviderFactory) Create(_ string, _ config.KeyManagementProviderConfig, _ string) (keymanagementprovider.KeyManagementProvider, error) {
+	var certMap map[keymanagementprovider.KMPMapKey][]*x509.Certificate
+	var keyMap map[keymanagementprovider.KMPMapKey]crypto.PublicKey
+	return &TestKeyManagementProvider{certificates: certMap, keys: keyMap}, nil
+}
diff --git a/pkg/keymanagementprovider/mocks/types.go b/pkg/keymanagementprovider/mocks/types.go
index 829b08e..246de21 100644
--- a/pkg/keymanagementprovider/mocks/types.go
+++ b/pkg/keymanagementprovider/mocks/types.go
@@ -37,3 +37,7 @@ func (c *TestKeyManagementProvider) GetCertificates(_ context.Context) (map[keym
 func (c *TestKeyManagementProvider) GetKeys(_ context.Context) (map[keymanagementprovider.KMPMapKey]crypto.PublicKey, keymanagementprovider.KeyManagementProviderStatus, error) {
 	return c.keys, c.status, c.err
 }
+
+func (c *TestKeyManagementProvider) IsRefreshable() bool {
+	return true
+}
diff --git a/pkg/keymanagementprovider/refresh/factory.go b/pkg/keymanagementprovider/refresh/factory.go
new file mode 100644
index 0000000..186951c
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/factory.go
@@ -0,0 +1,53 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package refresh
+
+import "fmt"
+
+var refresherFactories = make(map[string]RefresherFactory)
+
+type RefresherFactory interface {
+	// Create creates a new instance of the refresher using the provided configuration
+	Create(config map[string]interface{}) (Refresher, error)
+}
+
+// Refresher is an interface that defines methods to be implemented by a each refresher
+func Register(name string, factory RefresherFactory) {
+	if factory == nil {
+		panic("refresher factory cannot be nil")
+	}
+	_, registered := refresherFactories[name]
+	if registered {
+		panic(fmt.Sprintf("refresher factory named %s already registered", name))
+	}
+	refresherFactories[name] = factory
+}
+
+// CreateRefresherFromConfig creates a new instance of the refresher using the provided configuration
+func CreateRefresherFromConfig(refresherConfig map[string]interface{}) (Refresher, error) {
+	refresherType, ok := refresherConfig["type"].(string)
+	if !ok {
+		return nil, fmt.Errorf("refresher type is not a string")
+	}
+	if !ok || refresherType == "" {
+		return nil, fmt.Errorf("refresher type cannot be empty")
+	}
+	factory, ok := refresherFactories[refresherType]
+	if !ok {
+		return nil, fmt.Errorf("refresher factory with name %s not found", refresherType)
+	}
+	return factory.Create(refresherConfig)
+}
diff --git a/pkg/keymanagementprovider/refresh/kubeRefresh.go b/pkg/keymanagementprovider/refresh/kubeRefresh.go
new file mode 100644
index 0000000..aa06118
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/kubeRefresh.go
@@ -0,0 +1,212 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package refresh
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"maps"
+	"time"
+
+	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
+	"github.com/ratify-project/ratify/internal/constants"
+	cutils "github.com/ratify-project/ratify/pkg/controllers/utils"
+	kmp "github.com/ratify-project/ratify/pkg/keymanagementprovider"
+	"github.com/sirupsen/logrus"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type KubeRefresher struct {
+	client.Client
+	Request ctrl.Request
+	Result  ctrl.Result
+}
+
+// Register registers the kubeRefresher factory
+func init() {
+	Register(KubeRefresherType, &KubeRefresher{})
+}
+
+// Refresh the certificates/keys for the key management provider by calling the GetCertificates and GetKeys methods
+func (kr *KubeRefresher) Refresh(ctx context.Context) error {
+	logger := logrus.WithContext(ctx)
+
+	var resource = kr.Request.Name
+	var keyManagementProvider configv1beta1.KeyManagementProvider
+
+	logger.Infof("reconciling cluster key management provider '%v'", resource)
+
+	if err := kr.Get(ctx, kr.Request.NamespacedName, &keyManagementProvider); err != nil {
+		if apierrors.IsNotFound(err) {
+			logger.Infof("deletion detected, removing key management provider %v", resource)
+			kmp.DeleteResourceFromMap(resource)
+		} else {
+			logger.Error(err, "unable to fetch key management provider")
+		}
+
+		kr.Result = ctrl.Result{}
+
+		return client.IgnoreNotFound(err)
+	}
+
+	lastFetchedTime := metav1.Now()
+	isFetchSuccessful := false
+
+	// get certificate store list to check if certificate store is configured
+	// TODO: remove check in v2.0.0+
+	var certificateStoreList configv1beta1.CertificateStoreList
+	if err := kr.List(ctx, &certificateStoreList); err != nil {
+		logger.Error(err, "unable to list certificate stores")
+		kr.Result = ctrl.Result{}
+		return err
+	}
+
+	if len(certificateStoreList.Items) > 0 {
+		// Note: for backwards compatibility in upgrade scenarios, Ratify will only log a warning statement.
+		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
+	}
+
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	if err != nil {
+		kmp.SetCertificateError(resource, err)
+		kmp.SetKeyError(resource, err)
+		writeKMProviderStatus(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Request = ctrl.Request{}
+		return err
+	}
+
+	// fetch certificates and store in map
+	certificates, certAttributes, err := provider.GetCertificates(ctx)
+	if err != nil {
+		kmp.SetCertificateError(resource, err)
+		writeKMProviderStatus(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Request = ctrl.Request{}
+		return fmt.Errorf("error fetching certificates in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+
+	// fetch keys and store in map
+	keys, keyAttributes, err := provider.GetKeys(ctx)
+	if err != nil {
+		kmp.SetKeyError(resource, err)
+		writeKMProviderStatus(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Request = ctrl.Request{}
+		return fmt.Errorf("error fetching keys in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+	kmp.SaveSecrets(resource, keyManagementProvider.Spec.Type, keys, certificates)
+	// merge certificates and keys status into one
+	maps.Copy(keyAttributes, certAttributes)
+	isFetchSuccessful = true
+	emptyErrorString := ""
+	writeKMProviderStatus(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, keyAttributes)
+
+	logger.Infof("%v certificate(s) & %v key(s) fetched for key management provider %v", len(certificates), len(keys), resource)
+
+	// returning empty result and no error to indicate we’ve successfully reconciled this object
+	// will not reconcile again unless resource is recreated
+	if !provider.IsRefreshable() {
+		kr.Request = ctrl.Request{}
+		return nil
+	}
+
+	// if interval is not set, disable refresh
+	if keyManagementProvider.Spec.RefreshInterval == "" {
+		kr.Result = ctrl.Result{}
+		return nil
+	}
+	// resource is refreshable, requeue after interval
+	intervalDuration, err := time.ParseDuration(keyManagementProvider.Spec.RefreshInterval)
+	if err != nil {
+		logger.Error(err, "unable to parse interval duration")
+		kr.Result = ctrl.Result{}
+		return err
+	}
+
+	logger.Info("Reconciled KeyManagementProvider", "intervalDuration", intervalDuration)
+	kr.Result = ctrl.Result{RequeueAfter: intervalDuration}
+
+	return nil
+}
+
+// GetResult returns the result of the refresh as a ctrl.Result
+func (kr *KubeRefresher) GetResult() interface{} {
+	return kr.Result
+}
+
+// Create creates a new KubeRefresher instance
+func (kr *KubeRefresher) Create(config map[string]interface{}) (Refresher, error) {
+	client, ok := config["client"].(client.Client)
+	if !ok {
+		return nil, fmt.Errorf("client is required in config")
+	}
+
+	request, ok := config["request"].(ctrl.Request)
+	if !ok {
+		return nil, fmt.Errorf("request is required in config")
+	}
+
+	return &KubeRefresher{
+		Client:  client,
+		Request: request,
+	}, nil
+}
+
+func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.KeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	if isSuccess {
+		updateKMProviderSuccessStatus(keyManagementProvider, &operationTime, kmProviderStatus)
+	} else {
+		updateKMProviderErrorStatus(keyManagementProvider, errorString, &operationTime)
+	}
+	if statusErr := r.Status().Update(ctx, keyManagementProvider); statusErr != nil {
+		logger.Error(statusErr, ",unable to update key management provider error status")
+	}
+}
+
+// updateKMProviderErrorStatus updates the key management provider status with error, brief error and last fetched time
+func updateKMProviderErrorStatus(keyManagementProvider *configv1beta1.KeyManagementProvider, errorString string, operationTime *metav1.Time) {
+	// truncate brief error string to maxBriefErrLength
+	briefErr := errorString
+	if len(errorString) > constants.MaxBriefErrLength {
+		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
+	}
+	keyManagementProvider.Status.IsSuccess = false
+	keyManagementProvider.Status.Error = errorString
+	keyManagementProvider.Status.BriefError = briefErr
+	keyManagementProvider.Status.LastFetchedTime = operationTime
+}
+
+// updateKMProviderSuccessStatus updates the key management provider status if status argument is non nil
+// Success status includes last fetched time and other provider-specific properties
+func updateKMProviderSuccessStatus(keyManagementProvider *configv1beta1.KeyManagementProvider, lastOperationTime *metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	keyManagementProvider.Status.IsSuccess = true
+	keyManagementProvider.Status.Error = ""
+	keyManagementProvider.Status.BriefError = ""
+	keyManagementProvider.Status.LastFetchedTime = lastOperationTime
+
+	if kmProviderStatus != nil {
+		jsonString, _ := json.Marshal(kmProviderStatus)
+
+		raw := runtime.RawExtension{
+			Raw: jsonString,
+		}
+		keyManagementProvider.Status.Properties = raw
+	}
+}
diff --git a/pkg/keymanagementprovider/refresh/kubeRefreshNamespaced.go b/pkg/keymanagementprovider/refresh/kubeRefreshNamespaced.go
new file mode 100644
index 0000000..9343250
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/kubeRefreshNamespaced.go
@@ -0,0 +1,210 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package refresh
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"maps"
+	"time"
+
+	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
+	"github.com/ratify-project/ratify/internal/constants"
+	cutils "github.com/ratify-project/ratify/pkg/controllers/utils"
+	kmp "github.com/ratify-project/ratify/pkg/keymanagementprovider"
+	"github.com/sirupsen/logrus"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type KubeRefresherNamespaced struct {
+	client.Client
+	Request ctrl.Request
+	Result  ctrl.Result
+}
+
+// Register registers the kubeRefresherNamespaced factory
+func init() {
+	Register(KubeRefresherNamespacedType, &KubeRefresherNamespaced{})
+}
+
+// Refresh the certificates/keys for the key management provider by calling the GetCertificates and GetKeys methods
+func (kr *KubeRefresherNamespaced) Refresh(ctx context.Context) error {
+	logger := logrus.WithContext(ctx)
+
+	var resource = kr.Request.NamespacedName.String()
+	var keyManagementProvider configv1beta1.NamespacedKeyManagementProvider
+
+	logger.Infof("reconciling namespaced key management provider '%v'", resource)
+
+	if err := kr.Get(ctx, kr.Request.NamespacedName, &keyManagementProvider); err != nil {
+		if apierrors.IsNotFound(err) {
+			logger.Infof("deletion detected, removing key management provider %v", resource)
+			kmp.DeleteResourceFromMap(resource)
+		} else {
+			logger.Error(err, "unable to fetch key management provider")
+		}
+
+		kr.Result = ctrl.Result{}
+
+		return client.IgnoreNotFound(err)
+	}
+
+	lastFetchedTime := metav1.Now()
+	isFetchSuccessful := false
+
+	// get certificate store list to check if certificate store is configured
+	// TODO: remove check in v2.0.0+
+	var certificateStoreList configv1beta1.CertificateStoreList
+	if err := kr.List(ctx, &certificateStoreList); err != nil {
+		logger.Error(err, "unable to list certificate stores")
+		kr.Result = ctrl.Result{}
+		return err
+	}
+	// if certificate store is configured, return error. Only one of certificate store and key management provider can be configured
+	if len(certificateStoreList.Items) > 0 {
+		// Note: for backwards compatibility in upgrade scenarios, Ratify will only log a warning statement.
+		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
+	}
+
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	if err != nil {
+		kmp.SetCertificateError(resource, err)
+		kmp.SetKeyError(resource, err)
+		writeKMProviderStatusNamespaced(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Result = ctrl.Result{}
+		return err
+	}
+
+	// fetch certificates and store in map
+	certificates, certAttributes, err := provider.GetCertificates(ctx)
+	if err != nil {
+		kmp.SetCertificateError(resource, err)
+		writeKMProviderStatusNamespaced(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Result = ctrl.Result{}
+		return fmt.Errorf("error fetching certificates in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+
+	// fetch keys and store in map
+	keys, keyAttributes, err := provider.GetKeys(ctx)
+	if err != nil {
+		kmp.SetKeyError(resource, err)
+		writeKMProviderStatusNamespaced(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		kr.Result = ctrl.Result{}
+		return fmt.Errorf("error fetching keys in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+	kmp.SaveSecrets(resource, keyManagementProvider.Spec.Type, keys, certificates)
+	// merge certificates and keys status into one
+	maps.Copy(keyAttributes, certAttributes)
+	isFetchSuccessful = true
+	emptyErrorString := ""
+	writeKMProviderStatusNamespaced(ctx, kr, &keyManagementProvider, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, keyAttributes)
+
+	logger.Infof("%v certificate(s) & %v key(s) fetched for key management provider %v", len(certificates), len(keys), resource)
+
+	if !provider.IsRefreshable() {
+		kr.Result = ctrl.Result{}
+		return nil
+	}
+
+	// if interval is not set, disable refresh
+	if keyManagementProvider.Spec.RefreshInterval == "" {
+		kr.Result = ctrl.Result{}
+		return nil
+	}
+
+	intervalDuration, err := time.ParseDuration(keyManagementProvider.Spec.RefreshInterval)
+	if err != nil {
+		logger.Error(err, "unable to parse interval duration")
+		kr.Result = ctrl.Result{}
+		return err
+	}
+
+	logger.Info("Reconciled KeyManagementProvider", "intervalDuration", intervalDuration)
+	kr.Result = ctrl.Result{RequeueAfter: intervalDuration}
+
+	return nil
+}
+
+// GetResult returns the result of the refresh as ctrl.Result
+func (kr *KubeRefresherNamespaced) GetResult() interface{} {
+	return kr.Result
+}
+
+// Create creates a new instance of KubeRefresherNamespaced
+func (kr *KubeRefresherNamespaced) Create(config map[string]interface{}) (Refresher, error) {
+	client, ok := config["client"].(client.Client)
+	if !ok {
+		return nil, fmt.Errorf("client is required in config")
+	}
+
+	request, ok := config["request"].(ctrl.Request)
+	if !ok {
+		return nil, fmt.Errorf("request is required in config")
+	}
+
+	return &KubeRefresherNamespaced{
+		Client:  client,
+		Request: request,
+	}, nil
+}
+
+// writeKMProviderStatus updates the status of the key management provider resource
+func writeKMProviderStatusNamespaced(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	if isSuccess {
+		updateKMProviderSuccessStatusNamespaced(keyManagementProvider, &operationTime, kmProviderStatus)
+	} else {
+		updateKMProviderErrorStatusNamespaced(keyManagementProvider, errorString, &operationTime)
+	}
+	if statusErr := r.Status().Update(ctx, keyManagementProvider); statusErr != nil {
+		logger.Error(statusErr, ",unable to update key management provider error status")
+	}
+}
+
+// updateKMProviderErrorStatus updates the key management provider status with error, brief error and last fetched time
+func updateKMProviderErrorStatusNamespaced(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, errorString string, operationTime *metav1.Time) {
+	// truncate brief error string to maxBriefErrLength
+	briefErr := errorString
+	if len(errorString) > constants.MaxBriefErrLength {
+		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
+	}
+	keyManagementProvider.Status.IsSuccess = false
+	keyManagementProvider.Status.Error = errorString
+	keyManagementProvider.Status.BriefError = briefErr
+	keyManagementProvider.Status.LastFetchedTime = operationTime
+}
+
+// updateKMProviderSuccessStatus updates the key management provider status if status argument is non nil
+// Success status includes last fetched time and other provider-specific properties
+func updateKMProviderSuccessStatusNamespaced(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, lastOperationTime *metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	keyManagementProvider.Status.IsSuccess = true
+	keyManagementProvider.Status.Error = ""
+	keyManagementProvider.Status.BriefError = ""
+	keyManagementProvider.Status.LastFetchedTime = lastOperationTime
+
+	if kmProviderStatus != nil {
+		jsonString, _ := json.Marshal(kmProviderStatus)
+
+		raw := runtime.RawExtension{
+			Raw: jsonString,
+		}
+		keyManagementProvider.Status.Properties = raw
+	}
+}
diff --git a/pkg/keymanagementprovider/refresh/refresh.go b/pkg/keymanagementprovider/refresh/refresh.go
new file mode 100644
index 0000000..78e32b1
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/refresh.go
@@ -0,0 +1,34 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package refresh
+
+import (
+	"context"
+)
+
+const (
+	KubeRefresherType           = "kubeRefresher"
+	KubeRefresherNamespacedType = "kubeRefresherNamespaced"
+)
+
+// Refresher is an interface that defines methods to be implemented by a each refresher
+type Refresher interface {
+	// Refresh is a method that refreshes the certificates/keys
+	Refresh(ctx context.Context) error
+	// GetResult is a method that returns the result of the refresh
+	GetResult() interface{}
+}
diff --git a/pkg/policyprovider/configpolicy/configpolicy.go b/pkg/policyprovider/configpolicy/configpolicy.go
index a0475bb..2ab93ca 100644
--- a/pkg/policyprovider/configpolicy/configpolicy.go
+++ b/pkg/policyprovider/configpolicy/configpolicy.go
@@ -97,11 +97,8 @@ func (enforcer PolicyEnforcer) ContinueVerifyOnFailure(_ context.Context, _ comm
 
 // ErrorToVerifyResult converts an error to a properly formatted verify result
 func (enforcer PolicyEnforcer) ErrorToVerifyResult(_ context.Context, subjectRefString string, verifyError error) types.VerifyResult {
-	errorReport := verifier.VerifierResult{
-		Subject:   subjectRefString,
-		IsSuccess: false,
-		Message:   fmt.Sprintf("verification failed: %v", verifyError),
-	}
+	verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail(fmt.Sprintf("failed to verify artifact: %s", subjectRefString)).WithError(verifyError)
+	errorReport := verifier.NewVerifierResult(subjectRefString, "", "", "", false, &verifierErr, nil)
 	var reports []interface{}
 	reports = append(reports, errorReport)
 	return types.VerifyResult{IsSuccess: false, VerifierReports: reports}
diff --git a/pkg/referrerstore/oras/mocks/oras_storage.go b/pkg/referrerstore/oras/mocks/oras_storage.go
index a02e6cc..b2bffa4 100644
--- a/pkg/referrerstore/oras/mocks/oras_storage.go
+++ b/pkg/referrerstore/oras/mocks/oras_storage.go
@@ -28,9 +28,14 @@ import (
 type TestStorage struct {
 	content.Storage
 	ExistsMap map[digest.Digest]io.Reader
+	ExistsErr error
+	FetchErr  error
 }
 
 func (s TestStorage) Exists(_ context.Context, target oci.Descriptor) (bool, error) {
+	if s.ExistsErr != nil {
+		return false, s.ExistsErr
+	}
 	if _, ok := s.ExistsMap[target.Digest]; ok {
 		return true, nil
 	}
@@ -43,6 +48,9 @@ func (s TestStorage) Push(_ context.Context, expected oci.Descriptor, content io
 }
 
 func (s TestStorage) Fetch(_ context.Context, target oci.Descriptor) (io.ReadCloser, error) {
+	if s.FetchErr != nil {
+		return nil, s.FetchErr
+	}
 	if reader, ok := s.ExistsMap[target.Digest]; ok {
 		return io.NopCloser(reader), nil
 	}
diff --git a/pkg/referrerstore/oras/oras.go b/pkg/referrerstore/oras/oras.go
index 5a4942b..ef11164 100644
--- a/pkg/referrerstore/oras/oras.go
+++ b/pkg/referrerstore/oras/oras.go
@@ -305,10 +305,18 @@ func (store *orasStore) GetReferenceManifest(ctx context.Context, subjectReferen
 	// check if manifest exists in local ORAS cache
 	isCached, err := store.localCache.Exists(ctx, referenceDesc.Descriptor)
 	if err != nil {
-		return ocispecs.ReferenceManifest{}, err
+		logger.GetLogger(ctx, logOpt).Warnf("failed to check if manifest [%s] exists in cache: %v", referenceDesc.Descriptor.Digest, err)
 	}
 	metrics.ReportBlobCacheCount(ctx, isCached)
 
+	if isCached {
+		manifestBytes, err = store.getRawContentFromCache(ctx, referenceDesc.Descriptor)
+		if err != nil {
+			isCached = false
+			logger.GetLogger(ctx, logOpt).Warnf("failed to get manifest [%s] from cache: %v", referenceDesc.Descriptor.Digest, err)
+		}
+	}
+
 	if !isCached {
 		// fetch manifest content from repository
 		manifestReader, err := repository.Fetch(ctx, referenceDesc.Descriptor)
@@ -326,12 +334,7 @@ func (store *orasStore) GetReferenceManifest(ctx context.Context, subjectReferen
 		orasExistsExpectedError := fmt.Errorf("%s: %s: %w", referenceDesc.Descriptor.Digest, referenceDesc.Descriptor.MediaType, errdef.ErrAlreadyExists)
 		err = store.localCache.Push(ctx, referenceDesc.Descriptor, bytes.NewReader(manifestBytes))
 		if err != nil && err.Error() != orasExistsExpectedError.Error() {
-			return ocispecs.ReferenceManifest{}, err
-		}
-	} else {
-		manifestBytes, err = store.getRawContentFromCache(ctx, referenceDesc.Descriptor)
-		if err != nil {
-			return ocispecs.ReferenceManifest{}, err
+			logger.GetLogger(ctx, logOpt).Warnf("failed to save manifest [%s] in cache: %v", referenceDesc.Descriptor.Digest, err)
 		}
 	}
 
diff --git a/pkg/verifier/api.go b/pkg/verifier/api.go
index 8f96955..d242466 100644
--- a/pkg/verifier/api.go
+++ b/pkg/verifier/api.go
@@ -23,23 +23,6 @@ import (
 	"github.com/ratify-project/ratify/pkg/referrerstore"
 )
 
-// VerifierResult describes the result of verifying a reference manifest for a subject
-type VerifierResult struct { //nolint:revive // ignore linter to have unique type name
-	Subject         string           `json:"subject,omitempty"`
-	IsSuccess       bool             `json:"isSuccess"`
-	Name            string           `json:"name,omitempty"`
-	VerifierName    string           `json:"verifierName,omitempty"`
-	Type            string           `json:"type,omitempty"`
-	VerifierType    string           `json:"verifierType,omitempty"`
-	Message         string           `json:"message,omitempty"`
-	ErrorReason     string           `json:"errorReason,omitempty"`
-	Extensions      interface{}      `json:"extensions,omitempty"`
-	NestedResults   []VerifierResult `json:"nestedResults,omitempty"`
-	ArtifactType    string           `json:"artifactType,omitempty"`
-	ReferenceDigest string           `json:"referenceDigest,omitempty"`
-	Remediation     string           `json:"remediation,omitempty"`
-}
-
 // ReferenceVerifier is an interface that defines methods to verify a reference
 // for a subject by a verifier.
 type ReferenceVerifier interface {
diff --git a/pkg/verifier/cosign/cosign.go b/pkg/verifier/cosign/cosign.go
index a5a3bc4..fb3efd7 100644
--- a/pkg/verifier/cosign/cosign.go
+++ b/pkg/verifier/cosign/cosign.go
@@ -284,15 +284,15 @@ func (v *cosignVerifier) verifyInternal(ctx context.Context, subjectReference co
 	}
 
 	if hasValidSignature {
-		return verifier.VerifierResult{
-			Name:         v.name,
-			Type:         v.verifierType,
-			VerifierName: v.name,
-			VerifierType: v.verifierType,
-			IsSuccess:    true,
-			Message:      "Verification success. Valid signatures found. Please refer to extensions field for verifications performed.",
-			Extensions:   Extension{SignatureExtension: sigExtensions, TrustPolicy: trustPolicy.GetName()},
-		}, nil
+		return verifier.NewVerifierResult(
+			"",
+			v.name,
+			v.verifierType,
+			"Verification success. Valid signatures found. Please refer to extensions field for verifications performed.",
+			true,
+			nil,
+			Extension{SignatureExtension: sigExtensions, TrustPolicy: trustPolicy.GetName()},
+		), nil
 	}
 
 	errorResult := errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("no valid signatures found"))
@@ -397,15 +397,15 @@ func (v *cosignVerifier) verifyLegacy(ctx context.Context, subjectReference comm
 	}
 
 	if len(signatures) > 0 {
-		return verifier.VerifierResult{
-			Name:         v.name,
-			Type:         v.verifierType,
-			VerifierName: v.name,
-			VerifierType: v.verifierType,
-			IsSuccess:    true,
-			Message:      "Verification success. Valid signatures found",
-			Extensions:   LegacyExtension{SignatureExtension: sigExtensions},
-		}, nil
+		return verifier.NewVerifierResult(
+			"",
+			v.name,
+			v.verifierType,
+			"Verification success. Valid signatures found",
+			true,
+			nil,
+			LegacyExtension{SignatureExtension: sigExtensions},
+		), nil
 	}
 
 	errorResult := errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("no valid signatures found"))
@@ -485,15 +485,16 @@ func staticLayerOpts(desc imgspec.Descriptor) ([]static.Option, error) {
 
 // ErrorToVerifyResult returns a verifier result with the error message and isSuccess set to false
 func errorToVerifyResult(name string, verifierType string, err error) verifier.VerifierResult {
-	return verifier.VerifierResult{
-		IsSuccess:    false,
-		Name:         name,
-		Type:         verifierType,
-		VerifierName: name,
-		VerifierType: verifierType,
-		Message:      "Verification failed",
-		ErrorReason:  err.Error(),
-	}
+	verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail("Verification failed").WithError(err)
+	return verifier.NewVerifierResult(
+		"",
+		name,
+		verifierType,
+		"",
+		false,
+		&verifierErr,
+		nil,
+	)
 }
 
 // decodeASN1Signature decodes the ASN.1 signature to raw signature bytes
diff --git a/pkg/verifier/notation/notation.go b/pkg/verifier/notation/notation.go
index 1a43493..f9bc58d 100644
--- a/pkg/verifier/notation/notation.go
+++ b/pkg/verifier/notation/notation.go
@@ -150,39 +150,30 @@ func (v *notationPluginVerifier) Verify(ctx context.Context,
 		return verifier.VerifierResult{IsSuccess: false}, re.ErrorCodeGetReferenceManifestFailure.NewError(re.ReferrerStore, store.Name(), re.EmptyLink, err, fmt.Sprintf("failed to resolve reference manifest: %+v", referenceDescriptor), re.HideStackTrace)
 	}
 
-	if len(referenceManifest.Blobs) == 0 {
-		return verifier.VerifierResult{IsSuccess: false}, re.ErrorCodeSignatureNotFound.NewError(re.Verifier, v.name, re.EmptyLink, nil, fmt.Sprintf("no signature content found for referrer: %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()), re.HideStackTrace)
+	if len(referenceManifest.Blobs) != 1 {
+		return verifier.VerifierResult{IsSuccess: false}, re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Notation signature manifest requires exactly one signature envelope blob, got %d", len(referenceManifest.Blobs))).WithRemediation(fmt.Sprintf("Please inspect the artifact [%s@%s] is correctly signed by Notation signer", subjectReference.Path, referenceDescriptor.Digest.String()))
 	}
 
-	for _, blobDesc := range referenceManifest.Blobs {
-		refBlob, err := store.GetBlobContent(ctx, subjectReference, blobDesc.Digest)
-		if err != nil {
-			return verifier.VerifierResult{IsSuccess: false}, re.ErrorCodeGetBlobContentFailure.NewError(re.ReferrerStore, store.Name(), re.EmptyLink, err, fmt.Sprintf("failed to get blob content of digest: %s", blobDesc.Digest), re.HideStackTrace)
-		}
-
-		// TODO: notation verify API only accepts digested reference now.
-		// Pass in tagged reference instead once notation-go supports it.
-		subjectRef := fmt.Sprintf("%s@%s", subjectReference.Path, subjectReference.Digest.String())
-		outcome, err := v.verifySignature(ctx, subjectRef, blobDesc.MediaType, subjectDesc.Descriptor, refBlob)
-		if err != nil {
-			return verifier.VerifierResult{IsSuccess: false, Extensions: extensions}, re.ErrorCodeVerifyPluginFailure.NewError(re.Verifier, v.name, re.NotationTsgLink, err, "failed to verify signature of digest", re.HideStackTrace)
-		}
+	blobDesc := referenceManifest.Blobs[0]
+	refBlob, err := store.GetBlobContent(ctx, subjectReference, blobDesc.Digest)
+	if err != nil {
+		return verifier.VerifierResult{IsSuccess: false}, re.ErrorCodeGetBlobContentFailure.NewError(re.ReferrerStore, store.Name(), re.EmptyLink, err, fmt.Sprintf("failed to get blob content of digest: %s", blobDesc.Digest), re.HideStackTrace)
+	}
 
-		// Note: notation verifier already validates certificate chain is not empty.
-		cert := outcome.EnvelopeContent.SignerInfo.CertificateChain[0]
-		extensions["Issuer"] = cert.Issuer.String()
-		extensions["SN"] = cert.Subject.String()
+	// TODO: notation verify API only accepts digested reference now.
+	// Pass in tagged reference instead once notation-go supports it.
+	subjectRef := fmt.Sprintf("%s@%s", subjectReference.Path, subjectReference.Digest.String())
+	outcome, err := v.verifySignature(ctx, subjectRef, blobDesc.MediaType, subjectDesc.Descriptor, refBlob)
+	if err != nil {
+		return verifier.VerifierResult{IsSuccess: false, Extensions: extensions}, re.ErrorCodeVerifyPluginFailure.NewError(re.Verifier, v.name, re.NotationTsgLink, err, "failed to verify signature of digest", re.HideStackTrace)
 	}
 
-	return verifier.VerifierResult{
-		Name:         v.name,
-		Type:         v.verifierType,
-		VerifierName: v.name,
-		VerifierType: v.verifierType,
-		IsSuccess:    true,
-		Message:      "Signature verification success",
-		Extensions:   extensions,
-	}, nil
+	// Note: notation verifier already validates certificate chain is not empty.
+	cert := outcome.EnvelopeContent.SignerInfo.CertificateChain[0]
+	extensions["Issuer"] = cert.Issuer.String()
+	extensions["SN"] = cert.Subject.String()
+
+	return verifier.NewVerifierResult("", v.name, v.verifierType, "Signature verification success", true, nil, extensions), nil
 }
 
 func getVerifierService(conf *NotationPluginVerifierConfig, pluginDirectory string) (notation.Verifier, error) {
diff --git a/pkg/verifier/notation/truststore.go b/pkg/verifier/notation/truststore.go
index cb71ac6..c7bceb4 100644
--- a/pkg/verifier/notation/truststore.go
+++ b/pkg/verifier/notation/truststore.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 
 	"github.com/notaryproject/notation-go/verifier/truststore"
+	re "github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/internal/logger"
 	"github.com/ratify-project/ratify/pkg/controllers"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
@@ -69,28 +70,32 @@ func (s *trustStore) getCertificatesInternal(ctx context.Context, storeType trus
 	if len(certGroup) > 0 {
 		for _, certStore := range certGroup {
 			logger.GetLogger(ctx, logOpt).Debugf("truststore getting certStore %v", certStore)
-			certMap, err := keymanagementprovider.GetCertificatesFromMap(ctx, certStore)
-			if err != nil {
-				logger.GetLogger(ctx, logOpt).Warnf("unable to fetch certificates for Key Management Provider %+v: %v", certStore, err)
+			certMap, kmpErr := keymanagementprovider.GetCertificatesFromMap(ctx, certStore)
+			if kmpErr != nil {
+				logger.GetLogger(ctx, logOpt).Warnf("unable to fetch certificates for Key Management Provider %+v: %v", certStore, kmpErr)
 			}
 			result := keymanagementprovider.FlattenKMPMap(certMap)
+			var certStoreErr error
 			// notation verifier does not consider specific named/versioned certificates within a key management provider resource
 			if len(result) == 0 {
 				logger.GetLogger(ctx, logOpt).Warnf("no certificate fetched for Key Management Provider %+v", certStore)
 				// check certificate store if key management provider does not have certificates.
 				// NOTE: certificate store and key management provider should not be configured together.
 				// User will be warned by the controller/CLI
-				if result, err = controllers.NamespacedCertStores.GetCertsFromStore(ctx, certStore); err != nil {
-					logger.GetLogger(ctx, logOpt).Warnf("unable to fetch certificates for Certificate Store %+v: %v", certStore, err)
+				if result, certStoreErr = controllers.NamespacedCertStores.GetCertsFromStore(ctx, certStore); certStoreErr != nil {
+					logger.GetLogger(ctx, logOpt).Warnf("unable to fetch certificates for Certificate Store %+v: %v", certStore, certStoreErr)
 				}
 				if len(result) == 0 {
 					logger.GetLogger(ctx, logOpt).Warnf("no certificate fetched for Certificate Store %+v", certStore)
 				}
 			}
+			if err := parseErrFromKmpAndCertStore(kmpErr, certStoreErr); err != nil {
+				return []*x509.Certificate{}, re.ErrorCodeCertInvalid.WithError(err).WithDetail(fmt.Sprintf("unable to fetch certificates from Key Management Provider and Certificate Store: %s", certStore))
+			}
 			certs = append(certs, result...)
 		}
 		if len(certs) == 0 {
-			return certs, fmt.Errorf("unable to fetch certificates for namedStore: %+v", namedStore)
+			return certs, fmt.Errorf("no certificates fetched in namedStore: %+v", namedStore)
 		}
 	} else {
 		for _, path := range s.certPaths {
@@ -122,3 +127,33 @@ func (s *trustStore) filterValidCerts(certs []*x509.Certificate) ([]*x509.Certif
 	}
 	return filteredCerts, nil
 }
+
+// parseErrFromKmpAndCertStore prioritizes and returns the appropriate error from either KMP or CertStore.
+// If the certStoreErr is a reconcile error while kmpErr is not, it returns certStoreErr,
+// otherwise it returns kmpErr.
+// A reconcile error occurs during CR reconciliation, indicating that the resource
+// was applied by users.
+// Since key management provider and certificate store are mutually exclusive,
+// a reconcile error will only originate from one of them.
+// Consequently, the reconcile error from one resource takes precedence over
+// errors from the other.
+// Note: this method should be deleted once certificate store is completely removed.
+func parseErrFromKmpAndCertStore(kmpErr, certStoreErr error) error {
+	if kmpErr == nil || certStoreErr == nil {
+		return nil
+	}
+	if isReconcileError(certStoreErr) && !isReconcileError(kmpErr) {
+		return certStoreErr
+	}
+	return kmpErr
+}
+
+// isReconcileError checks if the error is a reconcile error.
+// If the error is not NotFound or Forbidden, it is considered as an error from KMP/CertStore reconciliation.
+func isReconcileError(err error) bool {
+	ratifyErr := &re.Error{}
+	if errors.As(err, ratifyErr) {
+		return ratifyErr.ErrorCode() != re.ErrorCodeNotFound && ratifyErr.ErrorCode() != re.ErrorCodeForbidden
+	}
+	return true
+}
diff --git a/pkg/verifier/result.go b/pkg/verifier/result.go
new file mode 100644
index 0000000..dff28e9
--- /dev/null
+++ b/pkg/verifier/result.go
@@ -0,0 +1,62 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package verifier
+
+import "github.com/ratify-project/ratify/errors"
+
+// VerifierResult describes the result of verifying a reference manifest for a subject.
+// Note: This struct is used to represent the result of verification in v0.
+type VerifierResult struct { //nolint:revive // ignore linter to have unique type name
+	Subject   string `json:"subject,omitempty"`
+	IsSuccess bool   `json:"isSuccess"`
+	// Name will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707
+	Name         string `json:"name,omitempty"`
+	VerifierName string `json:"verifierName,omitempty"`
+	// Type will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707
+	Type            string           `json:"type,omitempty"`
+	VerifierType    string           `json:"verifierType,omitempty"`
+	ReferenceDigest string           `json:"referenceDigest,omitempty"`
+	ArtifactType    string           `json:"artifactType,omitempty"`
+	Message         string           `json:"message,omitempty"`
+	ErrorReason     string           `json:"errorReason,omitempty"`
+	Remediation     string           `json:"remediation,omitempty"`
+	Extensions      interface{}      `json:"extensions,omitempty"`
+	NestedResults   []VerifierResult `json:"nestedResults,omitempty"`
+}
+
+// NewVerifierResult creates a new VerifierResult object with the given parameters.
+func NewVerifierResult(subject, verifierName, verifierType, message string, isSuccess bool, err *errors.Error, extensions interface{}) VerifierResult {
+	var errorReason, remediation string
+	if err != nil {
+		if err.GetDetail() != "" {
+			message = err.GetDetail()
+		}
+		errorReason = err.GetErrorReason()
+		remediation = err.GetRemediation()
+	}
+	return VerifierResult{
+		Subject:      subject,
+		IsSuccess:    isSuccess,
+		Name:         verifierName,
+		Type:         verifierType,
+		VerifierName: verifierName,
+		VerifierType: verifierType,
+		Message:      message,
+		ErrorReason:  errorReason,
+		Remediation:  remediation,
+		Extensions:   extensions,
+	}
+}
diff --git a/pkg/verifier/types/types.go b/pkg/verifier/types/types.go
index 2eed0a6..d21e9b0 100644
--- a/pkg/verifier/types/types.go
+++ b/pkg/verifier/types/types.go
@@ -19,6 +19,7 @@ import (
 	"encoding/json"
 	"io"
 
+	"github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/pkg/verifier"
 )
 
@@ -47,11 +48,14 @@ const (
 
 // VerifierResult describes the verification result returned from the verifier plugin
 type VerifierResult struct {
-	IsSuccess    bool        `json:"isSuccess"`
-	Message      string      `json:"message"`
-	ErrorReason  string      `json:"errorReason,omitempty"`
-	Name         string      `json:"name"`
-	VerifierName string      `json:"verifierName,omitempty"`
+	IsSuccess   bool   `json:"isSuccess"`
+	Message     string `json:"message"`
+	ErrorReason string `json:"errorReason,omitempty"`
+	Remediation string `json:"remediation,omitempty"`
+	// Name will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707
+	Name         string `json:"name"`
+	VerifierName string `json:"verifierName,omitempty"`
+	// Type will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707
 	Type         string      `json:"type,omitempty"`
 	VerifierType string      `json:"verifierType,omitempty"`
 	Extensions   interface{} `json:"extensions"`
@@ -79,6 +83,30 @@ func WriteVerifyResultResult(result *verifier.VerifierResult, w io.Writer) error
 	return json.NewEncoder(w).Encode(result)
 }
 
+// CreateVerifierResult creates a new verifier result object from given input.
+func CreateVerifierResult(verifierName, verifierType, message string, isSuccess bool, err *errors.Error) VerifierResult {
+	var errorReason string
+	var remediation string
+	if err != nil {
+		if err.GetDetail() != "" {
+			message = err.GetDetail()
+		}
+		errorReason = err.GetErrorReason()
+		remediation = err.GetRemediation()
+	}
+
+	return VerifierResult{
+		IsSuccess:    isSuccess,
+		Name:         verifierName,
+		Type:         verifierType,
+		VerifierName: verifierName,
+		VerifierType: verifierType,
+		Message:      message,
+		ErrorReason:  errorReason,
+		Remediation:  remediation,
+	}
+}
+
 // NewVerifierResult creates a new verifier result object from the given
 // verifier.VerifierResult.
 func NewVerifierResult(result verifier.VerifierResult) VerifierResult {
@@ -90,5 +118,7 @@ func NewVerifierResult(result verifier.VerifierResult) VerifierResult {
 		VerifierName: result.VerifierName,
 		VerifierType: result.VerifierType,
 		Extensions:   result.Extensions,
+		ErrorReason:  result.ErrorReason,
+		Remediation:  result.Remediation,
 	}
 }
diff --git a/plugins/verifier/sbom/sbom.go b/plugins/verifier/sbom/sbom.go
index cd8448d..70a18ad 100644
--- a/plugins/verifier/sbom/sbom.go
+++ b/plugins/verifier/sbom/sbom.go
@@ -22,12 +22,14 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/pkg/common"
 	"github.com/ratify-project/ratify/pkg/ocispecs"
 	"github.com/ratify-project/ratify/pkg/referrerstore"
 	"github.com/ratify-project/ratify/plugins/verifier/sbom/utils"
 
 	// This import is required to utilize the oras built-in referrer store
+	re "github.com/ratify-project/ratify/errors"
 	_ "github.com/ratify-project/ratify/pkg/referrerstore/oras"
 	"github.com/ratify-project/ratify/pkg/verifier"
 	"github.com/ratify-project/ratify/pkg/verifier/plugin/skel"
@@ -82,20 +84,15 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 	ctx := context.Background()
 	referenceManifest, err := referrerStore.GetReferenceManifest(ctx, subjectReference, referenceDescriptor)
 	if err != nil {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Error fetching reference manifest for subject: %s reference descriptor: %v, err: %v", subjectReference, referenceDescriptor.Descriptor, err),
-		}, nil
+		storeErr := re.ErrorCodeGetReferenceManifestFailure.WithDetail(fmt.Sprintf("Failed to fetch reference manifest for subject: %s reference descriptor: %v", subjectReference, referenceDescriptor.Descriptor)).WithError(err)
+		result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &storeErr, nil)
+		return &result, nil
 	}
 
 	if len(referenceManifest.Blobs) == 0 {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("SBOM validation failed: no layers found in manifest for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()),
-		}, nil
+		noBlobErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No layers found in manifest for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()))
+		result := verifier.NewVerifierResult("", input.Name, verifierType, "SBOM validation failed", false, &noBlobErr, nil)
+		return &result, nil
 	}
 
 	artifactType := referenceDescriptor.ArtifactType
@@ -103,33 +100,22 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 		refBlob, err := referrerStore.GetBlobContent(ctx, subjectReference, blobDesc.Digest)
 
 		if err != nil {
-			return &verifier.VerifierResult{
-				Name:      input.Name,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Error fetching blob for subject: %s digest: %s, err: %v", subjectReference, blobDesc.Digest, err),
-			}, nil
+			storeErr := re.ErrorCodeGetBlobContentFailure.WithDetail(fmt.Sprintf("Failed to fetch blob for subject: %s digest: %s", subjectReference, blobDesc.Digest)).WithError(err)
+			result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &storeErr, nil)
+			return &result, nil
 		}
 
 		switch artifactType {
 		case SpdxJSONMediaType:
 			return processSpdxJSONMediaType(input.Name, verifierType, refBlob, input.DisallowedLicenses, input.DisallowedPackages), nil
 		default:
-			return &verifier.VerifierResult{
-				Name:      input.Name,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Unsupported artifactType: %s", artifactType),
-			}, nil
+			storeErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Unsupported artifactType: %s", artifactType))
+			result := verifier.NewVerifierResult("", input.Name, verifierType, "Failed to process SBOM blobs.", false, &storeErr, nil)
+			return &result, nil
 		}
 	}
-
-	return &verifier.VerifierResult{
-		Name:      input.Name,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "SBOM verification success. No license or package violation found.",
-	}, nil
+	result := verifier.NewVerifierResult("", input.Name, verifierType, "SBOM verification success. No license or package violation found.", true, nil, nil)
+	return &result, nil
 }
 
 // getViolations returns the package and license violations based on the deny list
@@ -177,31 +163,26 @@ func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte,
 			}
 
 			if len(licenseViolation) != 0 || len(packageViolation) != 0 {
-				return &verifier.VerifierResult{
-					Name:       name,
-					IsSuccess:  false,
-					Extensions: extensionData,
-					Message:    "SBOM validation failed. Please review extensions data for license and package violation found.",
-				}
+				sbomErr := errors.ErrorCodeVerifyPluginFailure.WithDetail("License or package violation found.").WithRemediation("Please review extensions data for license and package violation found.")
+				result := verifier.NewVerifierResult("", name, verifierType, "SBOM validation failed", false, &sbomErr, extensionData)
+				return &result
 			}
 		}
 
-		return &verifier.VerifierResult{
-			Name:      name,
-			Type:      verifierType,
-			IsSuccess: true,
-			Extensions: map[string]interface{}{
-				CreationInfo: spdxDoc.CreationInfo,
-			},
-			Message: "SBOM verification success. No license or package violation found.",
-		}
-	}
-	return &verifier.VerifierResult{
-		Name:      name,
-		Type:      verifierType,
-		IsSuccess: false,
-		Message:   fmt.Sprintf("SBOM failed to parse: %v", err),
+		result := verifier.NewVerifierResult(
+			"",
+			name,
+			verifierType,
+			"SBOM verification success. No license or package violation found.",
+			true,
+			nil,
+			map[string]interface{}{CreationInfo: spdxDoc.CreationInfo},
+		)
+		return &result
 	}
+	verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("failed to verify artifact: %s", name)).WithError(err)
+	result := verifier.NewVerifierResult("", name, verifierType, "", false, &verifierErr, nil)
+	return &result
 }
 
 // iterate through all package info and check against the deny list
diff --git a/plugins/verifier/schemavalidator/schema_validator.go b/plugins/verifier/schemavalidator/schema_validator.go
index 2af6639..d318862 100644
--- a/plugins/verifier/schemavalidator/schema_validator.go
+++ b/plugins/verifier/schemavalidator/schema_validator.go
@@ -20,6 +20,8 @@ import (
 	"encoding/json"
 	"fmt"
 
+	"github.com/ratify-project/ratify/errors"
+	re "github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/pkg/common"
 	"github.com/ratify-project/ratify/pkg/ocispecs"
 	"github.com/ratify-project/ratify/pkg/referrerstore"
@@ -71,12 +73,9 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 	}
 
 	if len(referenceManifest.Blobs) == 0 {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("schema validation failed: no blobs found for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()),
-		}, nil
+		noBlobErr := errors.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No blobs found for referrer %s@%s.", subjectReference.Path, referenceDescriptor.Digest.String()))
+		result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &noBlobErr, nil)
+		return &result, nil
 	}
 
 	for _, blobDesc := range referenceManifest.Blobs {
@@ -87,21 +86,14 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 
 		err = processMediaType(schemaMap, blobDesc.MediaType, refBlob)
 		if err != nil {
-			return &verifier.VerifierResult{
-				Name:      input.Name,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("schema validation failed for digest:[%s],media type:[%s],parse errors:[%v]", blobDesc.Digest, blobDesc.MediaType, err.Error()),
-			}, nil
+			verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("schema validation failed for digest:[%s], media type:[%s].", blobDesc.Digest, blobDesc.MediaType)).WithError(err)
+			result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &verifierErr, nil)
+			return &result, nil
 		}
 	}
 
-	return &verifier.VerifierResult{
-		Name:      input.Name,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "schema validation passed for configured media types",
-	}, nil
+	result := verifier.NewVerifierResult("", input.Name, verifierType, "schema validation passed for configured media types", true, nil, nil)
+	return &result, nil
 }
 
 func processMediaType(schemaMap map[string]string, mediaType string, refBlob []byte) error {
diff --git a/plugins/verifier/vulnerabilityreport/vulnerability_report.go b/plugins/verifier/vulnerabilityreport/vulnerability_report.go
index 37089cc..ec4004f 100644
--- a/plugins/verifier/vulnerabilityreport/vulnerability_report.go
+++ b/plugins/verifier/vulnerabilityreport/vulnerability_report.go
@@ -26,6 +26,8 @@ import (
 
 	imagespec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/owenrumney/go-sarif/v2/sarif"
+	"github.com/ratify-project/ratify/errors"
+	re "github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/pkg/common"
 	"github.com/ratify-project/ratify/pkg/ocispecs"
 	"github.com/ratify-project/ratify/pkg/referrerstore"
@@ -93,38 +95,39 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 	}
 	createdTime, err := extractCreationTimestamp(input.CreatedAnnotationName, referenceDescriptor)
 	if err != nil {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: error extracting create timestamp annotation:[%v]", err.Error()),
-		}, nil
+		verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to create timestamp annotation.").WithError(err)
+		result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &verifierErr, nil)
+		return &result, nil
 	}
 
 	// check report is newer than allowed maximum age
 	if input.MaximumAge != "" {
 		ok, err := validateMaximumAge(input.MaximumAge, createdTime)
 		if err != nil {
-			return &verifier.VerifierResult{
-				Name:      input.Name,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: error validating maximum age:[%v]", err.Error()),
-				Extensions: map[string]interface{}{
-					CreatedAnnotation: createdTime,
-				},
-			}, nil
+			verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to validate maximum age.").WithError(err)
+			result := verifier.NewVerifierResult(
+				"",
+				input.Name,
+				verifierType,
+				"",
+				false,
+				&verifierErr,
+				map[string]interface{}{CreatedAnnotation: createdTime},
+			)
+			return &result, nil
 		}
 		if !ok {
-			return &verifier.VerifierResult{
-				Name:      input.Name,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: report is older than maximum age:[%s]", input.MaximumAge),
-				Extensions: map[string]interface{}{
-					CreatedAnnotation: createdTime,
-				},
-			}, nil
+			verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Report is older than maximum age:[%s].", input.MaximumAge))
+			result := verifier.NewVerifierResult(
+				"",
+				input.Name,
+				verifierType,
+				"",
+				false,
+				&verifierErr,
+				map[string]interface{}{CreatedAnnotation: createdTime},
+			)
+			return &result, nil
 		}
 	}
 
@@ -132,84 +135,96 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
 
 	referenceManifest, err := referrerStore.GetReferenceManifest(ctx, subjectReference, referenceDescriptor)
 	if err != nil {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: error fetching reference manifest for subject: %s reference descriptor: %v: [%v]", subjectReference, referenceDescriptor.Descriptor, err.Error()),
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to fetch reference manifest for subject: %s, reference descriptor: %v.", subjectReference, referenceDescriptor)).WithError(err)
+		result := verifier.NewVerifierResult(
+			"",
+			input.Name,
+			verifierType,
+			"",
+			false,
+			&verifierErr,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 
 	if len(referenceManifest.Blobs) == 0 {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: no layers found in manifest for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()),
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		noBlobErr := errors.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No layers found in manifest for referrer %s@%s.", subjectReference.Path, referenceDescriptor.Digest.String()))
+		result := verifier.NewVerifierResult(
+			"",
+			input.Name,
+			verifierType,
+			"",
+			false,
+			&noBlobErr,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 
 	blobDesc := referenceManifest.Blobs[0]
 	refBlob, err := referrerStore.GetBlobContent(ctx, subjectReference, blobDesc.Digest)
 	if err != nil {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: error fetching blob for subject:[%s] digest:[%s]: [%v]", subjectReference, blobDesc.Digest, err.Error()),
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to fetch blob for subject:[%s] digest:[%s].", subjectReference, blobDesc.Digest)).WithError(err)
+		result := verifier.NewVerifierResult(
+			"",
+			input.Name,
+			verifierType,
+			"",
+			false,
+			&verifierErr,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 
 	// skip all validation if passthrough is enabled
 	if input.Passthrough {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: true,
-			Message:   "Validation skipped. passthrough enabled",
-			Extensions: map[string]interface{}{
+		result := verifier.NewVerifierResult(
+			"",
+			input.Name,
+			verifierType,
+			"Validation skipped. passthrough enabled",
+			true,
+			nil,
+			map[string]interface{}{
 				CreatedAnnotation: createdTime,
 				"passthrough":     true,
 				"report":          string(refBlob),
 			},
-		}, nil
+		)
+		return &result, nil
 	}
 
 	// validate json schema
 	if err := verifyJSONSchema(referenceDescriptor.ArtifactType, refBlob, input.SchemaURL); err != nil {
-		return &verifier.VerifierResult{
-			Name:      input.Name,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: schema validation failed for digest:[%s],artifact type:[%s],parse errors:[%v]", blobDesc.Digest, referenceDescriptor.ArtifactType, err.Error()),
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Schema validation failed for digest:[%s],artifact type:[%s].", blobDesc.Digest, referenceDescriptor.ArtifactType)).WithError(err)
+		result := verifier.NewVerifierResult(
+			"",
+			input.Name,
+			verifierType,
+			"",
+			false,
+			&verifierErr,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 
 	if referenceDescriptor.ArtifactType == SarifArtifactType {
 		return processSarifReport(input, input.Name, verifierType, refBlob, createdTime)
 	}
 
-	return &verifier.VerifierResult{
-		Name:      input.Name,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "Validation succeeded",
-		Extensions: map[string]interface{}{
-			CreatedAnnotation: createdTime,
-		},
-	}, nil
+	result := verifier.NewVerifierResult(
+		"",
+		input.Name,
+		verifierType,
+		"Validation succeeded",
+		true,
+		nil,
+		map[string]interface{}{CreatedAnnotation: createdTime},
+	)
+	return &result, nil
 }
 
 // verifyJSONSchema validates the json schema of the report
@@ -234,27 +249,30 @@ func verifyJSONSchema(artifactType string, refBlob []byte, schemaURL string) err
 func processSarifReport(input *PluginConfig, verifierName string, verifierType string, blob []byte, createdTime time.Time) (*verifier.VerifierResult, error) {
 	sarifReport, err := sarif.FromBytes(blob)
 	if err != nil {
-		return &verifier.VerifierResult{
-			Name:      verifierName,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   fmt.Sprintf("Validation failed: error parsing sarif report:[%v]", err.Error()),
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to parse sarif report.").WithError(err)
+		result := verifier.NewVerifierResult(
+			"",
+			verifierName,
+			verifierType,
+			"",
+			false,
+			&verifierErr,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 	// verify that there is at least one run in the report
 	if len(sarifReport.Runs) < 1 {
-		return &verifier.VerifierResult{
-			Name:      verifierName,
-			Type:      verifierType,
-			IsSuccess: false,
-			Message:   "Validation failed: no runs found in sarif report",
-			Extensions: map[string]interface{}{
-				CreatedAnnotation: createdTime,
-			},
-		}, nil
+		result := verifier.NewVerifierResult(
+			"",
+			verifierName,
+			verifierType,
+			"No runs found in sarif report.",
+			false,
+			nil,
+			map[string]interface{}{CreatedAnnotation: createdTime},
+		)
+		return &result, nil
 	}
 	scannerName := strings.ToLower(sarifReport.Runs[0].Tool.Driver.Name)
 	if len(input.DenylistCVEs) > 0 {
@@ -276,16 +294,19 @@ func processSarifReport(input *PluginConfig, verifierName string, verifierType s
 		}
 	}
 
-	return &verifier.VerifierResult{
-		Name:      verifierName,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "Validation succeeded",
-		Extensions: map[string]interface{}{
+	result := verifier.NewVerifierResult(
+		"",
+		verifierName,
+		verifierType,
+		"Validation succeeded",
+		true,
+		nil,
+		map[string]interface{}{
 			CreatedAnnotation: createdTime,
 			"scanner":         scannerName,
 		},
-	}, nil
+	)
+	return &result, nil
 }
 
 // verifyDenyListCVEs verifies that the report does not contain any deny-listed CVEs
@@ -301,16 +322,19 @@ func verifyDenyListCVEs(verifierName string, verifierType string, scannerName st
 	// iterate over the results and check which cves are deny-listed
 	for _, result := range sarifReport.Runs[0].Results {
 		if result.RuleID == nil || *result.RuleID == "" {
-			return &verifier.VerifierResult{
-				Name:      verifierName,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: rule id not found for result:[%v]", result),
-				Extensions: map[string]interface{}{
+			verifierResult := verifier.NewVerifierResult(
+				"",
+				verifierName,
+				verifierType,
+				fmt.Sprintf("Rule id not found for result:[%v].", result),
+				false,
+				nil,
+				map[string]interface{}{
 					"scanner":         scannerName,
 					CreatedAnnotation: createdTime,
 				},
-			}, nil
+			)
+			return &verifierResult, nil
 		}
 		ruleIDLower := strings.ToLower(*result.RuleID)
 		if _, ok := denylistCVESet[ruleIDLower]; ok {
@@ -326,30 +350,36 @@ func verifyDenyListCVEs(verifierName string, verifierType string, scannerName st
 	}
 
 	if len(denylistViolations) > 0 {
-		return &verifier.VerifierResult{
-			Name:      verifierName,
-			Type:      verifierType,
-			IsSuccess: false,
-			Extensions: map[string]interface{}{
+		result := verifier.NewVerifierResult(
+			"",
+			verifierName,
+			verifierType,
+			"Found denied CVEs. See extensions field for details.",
+			false,
+			nil,
+			map[string]interface{}{
 				"scanner":         scannerName,
 				"denylistCVEs":    denylistCVEs,
 				"cveViolations":   denylistViolations,
 				CreatedAnnotation: createdTime,
 			},
-			Message: "Validation failed: found denied CVEs. See extensions field for details.",
-		}, nil
+		)
+		return &result, nil
 	}
 
-	return &verifier.VerifierResult{
-		Name:      verifierName,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "Validation succeeded",
-		Extensions: map[string]interface{}{
+	result := verifier.NewVerifierResult(
+		"",
+		verifierName,
+		verifierType,
+		"Validation succeeded",
+		true,
+		nil,
+		map[string]interface{}{
 			"scanner":         scannerName,
 			CreatedAnnotation: createdTime,
 		},
-	}, nil
+	)
+	return &result, nil
 }
 
 // verifyDisallowedSeverities verifies that the report does not contain any disallowed severity levels
@@ -363,42 +393,52 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne
 	// iterate over the results and check if the severity is disallowed
 	for _, result := range sarifReport.Runs[0].Results {
 		if result.RuleID == nil || *result.RuleID == "" {
-			return &verifier.VerifierResult{
-				Name:      verifierName,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: rule id not found for result:[%v]", result),
-				Extensions: map[string]interface{}{
+			verifierResult := verifier.NewVerifierResult(
+				"",
+				verifierName,
+				verifierType,
+				fmt.Sprintf("Rule id not found for result:[%v].", result),
+				false,
+				nil,
+				map[string]interface{}{
 					"scanner":         scannerName,
 					CreatedAnnotation: createdTime,
 				},
-			}, nil
+			)
+			return &verifierResult, nil
 		}
 		rule, ok := ruleMap[*result.RuleID]
 		if !ok {
-			return &verifier.VerifierResult{
-				Name:      verifierName,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: rule not found for result:[%v]", result),
-				Extensions: map[string]interface{}{
+			verifierResult := verifier.NewVerifierResult(
+				"",
+				verifierName,
+				verifierType,
+				fmt.Sprintf("Rule not found for result:[%v].", result),
+				false,
+				nil,
+				map[string]interface{}{
 					"scanner":         scannerName,
 					CreatedAnnotation: createdTime,
 				},
-			}, nil
+			)
+			return &verifierResult, nil
 		}
 		severity, err := extractSeverity(scannerName, *rule)
 		if err != nil {
-			return &verifier.VerifierResult{
-				Name:      verifierName,
-				Type:      verifierType,
-				IsSuccess: false,
-				Message:   fmt.Sprintf("Validation failed: error extracting severity:[%v]", err.Error()),
-				Extensions: map[string]interface{}{
+			verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to extract severity.").WithError(err)
+			verifierResult := verifier.NewVerifierResult(
+				"",
+				verifierName,
+				verifierType,
+				"",
+				false,
+				&verifierErr,
+				map[string]interface{}{
 					"scanner":         scannerName,
 					CreatedAnnotation: createdTime,
 				},
-			}, nil
+			)
+			return &verifierResult, nil
 		}
 		// check if the severity is disallowed and add it to the map of violating CVE IDs
 		for _, disallowed := range disallowedSeverities {
@@ -409,29 +449,35 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne
 	}
 	// if there are violating rules, return them as custom extension field
 	if len(violatingRules) > 0 {
-		return &verifier.VerifierResult{
-			Name:      verifierName,
-			Type:      verifierType,
-			IsSuccess: false,
-			Extensions: map[string]interface{}{
+		result := verifier.NewVerifierResult(
+			"",
+			verifierName,
+			verifierType,
+			"Found disallowed severities. See extensions field for details.",
+			false,
+			nil,
+			map[string]interface{}{
 				"scanner":              scannerName,
 				"disallowedSeverities": disallowedSeverities,
 				"severityViolations":   violatingRules,
 				CreatedAnnotation:      createdTime,
 			},
-			Message: "Validation failed: found disallowed severities. See extensions field for details.",
-		}, nil
+		)
+		return &result, nil
 	}
-	return &verifier.VerifierResult{
-		Name:      verifierName,
-		Type:      verifierType,
-		IsSuccess: true,
-		Message:   "Validation succeeded",
-		Extensions: map[string]interface{}{
+	result := verifier.NewVerifierResult(
+		"",
+		verifierName,
+		verifierType,
+		"Validation succeeded",
+		true,
+		nil,
+		map[string]interface{}{
 			"scanner":         scannerName,
 			CreatedAnnotation: createdTime,
 		},
-	}, nil
+	)
+	return &result, nil
 }
 
 // extractSeverity extracts the severity from the rule help text using regex


Test patch:
diff --git a/pkg/certificateprovider/azurekeyvault/provider_test.go b/pkg/certificateprovider/azurekeyvault/provider_test.go
index 001c9b7..f11f31e 100644
--- a/pkg/certificateprovider/azurekeyvault/provider_test.go
+++ b/pkg/certificateprovider/azurekeyvault/provider_test.go
@@ -26,6 +26,7 @@ import (
 
 	kv "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
 	"github.com/Azure/go-autorest/autorest/azure"
+	"github.com/ratify-project/ratify/internal/version"
 	"github.com/ratify-project/ratify/pkg/certificateprovider/azurekeyvault/types"
 	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
@@ -105,7 +106,7 @@ func SkipTestInitializeKVClient(t *testing.T) {
 		assert.NoError(t, err)
 		assert.NotNil(t, kvBaseClient)
 		assert.NotNil(t, kvBaseClient.Authorizer)
-		assert.Contains(t, kvBaseClient.UserAgent, "ratify")
+		assert.Contains(t, kvBaseClient.UserAgent, version.UserAgent)
 	}
 }
 
diff --git a/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go b/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
index caa2011..d4129e2 100644
--- a/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
+++ b/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
@@ -17,267 +17,143 @@ package clusterresource
 
 import (
 	"context"
-	"fmt"
+	"errors"
+	"reflect"
 	"testing"
 
-	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
-	"github.com/ratify-project/ratify/internal/constants"
-	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
-	"github.com/sirupsen/logrus"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/refresh"
 	test "github.com/ratify-project/ratify/pkg/utils"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-const (
-	kmpName = "kmpName"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 )
 
-// TestUpdateErrorStatus tests the updateErrorStatus method
-func TestKMProviderUpdateErrorStatus(t *testing.T) {
-	var parametersString = "{\"certs\":{\"name\":\"certName\"}}"
-	var kmProviderStatus = []byte(parametersString)
-
-	status := configv1beta1.KeyManagementProviderStatus{
-		IsSuccess: true,
-		Properties: runtime.RawExtension{
-			Raw: kmProviderStatus,
+func TestKeyManagementProviderReconciler_ReconcileWithConfig(t *testing.T) {
+	tests := []struct {
+		name              string
+		refresherType     string
+		createConfigError bool
+		refreshError      bool
+		expectedError     bool
+	}{
+		{
+			name:              "Successful Reconcile",
+			refresherType:     "mockRefresher",
+			createConfigError: false,
+			refreshError:      false,
+			expectedError:     false,
+		},
+		{
+			name:              "Refresher Error",
+			refresherType:     "mockRefresher",
+			createConfigError: false,
+			refreshError:      true,
+			expectedError:     true,
+		},
+		{
+			name:              "Invalid Refresher Type",
+			refresherType:     "invalidRefresher",
+			createConfigError: true,
+			refreshError:      false,
+			expectedError:     true,
 		},
 	}
-	keyManagementProvider := configv1beta1.KeyManagementProvider{
-		Status: status,
-	}
-	expectedErr := "it's a long error from unit test"
-	lastFetchedTime := metav1.Now()
-	updateKMProviderErrorStatus(&keyManagementProvider, expectedErr, &lastFetchedTime)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Name:      "fake-name",
+					Namespace: "fake-namespace",
+				},
+			}
+			scheme, _ := test.CreateScheme()
+			client := fake.NewClientBuilder().WithScheme(scheme).Build()
 
-	if keyManagementProvider.Status.IsSuccess != false {
-		t.Fatalf("Unexpected error, expected isSuccess to be false , actual %+v", keyManagementProvider.Status.IsSuccess)
-	}
+			r := &KeyManagementProviderReconciler{
+				Client: client,
+				Scheme: runtime.NewScheme(),
+			}
 
-	if keyManagementProvider.Status.Error != expectedErr {
-		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedErr, keyManagementProvider.Status.Error)
-	}
-	expectedBriedErr := fmt.Sprintf("%s...", expectedErr[:30])
-	if keyManagementProvider.Status.BriefError != expectedBriedErr {
-		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedBriedErr, keyManagementProvider.Status.Error)
-	}
+			refresherConfig := map[string]interface{}{
+				"type":              tt.refresherType,
+				"client":            client,
+				"request":           req,
+				"createConfigError": tt.createConfigError,
+				"refreshError":      tt.refreshError,
+				"shouldError":       tt.expectedError,
+			}
 
-	//make sure properties of last cached cert was not overridden
-	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
-		t.Fatalf("Unexpected properties,  expected %+v, got %+v", parametersString, string(keyManagementProvider.Status.Properties.Raw))
+			_, err := r.ReconcileWithConfig(context.TODO(), refresherConfig)
+			if tt.expectedError && err == nil {
+				t.Errorf("Expected error, got nil")
+			}
+			if !tt.expectedError && err != nil {
+				t.Errorf("Expected no error, got %v", err)
+			}
+		})
 	}
 }
-
-// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method
-func TestKMProviderUpdateSuccessStatus(t *testing.T) {
-	kmProviderStatus := keymanagementprovider.KeyManagementProviderStatus{}
-	properties := map[string]string{}
-	properties["Name"] = "wabbit"
-	properties["Version"] = "ABC"
-
-	kmProviderStatus["Certificates"] = properties
-
-	lastFetchedTime := metav1.Now()
-
-	status := configv1beta1.KeyManagementProviderStatus{
-		IsSuccess: false,
-		Error:     "error from last operation",
-	}
-	keyManagementProvider := configv1beta1.KeyManagementProvider{
-		Status: status,
+func TestKeyManagementProviderReconciler_Reconcile(t *testing.T) {
+	req := ctrl.Request{
+		NamespacedName: client.ObjectKey{
+			Name:      "fake-name",
+			Namespace: "fake-namespace",
+		},
 	}
 
-	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, kmProviderStatus)
+	// Create a fake client and scheme
+	scheme, _ := test.CreateScheme()
+	client := fake.NewClientBuilder().WithScheme(scheme).Build()
 
-	if keyManagementProvider.Status.IsSuccess != true {
-		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	r := &KeyManagementProviderReconciler{
+		Client: client,
+		Scheme: runtime.NewScheme(),
 	}
 
-	if keyManagementProvider.Status.Error != "" {
-		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	// Call the Reconcile method
+	result, err := r.Reconcile(context.TODO(), req)
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
 	}
 
-	//make sure properties of last cached cert was updated
-	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
-		t.Fatalf("Properties should not be empty")
+	// Check the result
+	expectedResult := ctrl.Result{}
+	if !reflect.DeepEqual(result, expectedResult) {
+		t.Errorf("Expected result %v, got %v", expectedResult, result)
 	}
 }
 
-// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method with empty properties
-func TestKMProviderUpdateSuccessStatus_emptyProperties(t *testing.T) {
-	lastFetchedTime := metav1.Now()
-	status := configv1beta1.KeyManagementProviderStatus{
-		IsSuccess: false,
-		Error:     "error from last operation",
-	}
-	keyManagementProvider := configv1beta1.KeyManagementProvider{
-		Status: status,
-	}
-
-	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, nil)
-
-	if keyManagementProvider.Status.IsSuccess != true {
-		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
-	}
-
-	if keyManagementProvider.Status.Error != "" {
-		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
-	}
-
-	//make sure properties of last cached cert was updated
-	if len(keyManagementProvider.Status.Properties.Raw) != 0 {
-		t.Fatalf("Properties should be empty")
-	}
+type MockRefresher struct {
+	Results           ctrl.Result
+	CreateConfigError bool
+	RefreshError      bool
+	ShouldError       bool
 }
 
-func TestWriteKMProviderStatus(t *testing.T) {
-	logger := logrus.WithContext(context.Background())
-	lastFetchedTime := metav1.Now()
-	testCases := []struct {
-		name       string
-		isSuccess  bool
-		kmProvider *configv1beta1.KeyManagementProvider
-		errString  string
-		reconciler client.StatusClient
-	}{
-		{
-			name:       "success status",
-			isSuccess:  true,
-			errString:  "",
-			kmProvider: &configv1beta1.KeyManagementProvider{},
-			reconciler: &test.MockStatusClient{},
-		},
-		{
-			name:       "error status",
-			isSuccess:  false,
-			kmProvider: &configv1beta1.KeyManagementProvider{},
-			errString:  "a long error string that exceeds the max length of 30 characters",
-			reconciler: &test.MockStatusClient{},
-		},
-		{
-			name:       "status update failed",
-			isSuccess:  true,
-			kmProvider: &configv1beta1.KeyManagementProvider{},
-			reconciler: &test.MockStatusClient{
-				UpdateFailed: true,
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			writeKMProviderStatus(context.Background(), tc.reconciler, tc.kmProvider, logger, tc.isSuccess, tc.errString, lastFetchedTime, nil)
-
-			if tc.kmProvider.Status.IsSuccess != tc.isSuccess {
-				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.kmProvider.Status.IsSuccess)
-			}
-
-			if tc.kmProvider.Status.Error != tc.errString {
-				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.kmProvider.Status.Error)
-			}
-		})
+func (mr *MockRefresher) Refresh(_ context.Context) error {
+	if mr.RefreshError {
+		return errors.New("refresh error")
 	}
+	return nil
 }
 
-func TestKMPReconcile(t *testing.T) {
-	tests := []struct {
-		name             string
-		description      string
-		provider         *configv1beta1.KeyManagementProvider
-		req              *reconcile.Request
-		expectedErr      bool
-		expectedKMPCount int
-	}{
-		{
-			name:        "nonexistent KMP",
-			description: "Reconciling a non-existent KMP CR, it should be deleted from maps",
-			req: &reconcile.Request{
-				NamespacedName: types.NamespacedName{Name: "nonexistent"},
-			},
-			provider: &configv1beta1.KeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: constants.EmptyNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.KeyManagementProviderSpec{
-					Type: "inline",
-				},
-			},
-			expectedErr:      false,
-			expectedKMPCount: 0,
-		},
-		{
-			name:        "invalid params",
-			description: "Received invalid parameters of the KMP Spec, it should fail the reconcile and return an error",
-			provider: &configv1beta1.KeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: constants.EmptyNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.KeyManagementProviderSpec{
-					Type: "inline",
-				},
-			},
-			expectedErr:      true,
-			expectedKMPCount: 0,
-		},
-		{
-			name:        "valid params",
-			description: "Received a valid KMP manifest, it should be added to the cert map",
-			provider: &configv1beta1.KeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: constants.EmptyNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.KeyManagementProviderSpec{
-					Type: "inline",
-					Parameters: runtime.RawExtension{
-						Raw: []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
-					},
-				},
-			},
-			expectedErr:      false,
-			expectedKMPCount: 1,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			resetKMP()
-			scheme, _ := test.CreateScheme()
-			client := fake.NewClientBuilder().WithScheme(scheme)
-			client.WithObjects(tt.provider)
-			r := &KeyManagementProviderReconciler{
-				Scheme: scheme,
-				Client: client.Build(),
-			}
-			var req reconcile.Request
-			if tt.req != nil {
-				req = *tt.req
-			} else {
-				req = reconcile.Request{
-					NamespacedName: test.KeyFor(tt.provider),
-				}
-			}
+func (mr *MockRefresher) GetResult() interface{} {
+	return ctrl.Result{}
+}
 
-			_, err := r.Reconcile(context.Background(), req)
-			if tt.expectedErr != (err != nil) {
-				t.Fatalf("Reconcile() expected error %v, actual %v", tt.expectedErr, err)
-			}
-			certs, _ := keymanagementprovider.GetCertificatesFromMap(context.Background(), kmpName)
-			if len(certs) != tt.expectedKMPCount {
-				t.Fatalf("Cert map expected size %v, actual %v", tt.expectedKMPCount, len(certs))
-			}
-		})
+func (mr *MockRefresher) Create(config map[string]interface{}) (refresh.Refresher, error) {
+	createConfigError := config["createConfigError"].(bool)
+	refreshError := config["refreshError"].(bool)
+	if createConfigError {
+		return nil, errors.New("create error")
 	}
+	return &MockRefresher{
+		CreateConfigError: createConfigError,
+		RefreshError:      refreshError,
+	}, nil
 }
 
-func resetKMP() {
-	keymanagementprovider.DeleteCertificatesFromMap(storeName)
+func init() {
+	refresh.Register("mockRefresher", &MockRefresher{})
 }
diff --git a/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go b/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go
index fe0728d..7e4717a 100644
--- a/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go
+++ b/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go
@@ -17,264 +17,144 @@ package namespaceresource
 
 import (
 	"context"
-	"fmt"
+	"errors"
+	"reflect"
 	"testing"
 
-	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
-	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
-	"github.com/sirupsen/logrus"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-	"github.com/ratify-project/ratify/internal/constants"
-	ctxUtils "github.com/ratify-project/ratify/internal/context"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/refresh"
 	test "github.com/ratify-project/ratify/pkg/utils"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-const (
-	kmpName = "kmpName"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 )
 
-// TestUpdateErrorStatus tests the updateErrorStatus method
-func TestKMProviderUpdateErrorStatus(t *testing.T) {
-	var parametersString = "{\"certs\":{\"name\":\"certName\"}}"
-	var kmProviderStatus = []byte(parametersString)
-
-	status := configv1beta1.NamespacedKeyManagementProviderStatus{
-		IsSuccess: true,
-		Properties: runtime.RawExtension{
-			Raw: kmProviderStatus,
+func TestKeyManagementProviderReconciler_ReconcileWithConfig(t *testing.T) {
+	tests := []struct {
+		name              string
+		refresherType     string
+		createConfigError bool
+		refreshError      bool
+		expectedError     bool
+	}{
+		{
+			name:              "Successful Reconcile",
+			refresherType:     "mockRefresher",
+			createConfigError: false,
+			refreshError:      false,
+			expectedError:     false,
+		},
+		{
+			name:              "Refresher Error",
+			refresherType:     "mockRefresher",
+			createConfigError: false,
+			refreshError:      true,
+			expectedError:     true,
+		},
+		{
+			name:              "Invalid Refresher Type",
+			refresherType:     "invalidRefresher",
+			createConfigError: true,
+			refreshError:      false,
+			expectedError:     true,
 		},
 	}
-	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
-		Status: status,
-	}
-	expectedErr := "it's a long error from unit test"
-	lastFetchedTime := metav1.Now()
-	updateKMProviderErrorStatus(&keyManagementProvider, expectedErr, &lastFetchedTime)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Name:      "fake-name",
+					Namespace: "fake-namespace",
+				},
+			}
+			scheme, _ := test.CreateScheme()
+			client := fake.NewClientBuilder().WithScheme(scheme).Build()
 
-	if keyManagementProvider.Status.IsSuccess != false {
-		t.Fatalf("Unexpected error, expected isSuccess to be false , actual %+v", keyManagementProvider.Status.IsSuccess)
-	}
+			r := &KeyManagementProviderReconciler{
+				Client: client,
+				Scheme: runtime.NewScheme(),
+			}
 
-	if keyManagementProvider.Status.Error != expectedErr {
-		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedErr, keyManagementProvider.Status.Error)
-	}
-	expectedBriedErr := fmt.Sprintf("%s...", expectedErr[:30])
-	if keyManagementProvider.Status.BriefError != expectedBriedErr {
-		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedBriedErr, keyManagementProvider.Status.Error)
-	}
+			refresherConfig := map[string]interface{}{
+				"type":              tt.refresherType,
+				"client":            client,
+				"request":           req,
+				"createConfigError": tt.createConfigError,
+				"refreshError":      tt.refreshError,
+				"shouldError":       tt.expectedError,
+			}
 
-	//make sure properties of last cached cert was not overridden
-	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
-		t.Fatalf("Unexpected properties,  expected %+v, got %+v", parametersString, string(keyManagementProvider.Status.Properties.Raw))
+			_, err := r.ReconcileWithConfig(context.TODO(), refresherConfig)
+			if tt.expectedError && err == nil {
+				t.Errorf("Expected error, got nil")
+			}
+			if !tt.expectedError && err != nil {
+				t.Errorf("Expected no error, got %v", err)
+			}
+		})
 	}
 }
 
-// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method
-func TestKMProviderUpdateSuccessStatus(t *testing.T) {
-	kmProviderStatus := keymanagementprovider.KeyManagementProviderStatus{}
-	properties := map[string]string{}
-	properties["Name"] = "wabbit"
-	properties["Version"] = "ABC"
-
-	kmProviderStatus["Certificates"] = properties
-
-	lastFetchedTime := metav1.Now()
-
-	status := configv1beta1.NamespacedKeyManagementProviderStatus{
-		IsSuccess: false,
-		Error:     "error from last operation",
-	}
-	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
-		Status: status,
+func TestKeyManagementProviderReconciler_Reconcile(t *testing.T) {
+	req := ctrl.Request{
+		NamespacedName: client.ObjectKey{
+			Name:      "fake-name",
+			Namespace: "fake-namespace",
+		},
 	}
 
-	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, kmProviderStatus)
+	// Create a fake client and scheme
+	scheme, _ := test.CreateScheme()
+	client := fake.NewClientBuilder().WithScheme(scheme).Build()
 
-	if keyManagementProvider.Status.IsSuccess != true {
-		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	r := &KeyManagementProviderReconciler{
+		Client: client,
+		Scheme: runtime.NewScheme(),
 	}
 
-	if keyManagementProvider.Status.Error != "" {
-		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	// Call the Reconcile method
+	result, err := r.Reconcile(context.TODO(), req)
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
 	}
 
-	//make sure properties of last cached cert was updated
-	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
-		t.Fatalf("Properties should not be empty")
+	// Check the result
+	expectedResult := ctrl.Result{}
+	if !reflect.DeepEqual(result, expectedResult) {
+		t.Errorf("Expected result %v, got %v", expectedResult, result)
 	}
 }
 
-// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method with empty properties
-func TestKMProviderUpdateSuccessStatus_emptyProperties(t *testing.T) {
-	lastFetchedTime := metav1.Now()
-	status := configv1beta1.NamespacedKeyManagementProviderStatus{
-		IsSuccess: false,
-		Error:     "error from last operation",
-	}
-	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
-		Status: status,
-	}
-
-	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, nil)
-
-	if keyManagementProvider.Status.IsSuccess != true {
-		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
-	}
-
-	if keyManagementProvider.Status.Error != "" {
-		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
-	}
-
-	//make sure properties of last cached cert was updated
-	if len(keyManagementProvider.Status.Properties.Raw) != 0 {
-		t.Fatalf("Properties should be empty")
-	}
+type MockRefresher struct {
+	Results           ctrl.Result
+	CreateConfigError bool
+	RefreshError      bool
+	ShouldError       bool
 }
 
-func TestWriteKMProviderStatus(t *testing.T) {
-	logger := logrus.WithContext(context.Background())
-	lastFetchedTime := metav1.Now()
-	testCases := []struct {
-		name       string
-		isSuccess  bool
-		kmProvider *configv1beta1.NamespacedKeyManagementProvider
-		errString  string
-		reconciler client.StatusClient
-	}{
-		{
-			name:       "success status",
-			isSuccess:  true,
-			errString:  "",
-			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
-			reconciler: &test.MockStatusClient{},
-		},
-		{
-			name:       "error status",
-			isSuccess:  false,
-			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
-			errString:  "a long error string that exceeds the max length of 30 characters",
-			reconciler: &test.MockStatusClient{},
-		},
-		{
-			name:       "status update failed",
-			isSuccess:  true,
-			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
-			reconciler: &test.MockStatusClient{
-				UpdateFailed: true,
-			},
-		},
+func (mr *MockRefresher) Refresh(_ context.Context) error {
+	if mr.RefreshError {
+		return errors.New("refresh error")
 	}
+	return nil
+}
 
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			writeKMProviderStatus(context.Background(), tc.reconciler, tc.kmProvider, logger, tc.isSuccess, tc.errString, lastFetchedTime, nil)
-
-			if tc.kmProvider.Status.IsSuccess != tc.isSuccess {
-				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.kmProvider.Status.IsSuccess)
-			}
-
-			if tc.kmProvider.Status.Error != tc.errString {
-				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.kmProvider.Status.Error)
-			}
-		})
-	}
+func (mr *MockRefresher) GetResult() interface{} {
+	return ctrl.Result{}
 }
 
-func TestKMPReconcile(t *testing.T) {
-	tests := []struct {
-		name             string
-		description      string
-		provider         *configv1beta1.NamespacedKeyManagementProvider
-		req              *reconcile.Request
-		expectedErr      bool
-		expectedKMPCount int
-	}{
-		{
-			name:        "nonexistent KMP",
-			description: "Reconciling a non-existent KMP CR, it should be deleted from maps",
-			req: &reconcile.Request{
-				NamespacedName: types.NamespacedName{Name: "nonexistent"},
-			},
-			provider: &configv1beta1.NamespacedKeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: testNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
-					Type: "inline",
-				},
-			},
-			expectedErr:      false,
-			expectedKMPCount: 0,
-		},
-		{
-			name:        "invalid params",
-			description: "Received invalid parameters of the KMP Spec, it should fail the reconcile and return an error",
-			provider: &configv1beta1.NamespacedKeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: testNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
-					Type: "inline",
-				},
-			},
-			expectedErr:      true,
-			expectedKMPCount: 0,
-		},
-		{
-			name:        "valid params",
-			description: "Received a valid KMP manifest, it should be added to the cert map",
-			provider: &configv1beta1.NamespacedKeyManagementProvider{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: testNamespace,
-					Name:      kmpName,
-				},
-				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
-					Type: "inline",
-					Parameters: runtime.RawExtension{
-						Raw: []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
-					},
-				},
-			},
-			expectedErr:      false,
-			expectedKMPCount: 1,
-		},
+func (mr *MockRefresher) Create(config map[string]interface{}) (refresh.Refresher, error) {
+	createConfigError := config["shouldError"].(bool)
+	refreshError := config["refreshError"].(bool)
+	if createConfigError {
+		return nil, errors.New("create error")
 	}
+	return &MockRefresher{
+		CreateConfigError: createConfigError,
+		RefreshError:      refreshError,
+	}, nil
+}
 
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			scheme, _ := test.CreateScheme()
-			client := fake.NewClientBuilder().WithScheme(scheme)
-			client.WithObjects(tt.provider)
-			r := &KeyManagementProviderReconciler{
-				Scheme: scheme,
-				Client: client.Build(),
-			}
-			var req reconcile.Request
-			if tt.req != nil {
-				req = *tt.req
-			} else {
-				req = reconcile.Request{
-					NamespacedName: test.KeyFor(tt.provider),
-				}
-			}
-
-			_, err := r.Reconcile(context.Background(), req)
-			if tt.expectedErr != (err != nil) {
-				t.Fatalf("Reconcile() expected error %v, actual %v", tt.expectedErr, err)
-			}
-			ctx := ctxUtils.SetContextWithNamespace(context.Background(), testNamespace)
-			certs, _ := keymanagementprovider.GetCertificatesFromMap(ctx, testNamespace+constants.NamespaceSeperator+kmpName)
-			if len(certs) != tt.expectedKMPCount {
-				t.Fatalf("Cert map expected size %v, actual %v", tt.expectedKMPCount, len(certs))
-			}
-		})
-	}
+func init() {
+	refresh.Register("mockRefresher", &MockRefresher{})
 }
diff --git a/pkg/customresources/certificatestores/api.go b/pkg/customresources/certificatestores/api.go
index 9cc4709..95d7201 100644
--- a/pkg/customresources/certificatestores/api.go
+++ b/pkg/customresources/certificatestores/api.go
@@ -28,4 +28,7 @@ type CertStoreManager interface {
 
 	// DeleteStore deletes the certificate from the given scope.
 	DeleteStore(storeName string)
+
+	// AddStoreError adds an error to the given certificate store.
+	AddStoreError(storeName string, err error)
 }
diff --git a/pkg/customresources/certificatestores/certificatestores.go b/pkg/customresources/certificatestores/certificatestores.go
index 1bea741..96d33d3 100644
--- a/pkg/customresources/certificatestores/certificatestores.go
+++ b/pkg/customresources/certificatestores/certificatestores.go
@@ -21,6 +21,7 @@ import (
 	"strings"
 	"sync"
 
+	"github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/internal/constants"
 	ctxUtils "github.com/ratify-project/ratify/internal/context"
 	"github.com/ratify-project/ratify/pkg/utils"
@@ -37,6 +38,15 @@ type ActiveCertStores struct {
 	//   "namespace2/store2": []*x509.Certificate
 	// }
 	scopedCertStores sync.Map
+
+	// scopedStoreErrors is mapping from cert store name to error generated while reconciling the store.
+	// The certificate store name is prefixed with the namespace.
+	// Example:
+	// {
+	//   "namespace1/store1": error,
+	//   "namespace2/store2": error
+	// }
+	scopedStoreErrors sync.Map
 }
 
 func NewActiveCertStores() CertStoreManager {
@@ -52,24 +62,34 @@ func (c *ActiveCertStores) GetCertsFromStore(ctx context.Context, storeName stri
 	}
 
 	if !hasAccessToStore(ctx, storeName) {
-		return []*x509.Certificate{}, fmt.Errorf("namespace: [%s] does not have access to certificate store: %s", ctxUtils.GetNamespace(ctx), storeName)
+		return []*x509.Certificate{}, errors.ErrorCodeForbidden.WithDetail(fmt.Sprintf("namespace: [%s] does not have access to certificate store: %s", ctxUtils.GetNamespace(ctx), storeName)).WithRemediation(fmt.Sprintf("Ensure the certificate store: %s is created under namespace: [%s]", storeName, ctxUtils.GetNamespace(ctx)))
 	}
+	if err, ok := c.scopedStoreErrors.Load(prependedName); ok && err != nil {
+		return []*x509.Certificate{}, err.(error)
+	}
+
 	if certs, ok := c.scopedCertStores.Load(prependedName); ok {
 		return certs.([]*x509.Certificate), nil
 	}
-	return []*x509.Certificate{}, fmt.Errorf("failed to access non-existent certificate store: %s", storeName)
+	return []*x509.Certificate{}, errors.ErrorCodeNotFound.WithDetail(fmt.Sprintf("failed to access non-existent certificate store: %s", storeName))
 }
 
 // AddStore fulfills the CertStoreManager interface.
 // It adds the given certificate under cert store.
 func (c *ActiveCertStores) AddStore(storeName string, cert []*x509.Certificate) {
 	c.scopedCertStores.Store(storeName, cert)
+	c.scopedStoreErrors.Delete(storeName)
 }
 
 // DeleteStore fulfills the CertStoreManager interface.
 // It deletes the given cert store.
 func (c *ActiveCertStores) DeleteStore(storeName string) {
 	c.scopedCertStores.Delete(storeName)
+	c.scopedStoreErrors.Delete(storeName)
+}
+
+func (c *ActiveCertStores) AddStoreError(storeName string, err error) {
+	c.scopedStoreErrors.Store(storeName, err)
 }
 
 // A namespaced verification request could access certStores in the same namespace.
diff --git a/pkg/customresources/certificatestores/certificatestores_test.go b/pkg/customresources/certificatestores/certificatestores_test.go
index 4e45e9f..e6065e4 100644
--- a/pkg/customresources/certificatestores/certificatestores_test.go
+++ b/pkg/customresources/certificatestores/certificatestores_test.go
@@ -19,6 +19,7 @@ import (
 	"crypto/rsa"
 	"crypto/x509"
 	"crypto/x509/pkix"
+	"errors"
 	"math/big"
 	"os"
 	"testing"
@@ -52,6 +53,11 @@ func TestCertStoresOperations(t *testing.T) {
 	ctx := context.Background()
 	certStore1 := []*x509.Certificate{cert1}
 
+	activeCertStores.AddStoreError(store1, errors.New("error1"))
+	if _, err := activeCertStores.GetCertsFromStore(ctx, store1); err == nil {
+		t.Fatalf("expect to get error, but got nil")
+	}
+
 	activeCertStores.AddStore(store1, certStore1)
 	certs, _ := activeCertStores.GetCertsFromStore(ctx, store1)
 	if len(certs) != 1 {
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider_test.go b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
index 9d134e5..676a438 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider_test.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
@@ -26,6 +26,7 @@ import (
 
 	kv "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
 	"github.com/Azure/go-autorest/autorest/azure"
+	"github.com/ratify-project/ratify/internal/version"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/azurekeyvault/types"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/config"
 	"github.com/stretchr/testify/assert"
@@ -62,11 +63,11 @@ func SkipTestInitializeKVClient(t *testing.T) {
 	}
 
 	for i := range testEnvs {
-		kvBaseClient, err := initializeKvClient(context.TODO(), testEnvs[i].KeyVaultEndpoint, "", "")
+		kvBaseClient, err := initializeKvClient(context.TODO(), testEnvs[i].KeyVaultEndpoint, "", "", version.UserAgent)
 		assert.NoError(t, err)
 		assert.NotNil(t, kvBaseClient)
 		assert.NotNil(t, kvBaseClient.Authorizer)
-		assert.Contains(t, kvBaseClient.UserAgent, "ratify")
+		assert.Contains(t, kvBaseClient.UserAgent, version.UserAgent)
 	}
 }
 
@@ -173,7 +174,7 @@ func TestCreate(t *testing.T) {
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			initKVClient = func(_ context.Context, _, _, _ string) (*kv.BaseClient, error) {
+			initKVClient = func(_ context.Context, _, _, _, _ string) (*kv.BaseClient, error) {
 				return &kv.BaseClient{}, nil
 			}
 			_, err := factory.Create("v1", tc.config, "")
@@ -224,7 +225,7 @@ func TestGetKeys(t *testing.T) {
 		},
 	}
 
-	initKVClient = func(_ context.Context, _, _, _ string) (*kv.BaseClient, error) {
+	initKVClient = func(_ context.Context, _, _, _, _ string) (*kv.BaseClient, error) {
 		return &kv.BaseClient{}, nil
 	}
 	provider, err := factory.Create("v1", config, "")
@@ -238,6 +239,26 @@ func TestGetKeys(t *testing.T) {
 	assert.Nil(t, keyStatus)
 }
 
+func TestIsRefreshable(t *testing.T) {
+	factory := &akvKMProviderFactory{}
+	config := config.KeyManagementProviderConfig{
+		"vaultUri": "https://testkv.vault.azure.net/",
+		"tenantID": "tid",
+		"clientID": "clientid",
+		"certificates": []map[string]interface{}{
+			{
+				"name":    "cert1",
+				"version": "",
+			},
+		},
+	}
+
+	provider, _ := factory.Create("v1", config, "")
+	if provider.IsRefreshable() != true {
+		t.Fatalf("expected true, got false")
+	}
+}
+
 // TestGetStatusMap tests the getStatusMap function
 func TestGetStatusMap(t *testing.T) {
 	certsStatus := []map[string]string{}
@@ -486,3 +507,38 @@ func TestValidate(t *testing.T) {
 		})
 	}
 }
+
+func TestInitializeKvClient(t *testing.T) {
+	tests := []struct {
+		name        string
+		kvEndpoint  string
+		userAgent   string
+		tenantID    string
+		clientID    string
+		expectedErr bool
+	}{
+		{
+			name:        "Empty user agent",
+			kvEndpoint:  "https://test.vault.azure.net",
+			userAgent:   "",
+			expectedErr: true,
+		},
+		{
+			name:        "Auth failure",
+			kvEndpoint:  "https://test.vault.azure.net",
+			userAgent:   version.UserAgent,
+			tenantID:    "testTenantID",
+			clientID:    "testClientID",
+			expectedErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := initializeKvClient(context.Background(), tt.kvEndpoint, tt.tenantID, tt.clientID, tt.userAgent)
+			if tt.expectedErr != (err != nil) {
+				t.Fatalf("expected error: %v, got: %v", tt.expectedErr, err)
+			}
+		})
+	}
+}
diff --git a/pkg/keymanagementprovider/inline/provider_test.go b/pkg/keymanagementprovider/inline/provider_test.go
index 68b4615..7cd56f6 100644
--- a/pkg/keymanagementprovider/inline/provider_test.go
+++ b/pkg/keymanagementprovider/inline/provider_test.go
@@ -116,3 +116,40 @@ func TestGetCertificates(t *testing.T) {
 		})
 	}
 }
+
+func TestGetKeys(t *testing.T) {
+	factory := &inlineKMProviderFactory{}
+	config := config.KeyManagementProviderConfig{
+		"type":        "inline",
+		"contentType": "key",
+		"value": `-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEozC27QupU+1GvAL0tqR7bT3Vpyyf
+OSeWVmPjy6J5x8+6OIpmTs8PKQB1vTF0gErwa1gS/QaOElLaxDKy0GS9Jg==
+-----END PUBLIC KEY-----`,
+	}
+
+	provider, err := factory.Create("v1.0", config, "")
+	if err != nil {
+		t.Fatalf("failed to create provider: %v", err)
+	}
+	keys, _, _ := provider.GetKeys(context.TODO())
+	if len(keys) != 1 {
+		t.Fatalf("expected 1 key, got %d", len(keys))
+	}
+}
+
+func TestIsRefreshable(t *testing.T) {
+	config := config.KeyManagementProviderConfig{
+		"type":        "inline",
+		"contentType": "certificate",
+		"value":       "-----BEGIN CERTIFICATE-----\nMIIEeDCCAmCgAwIBAgIUbztxbi/gSPMZGN53oZQZW1h/lbAwDQYJKoZIhvcNAQEL\nBQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdSZWRtb25k\nMRMwEQYDVQQKDApNeSBDb21wYW55MQ8wDQYDVQQLDAZNeSBPcmcxFzAVBgNVBAMM\nDmNhLmV4YW1wbGUuY29tMB4XDTIzMDIwMTIyNTMxMFoXDTI0MDIwMTIyNTMxMFow\nbTEZMBcGA1UEAxMQbGVhZi5leGFtcGxlLmNvbTEPMA0GA1UECxMGTXkgT3JnMRMw\nEQYDVQQKEwpNeSBDb21wYW55MRAwDgYDVQQHEwdSZWRtb25kMQswCQYDVQQIEwJX\nQTELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs\nMixf+WT34edYXs2c80zOg4Z/cxOVHU05gywjuISeaP+KS+Joc3emgbub1t5dPclk\nieIwrj3Olk3tvkrPiarOJIcrNR2zfBmQAufR4AUjoc4n1GQSp/voGgw1Hvh0wTkO\nYjhzLomrF242Ond8WTVO3Vq6/tfApfZMFM59eK9LMBkuvwTV4NeLnEnPvpLAoAvV\n9ZvCu7FuQ849R93Aoag2bZc3Tc3UCbahoJs9rTE/rnAqOhJWMGv2J1Y2Wu2eIvkD\n2uCmcVlY+7owG3TwLHTuIOBFl/5MXMvfR+B7yp1OkG23rTwwuSEBlMhYRzJFvssv\n8FX0sea7zhIg5dtoRjIlAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG\n9w0BAQsFAAOCAgEANRUu+9aBAuRf3OsdUWJflMAvuyzREp3skWSOUs4dw0MhcB6x\nb7BSyNdrBgPImLBpqYzNU6IT2eIlLXrYnKLehvPyZQx7LHvIeompA09aKMFFesqi\ngVoW5GRtp3qL3oNuuZJ80r/uKlB6Cj51TWqUbLcctBGHX7TWxFeWmFRnN0Bki00U\nJW/ElaTsr4GB+ltgZM+5USUqSNQqTa8t3d+vH6oVikyV1oYunM41xAfiRZtID04z\no15sLSkWTjavfmZ3+NjllipXFY2tnLqymCcObgdKtJHmTMFSDRngDjY+3+RVj4EY\npNaCCCepvtmXz1C5f06tlgY4ofaautJuAL7K93p/Q9ZcsIhmYWkCUZ0dkWq+eMdT\n9/lB9rQHbrDTaRxEQNIUezFMQEBxR9eC5JQfpw98LobAgA3r4vizQjQPsN0UZ54h\ncAHiyoo1VeckkotXaToRsoLjixPO9Fmss4H3urJTLpcU0drbVoG3emNh4K289vgR\nrjV11TenqvvR3+jJ2AX2ewSsF25m0afheZbrq2ZtyITPAbOqwMwTbTOvJT3HUztt\nhUP3qwsKNPR/hF3FSqZewiYOSqJi5Dk28Vd6mUEQzZa/Ma9RpBR+BAmfgH3Z9gX5\n0TqmAVQn1P8yh+bhEjiNa20bTJ+y5vQ9OrA7fiQ+6vpZCio4NFiEbYK4UBI=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFxzCCA6+gAwIBAgIUY1fnGcYJFIGNk8fevKdGtOuZ5vcwDQYJKoZIhvcNAQEL\nBQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdSZWRtb25k\nMRMwEQYDVQQKDApNeSBDb21wYW55MQ8wDQYDVQQLDAZNeSBPcmcxFzAVBgNVBAMM\nDmNhLmV4YW1wbGUuY29tMB4XDTIzMDIwMTIyNTIwN1oXDTMzMDEyOTIyNTIwN1ow\nazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdSZWRtb25kMRMw\nEQYDVQQKDApNeSBDb21wYW55MQ8wDQYDVQQLDAZNeSBPcmcxFzAVBgNVBAMMDmNh\nLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtQXd\n+qySVVMHx7iGz9xRdpDKb+zATK3asMFnMXWBn0MCkcOMJvhjajA8yrCpfMudjiW0\nReQ6xcjEfnJqkzwxrK7tPE9cQ6JzQGCxsmscRzKf1NoJL2ske5xBteKuJhSfZ9la\ncIZn/EU2F6eMAl9U4Y+ncIIrl4UoB5H/AJJj62WMl5QAvzXXwCBwlLHQe4/T3Axu\n9xmD3HTC7iQExOUFLdJx86fK3ym0futi1RgOUgD+OrnyDEIkD8mGxffPYPgszS71\nUuJX+NTsLZ/JW3ER/PMAPnBsMsMTTxEIGnrp1CXf2RnwQnDHVsxZFMgkLTS6dksT\nTGevnulTNtVvSKsZ7MpzE01j7zDie4V4dQJzBJMktbeVq9KRoPIEX0WcKpg8bGS8\nd5p5pr+Lu/NOv6ur+av8M649qCPwJAv5i2P6ggT4YMNtY0wMD2kjcHJ9/l2gYpZj\n3DG0Hy3Xo8uKUmTSC7iGhLsSjleNhJkKyh3RCsuMKB9juE4qeXPoaoPWBIuarbkq\neVVZJu2PlgN8UcxM/ym+9GNJIfNJ18WGWm+5P3IDvfBbJ39yvzZlG2czju4BUzYn\nlPOHA/Z18TxZhlPrPVnyKSVbeg7sW17yMUI2LCeaFIOYdIFvM09RaCyLIGrQwhpe\nkLh56xXk702oNHaLxyh/v5kz8EyxnpXIlDntis0CAwEAAaNjMGEwHQYDVR0OBBYE\nFCESfoahHMx7GyBdTBARen7mc37nMB8GA1UdIwQYMBaAFCESfoahHMx7GyBdTBAR\nen7mc37nMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\nDQEBCwUAA4ICAQBst1MbRJd3FLSY36qaNuDhGncvcXIcYqP2A/SXVhjuAhVOTsrE\nejDYBSkjfxCgoA3LZnQLTcbcPwRL5dTBEvqBqzbyArOjc+G3kbjOeuZYv09M8kUU\nYG1bVnxXV17GMk7zcBUnr1iwnp4E0pzB9gTv+Jv1oV+EtAHe5QOTOmW9mm2SUXbH\nmIya+KlzkIgVJJ8kiGOyXsr8i4wpyDXZf720tqTzPQFTf6rUXo9PhYzOWrrj8c7V\n+bmJurV3XkgvdOiwNase17wXUG9Ad8FhVYpUicq3Csfx5M87IXUIlx51AxaOQK3G\n3skyJyAm8R8pHzhcsEuVV7bGZlbFPZeWAHpbIEwpKHlLoN6qMINk2kEbcVahL2Mu\nXBUcvJdO2LbmEvfS+1imr32YbJ5Ufetru+G4mwAp6a0P6u5FU8lbE1ZoFHhflsVq\nErvOcRKhKjAim4iwIVGS55Xyx1IpF7YSTYpL89vlMmaJsssEoCQAkf+lxmC3eEuy\n2kBu3QB3cUHZXJK+01krC5MqeHcEVuc/fbNcTsCBp+RYXNRjYsp6IzGjqltUfInE\n3Tywg3P0ZLPO06WLNjNeAFZw6T8yV5gLcTJ1xc5pEf4UiY1uCf4NmDpeWhT+vvto\n2AxC/+7x7EkEfZnYiD6tcyHyY+iuroptws8lc5wRis859kydnq3vtxbXPQ==\n-----END CERTIFICATE-----\n",
+	}
+	factory := &inlineKMProviderFactory{}
+	provider, err := factory.Create("v1.0", config, "")
+	if err != nil {
+		t.Fatalf("failed to create provider: %v", err)
+	}
+	if provider.IsRefreshable() != false {
+		t.Fatalf("expected false")
+	}
+}
diff --git a/pkg/keymanagementprovider/keymanagementprovider_test.go b/pkg/keymanagementprovider/keymanagementprovider_test.go
index 6e6a55a..0441562 100644
--- a/pkg/keymanagementprovider/keymanagementprovider_test.go
+++ b/pkg/keymanagementprovider/keymanagementprovider_test.go
@@ -139,7 +139,7 @@ func TestDecodeCertificates_FailedX509ParseError(t *testing.T) {
 // TestSetCertificatesInMap checks if certificates are set in the map
 func TestSetCertificatesInMap(t *testing.T) {
 	certificatesMap.Delete("test")
-	SetCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
+	setCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
 	if _, ok := certificatesMap.Load("test"); !ok {
 		t.Fatalf("certificatesMap should have been set for key")
 	}
@@ -147,8 +147,8 @@ func TestSetCertificatesInMap(t *testing.T) {
 
 // TestGetCertificatesFromMap checks if certificates are fetched from the map
 func TestGetCertificatesFromMap(t *testing.T) {
-	certificatesMap.Delete("test")
-	SetCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
+	DeleteResourceFromMap("test")
+	setCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
 	certs, _ := GetCertificatesFromMap(context.Background(), "test")
 	if len(certs) != 1 {
 		t.Fatalf("certificates should have been fetched from the map")
@@ -157,18 +157,28 @@ func TestGetCertificatesFromMap(t *testing.T) {
 
 // TestGetCertificatesFromMap_FailedToFetch checks if certificates are fetched from the map
 func TestGetCertificatesFromMap_FailedToFetch(t *testing.T) {
-	certificatesMap.Delete("test")
+	DeleteResourceFromMap("test")
 	certs, _ := GetCertificatesFromMap(context.Background(), "test")
 	if len(certs) != 0 {
 		t.Fatalf("certificates should not have been fetched from the map")
 	}
 }
 
+// TestGetCertificatesFromMap_ErrorFromReconcile checks if error is returned from reconcile
+func TestGetCertificatesFromMap_ErrorFromReconcile(t *testing.T) {
+	DeleteResourceFromMap("test")
+	SetCertificateError("test", errors.New("test error"))
+	if _, err := GetCertificatesFromMap(context.Background(), "test"); err == nil {
+		t.Fatalf("expected error, but got nil")
+	}
+	DeleteResourceFromMap("test")
+}
+
 // TestDeleteCertificatesFromMap checks if certificates are deleted from the map
 func TestDeleteCertificatesFromMap(t *testing.T) {
 	certificatesMap.Delete("test")
-	SetCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
-	DeleteCertificatesFromMap("test")
+	setCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
+	DeleteResourceFromMap("test")
 	if _, ok := certificatesMap.Load("test"); ok {
 		t.Fatalf("certificatesMap should have been deleted for key")
 	}
@@ -185,20 +195,41 @@ func TestFlattenKMPMap(t *testing.T) {
 // TestSetKeysInMap checks if keys are set in the map
 func TestSetKeysInMap(t *testing.T) {
 	keyMap.Delete("test")
-	SetKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
+	setKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
 	if _, ok := keyMap.Load("test"); !ok {
 		t.Fatalf("keysMap should have been set for key")
 	}
 }
 
+// TestSaveSecrets checks if secrets are saved in the map.
+func TestSaveSecrets(t *testing.T) {
+	DeleteResourceFromMap("test")
+
+	SaveSecrets("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}}, map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
+	if _, ok := certificatesMap.Load("test"); !ok {
+		t.Fatalf("certificatesMap should have been set for key")
+	}
+	if _, ok := certificatesMap.Load("test"); !ok {
+		t.Fatalf("certificatesMap should have been set for key")
+	}
+
+	DeleteResourceFromMap("test")
+}
+
 // TestGetKeysFromMap checks if keys are fetched from the map
 func TestGetKeysFromMap(t *testing.T) {
-	keyMap.Delete("test")
-	SetKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
+	DeleteResourceFromMap("test")
+	setKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
 	keys, _ := GetKeysFromMap(context.Background(), "test")
 	if len(keys) != 1 {
 		t.Fatalf("keys should have been fetched from the map")
 	}
+
+	SetKeyError("test", errors.New("test error"))
+	if _, err := GetKeysFromMap(context.Background(), "test"); err == nil {
+		t.Fatalf("expected error, but got nil")
+	}
+	DeleteResourceFromMap("test")
 }
 
 // TestGetKeysFromMap_FailedToFetch checks if keys fail to fetch from map
@@ -222,8 +253,8 @@ func TestGetKeysFromMap_AccessDifferentNamespace_ReturnsFalse(t *testing.T) {
 // TestDeleteKeysFromMap checks if key map entry is deleted from the map
 func TestDeleteKeysFromMap(t *testing.T) {
 	keyMap.Delete("test")
-	SetKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
-	DeleteKeysFromMap("test")
+	setKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
+	DeleteResourceFromMap("test")
 	if _, ok := keyMap.Load("test"); ok {
 		t.Fatalf("keysMap should have been deleted for key")
 	}
diff --git a/pkg/keymanagementprovider/refresh/factory_test.go b/pkg/keymanagementprovider/refresh/factory_test.go
new file mode 100644
index 0000000..a4a267c
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/factory_test.go
@@ -0,0 +1,119 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package refresh
+
+import (
+	"context"
+	"testing"
+)
+
+type MockRefresher struct{}
+
+func (f *MockRefresher) Create(_ map[string]interface{}) (Refresher, error) {
+	return &MockRefresher{}, nil
+}
+
+func (f *MockRefresher) Refresh(_ context.Context) error {
+	return nil
+}
+
+func (f *MockRefresher) GetResult() interface{} {
+	return nil
+}
+
+func TestRefreshFactory_Create(t *testing.T) {
+	Register("mockRefresher", &MockRefresher{})
+	refresherConfig := map[string]interface{}{
+		"type": "mockRefresher",
+	}
+	factory := refresherFactories["mockRefresher"]
+	refresher, err := factory.Create(refresherConfig)
+	// refresher, err := CreateRefresherFromConfig(refresherConfig)
+	if _, ok := refresher.(*MockRefresher); !ok {
+		t.Errorf("Expected refresher to be of type MockRefresher, got %v", refresher)
+	}
+	if err != nil {
+		t.Errorf("Expected no error, got %v", err)
+	}
+}
+
+func TestRegister_InvalidFactory(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("Expected panic, got nil")
+		}
+	}()
+
+	Register("invalidRefresher", nil)
+}
+
+func TestRegister_DuplicateFactory(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("Expected panic, got nil")
+		}
+	}()
+
+	Register("duplicateRefresher", &MockRefresher{})
+	Register("duplicateRefresher", &MockRefresher{})
+}
+
+func TestRegister_ValidFactory(t *testing.T) {
+	refresherFactories = make(map[string]RefresherFactory)
+	Register("validRefresher", &MockRefresher{})
+	if len(refresherFactories) != 1 {
+		t.Errorf("Expected 1 factory to be registered, got %d", len(refresherFactories))
+	}
+}
+
+func TestCreateRefresherFromConfig(t *testing.T) {
+	Register("mockRefresher", &MockRefresher{})
+	tests := []struct {
+		name          string
+		refresherType string
+		expectedError bool
+	}{
+		{
+			name:          "Valid Refresher Type",
+			refresherType: "mockRefresher",
+			expectedError: false,
+		},
+		{
+			name:          "Invalid Refresher Type",
+			refresherType: "invalidRefresher",
+			expectedError: true,
+		},
+		{
+			name:          "Empty Refresher Type",
+			refresherType: "",
+			expectedError: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			refresherConfig := map[string]interface{}{
+				"type": tt.refresherType,
+			}
+			_, err := CreateRefresherFromConfig(refresherConfig)
+			if tt.expectedError && err == nil {
+				t.Errorf("Expected error, got nil")
+			}
+			if !tt.expectedError && err != nil {
+				t.Errorf("Expected no error, got %v", err)
+			}
+		})
+	}
+}
diff --git a/pkg/keymanagementprovider/refresh/kubeRefreshNamedspaced_test.go b/pkg/keymanagementprovider/refresh/kubeRefreshNamedspaced_test.go
new file mode 100644
index 0000000..884d94f
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/kubeRefreshNamedspaced_test.go
@@ -0,0 +1,376 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package refresh
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"testing"
+	"time"
+
+	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/mocks"
+	test "github.com/ratify-project/ratify/pkg/utils"
+	"github.com/sirupsen/logrus"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func TestKubeRefresherNamespaced_Refresh(t *testing.T) {
+	tests := []struct {
+		name           string
+		provider       *configv1beta1.NamespacedKeyManagementProvider
+		request        ctrl.Request
+		mockClient     bool
+		expectedResult ctrl.Result
+		expectedError  bool
+	}{
+		{
+			name: "Non-refreshable",
+			provider: &configv1beta1.NamespacedKeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
+					Type: "inline",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name: "Disabled",
+			provider: &configv1beta1.NamespacedKeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
+					Type:            "test-kmp",
+					RefreshInterval: "",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name: "Refreshable",
+			provider: &configv1beta1.NamespacedKeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
+					Type:            "test-kmp",
+					RefreshInterval: "1m",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{RequeueAfter: time.Minute},
+			expectedError:  false,
+		},
+		{
+			name: "Invalid Interval",
+			provider: &configv1beta1.NamespacedKeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
+					Type:            "",
+					RefreshInterval: "1mm",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  true,
+		},
+		{
+			name: "IsNotFound",
+			provider: &configv1beta1.NamespacedKeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.NamespacedKeyManagementProviderSpec{
+					Type:            "",
+					RefreshInterval: "",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name:          "UnableToFetchKMP",
+			mockClient:    true,
+			expectedError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var client client.Client
+
+			if tt.mockClient {
+				client = mocks.TestClient{}
+			} else {
+				scheme, _ := test.CreateScheme()
+				client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.provider).Build()
+			}
+
+			kr := &KubeRefresherNamespaced{
+				Client:  client,
+				Request: tt.request,
+			}
+			err := kr.Refresh(context.Background())
+			result := kr.GetResult()
+			if !reflect.DeepEqual(result, tt.expectedResult) {
+				t.Fatalf("Expected nil but got %v with error %v", result, err)
+			}
+			if tt.expectedError && err == nil {
+				t.Fatalf("Expected error but got nil")
+			}
+		})
+	}
+}
+
+func TestKubeRefresherNamespaced_Create(t *testing.T) {
+	tests := []struct {
+		name          string
+		config        map[string]interface{}
+		expectedError bool
+	}{
+		{
+			name: "Success",
+			config: map[string]interface{}{
+				"client":  &mocks.TestClient{},
+				"request": ctrl.Request{},
+			},
+			expectedError: false,
+		},
+		{
+			name: "ClientMissing",
+			config: map[string]interface{}{
+				"request": ctrl.Request{},
+			},
+			expectedError: true,
+		},
+		{
+			name: "RequestMissing",
+			config: map[string]interface{}{
+				"client": &mocks.TestClient{},
+			},
+			expectedError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			kr := &KubeRefresherNamespaced{}
+			_, err := kr.Create(tt.config)
+			if tt.expectedError && err == nil {
+				t.Fatalf("Expected error but got nil")
+			}
+		})
+	}
+}
+
+func TestKMProviderUpdateErrorStatusNamespaced(t *testing.T) {
+	var parametersString = "{\"certs\":{\"name\":\"certName\"}}"
+	var kmProviderStatus = []byte(parametersString)
+
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: true,
+		Properties: runtime.RawExtension{
+			Raw: kmProviderStatus,
+		},
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+	expectedErr := "it's a long error from unit test"
+	lastFetchedTime := metav1.Now()
+	updateKMProviderErrorStatusNamespaced(&keyManagementProvider, expectedErr, &lastFetchedTime)
+
+	if keyManagementProvider.Status.IsSuccess != false {
+		t.Fatalf("Unexpected error, expected isSuccess to be false , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != expectedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedErr, keyManagementProvider.Status.Error)
+	}
+	expectedBriedErr := fmt.Sprintf("%s...", expectedErr[:30])
+	if keyManagementProvider.Status.BriefError != expectedBriedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedBriedErr, keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was not overridden
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Unexpected properties,  expected %+v, got %+v", parametersString, string(keyManagementProvider.Status.Properties.Raw))
+	}
+}
+
+func TestKMProviderUpdateSuccessStatusNamespaced(t *testing.T) {
+	kmProviderStatus := keymanagementprovider.KeyManagementProviderStatus{}
+	properties := map[string]string{}
+	properties["Name"] = "wabbit"
+	properties["Version"] = "ABC"
+
+	kmProviderStatus["Certificates"] = properties
+
+	lastFetchedTime := metav1.Now()
+
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatusNamespaced(&keyManagementProvider, &lastFetchedTime, kmProviderStatus)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Properties should not be empty")
+	}
+}
+
+func TestKMProviderUpdateSuccessStatusNamespaced_emptyProperties(t *testing.T) {
+	lastFetchedTime := metav1.Now()
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatusNamespaced(&keyManagementProvider, &lastFetchedTime, nil)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) != 0 {
+		t.Fatalf("Properties should be empty")
+	}
+}
+
+func TestWriteKMProviderStatusNamespaced(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	lastFetchedTime := metav1.Now()
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		kmProvider *configv1beta1.NamespacedKeyManagementProvider
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			errString:  "",
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "status update failed",
+			isSuccess:  true,
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			reconciler: &test.MockStatusClient{
+				UpdateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeKMProviderStatusNamespaced(context.Background(), tc.reconciler, tc.kmProvider, logger, tc.isSuccess, tc.errString, lastFetchedTime, nil)
+
+			if tc.kmProvider.Status.IsSuccess != tc.isSuccess {
+				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.kmProvider.Status.IsSuccess)
+			}
+
+			if tc.kmProvider.Status.Error != tc.errString {
+				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.kmProvider.Status.Error)
+			}
+		})
+	}
+}
diff --git a/pkg/keymanagementprovider/refresh/kubeRefresh_test.go b/pkg/keymanagementprovider/refresh/kubeRefresh_test.go
new file mode 100644
index 0000000..beb5037
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/kubeRefresh_test.go
@@ -0,0 +1,380 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package refresh
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"testing"
+	"time"
+
+	configv1beta1 "github.com/ratify-project/ratify/api/v1beta1"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider"
+	_ "github.com/ratify-project/ratify/pkg/keymanagementprovider/inline"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/mocks"
+	test "github.com/ratify-project/ratify/pkg/utils"
+	"github.com/sirupsen/logrus"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func TestKubeRefresher_Refresh(t *testing.T) {
+	tests := []struct {
+		name           string
+		provider       *configv1beta1.KeyManagementProvider
+		request        ctrl.Request
+		mockClient     bool
+		expectedResult ctrl.Result
+		expectedError  bool
+	}{
+		{
+			name: "Non-refreshable",
+			provider: &configv1beta1.KeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.KeyManagementProviderSpec{
+					Type: "inline",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name: "Disabled",
+			provider: &configv1beta1.KeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.KeyManagementProviderSpec{
+					Type:            "test-kmp",
+					RefreshInterval: "",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name: "Refreshable",
+			provider: &configv1beta1.KeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.KeyManagementProviderSpec{
+					Type:            "test-kmp",
+					RefreshInterval: "1m",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{RequeueAfter: time.Minute},
+			expectedError:  false,
+		},
+		{
+			name: "Invalid Interval",
+			provider: &configv1beta1.KeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.KeyManagementProviderSpec{
+					Type:            "",
+					RefreshInterval: "1mm",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			request: ctrl.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  true,
+		},
+		{
+			name: "IsNotFound",
+			provider: &configv1beta1.KeyManagementProvider{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "",
+					Name:      "kmpName",
+				},
+				Spec: configv1beta1.KeyManagementProviderSpec{
+					Type:            "",
+					RefreshInterval: "",
+					Parameters: runtime.RawExtension{
+						Raw: []byte(`{"vaultURI": "https://yourkeyvault.vault.azure.net/", "certificates": [{"name": "cert1", "version": "1"}], "tenantID": "yourtenantID", "clientID": "yourclientID"}`),
+					},
+				},
+			},
+			expectedResult: ctrl.Result{},
+			expectedError:  false,
+		},
+		{
+			name:          "UnableToFetchKMP",
+			mockClient:    true,
+			expectedError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var client client.Client
+
+			if tt.mockClient {
+				client = mocks.TestClient{}
+			} else {
+				scheme, _ := test.CreateScheme()
+				client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.provider).Build()
+			}
+
+			kr := &KubeRefresher{
+				Client:  client,
+				Request: tt.request,
+			}
+			err := kr.Refresh(context.Background())
+			result := kr.GetResult()
+			if !reflect.DeepEqual(result, tt.expectedResult) {
+				t.Fatalf("Expected nil but got %v with error %v", result, err)
+			}
+			if tt.expectedError && err == nil {
+				t.Fatalf("Expected error but got nil")
+			}
+		})
+	}
+}
+
+func TestKubeRefresher_Create(t *testing.T) {
+	tests := []struct {
+		name          string
+		config        map[string]interface{}
+		expectedError bool
+	}{
+		{
+			name: "Success",
+			config: map[string]interface{}{
+				"client":  &mocks.TestClient{},
+				"request": ctrl.Request{},
+			},
+			expectedError: false,
+		},
+		{
+			name: "ClientMissing",
+			config: map[string]interface{}{
+				"request": ctrl.Request{},
+			},
+			expectedError: true,
+		},
+		{
+			name: "RequestMissing",
+			config: map[string]interface{}{
+				"client": &mocks.TestClient{},
+			},
+			expectedError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			kr := &KubeRefresher{}
+			_, err := kr.Create(tt.config)
+			if tt.expectedError && err == nil {
+				t.Fatalf("Expected error but got nil")
+			}
+		})
+	}
+}
+
+func TestKMProviderUpdateErrorStatus(t *testing.T) {
+	var parametersString = "{\"certs\":{\"name\":\"certName\"}}"
+	var kmProviderStatus = []byte(parametersString)
+
+	status := configv1beta1.KeyManagementProviderStatus{
+		IsSuccess: true,
+		Properties: runtime.RawExtension{
+			Raw: kmProviderStatus,
+		},
+	}
+	keyManagementProvider := configv1beta1.KeyManagementProvider{
+		Status: status,
+	}
+	expectedErr := "it's a long error from unit test"
+	lastFetchedTime := metav1.Now()
+	updateKMProviderErrorStatus(&keyManagementProvider, expectedErr, &lastFetchedTime)
+
+	if keyManagementProvider.Status.IsSuccess != false {
+		t.Fatalf("Unexpected error, expected isSuccess to be false , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != expectedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedErr, keyManagementProvider.Status.Error)
+	}
+	expectedBriedErr := fmt.Sprintf("%s...", expectedErr[:30])
+	if keyManagementProvider.Status.BriefError != expectedBriedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedBriedErr, keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was not overridden
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Unexpected properties,  expected %+v, got %+v", parametersString, string(keyManagementProvider.Status.Properties.Raw))
+	}
+}
+
+// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method
+func TestKMProviderUpdateSuccessStatus(t *testing.T) {
+	kmProviderStatus := keymanagementprovider.KeyManagementProviderStatus{}
+	properties := map[string]string{}
+	properties["Name"] = "wabbit"
+	properties["Version"] = "ABC"
+
+	kmProviderStatus["Certificates"] = properties
+
+	lastFetchedTime := metav1.Now()
+
+	status := configv1beta1.KeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.KeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, kmProviderStatus)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Properties should not be empty")
+	}
+}
+
+// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method with empty properties
+func TestKMProviderUpdateSuccessStatus_emptyProperties(t *testing.T) {
+	lastFetchedTime := metav1.Now()
+	status := configv1beta1.KeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.KeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, nil)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) != 0 {
+		t.Fatalf("Properties should be empty")
+	}
+}
+
+func TestWriteKMProviderStatus(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	lastFetchedTime := metav1.Now()
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		kmProvider *configv1beta1.KeyManagementProvider
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			errString:  "",
+			kmProvider: &configv1beta1.KeyManagementProvider{},
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			kmProvider: &configv1beta1.KeyManagementProvider{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "status update failed",
+			isSuccess:  true,
+			kmProvider: &configv1beta1.KeyManagementProvider{},
+			reconciler: &test.MockStatusClient{
+				UpdateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeKMProviderStatus(context.Background(), tc.reconciler, tc.kmProvider, logger, tc.isSuccess, tc.errString, lastFetchedTime, nil)
+
+			if tc.kmProvider.Status.IsSuccess != tc.isSuccess {
+				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.kmProvider.Status.IsSuccess)
+			}
+
+			if tc.kmProvider.Status.Error != tc.errString {
+				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.kmProvider.Status.Error)
+			}
+		})
+	}
+}
diff --git a/pkg/keymanagementprovider/refresh/test_helper_test.go b/pkg/keymanagementprovider/refresh/test_helper_test.go
new file mode 100644
index 0000000..0fd82a1
--- /dev/null
+++ b/pkg/keymanagementprovider/refresh/test_helper_test.go
@@ -0,0 +1,26 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package refresh
+
+import (
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/factory"
+	"github.com/ratify-project/ratify/pkg/keymanagementprovider/mocks"
+)
+
+func init() {
+	// Register the mock KeyManagementProviderFactory
+	factory.Register("test-kmp", &mocks.TestKeyManagementProviderFactory{})
+}
diff --git a/pkg/referrerstore/oras/oras_test.go b/pkg/referrerstore/oras/oras_test.go
index 677eaa4..818d9c7 100644
--- a/pkg/referrerstore/oras/oras_test.go
+++ b/pkg/referrerstore/oras/oras_test.go
@@ -40,7 +40,11 @@ import (
 	"oras.land/oras-go/v2/registry/remote/errcode"
 )
 
-const inputOriginalPath = "localhost:5000/net-monitor:v0"
+const (
+	inputOriginalPath       = "localhost:5000/net-monitor:v0"
+	wrongReferenceMediatype = "application/vnd.oci.image.manifest.wrong.v1+json"
+	validReferenceMediatype = "application/vnd.oci.image.manifest.right.v1+json"
+)
 
 // TestORASName tests the Name method of the oras store.
 func TestORASName(t *testing.T) {
@@ -197,14 +201,12 @@ func TestORASGetReferenceManifest_CachedDesc(t *testing.T) {
 	ctx := context.Background()
 	firstDigest := digest.FromString("testDigest")
 	artifactDigest := digest.FromString("testArtifactDigest")
-	expectedReferenceMediatype := "application/vnd.oci.image.manifest.right.v1+json"
-	wrongReferenceMediatype := "application/vnd.oci.image.manifest.wrong.v1+json"
 	store, err := createBaseStore("1.0.0", conf)
 	if err != nil {
 		t.Fatalf("failed to create oras store: %v", err)
 	}
 	manifestCached := oci.Manifest{
-		MediaType: expectedReferenceMediatype,
+		MediaType: validReferenceMediatype,
 		Config:    oci.Descriptor{},
 		Layers:    []oci.Descriptor{},
 	}
@@ -247,8 +249,8 @@ func TestORASGetReferenceManifest_CachedDesc(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to get reference manifest: %v", err)
 	}
-	if manifest.MediaType != expectedReferenceMediatype {
-		t.Fatalf("expected media type %s, got %s", expectedReferenceMediatype, manifest.MediaType)
+	if manifest.MediaType != validReferenceMediatype {
+		t.Fatalf("expected media type %s, got %s", validReferenceMediatype, manifest.MediaType)
 	}
 }
 
@@ -261,8 +263,6 @@ func TestORASGetReferenceManifest_NotCachedDesc(t *testing.T) {
 	firstDigest := digest.FromString("testDigest")
 	artifactDigest := digest.FromString("testArtifactDigest")
 	artifactDigestNotCached := digest.FromString("testArtifactDigestNotCached")
-	expectedReferenceMediatype := "application/vnd.oci.image.manifest.right.v1+json"
-	wrongReferenceMediatype := "application/vnd.oci.image.manifest.wrong.v1+json"
 	store, err := createBaseStore("1.0.0", conf)
 	if err != nil {
 		t.Fatalf("failed to create oras store: %v", err)
@@ -277,7 +277,7 @@ func TestORASGetReferenceManifest_NotCachedDesc(t *testing.T) {
 		t.Fatalf("failed to marshal cached manifest: %v", err)
 	}
 	manifestNotCached := oci.Manifest{
-		MediaType: expectedReferenceMediatype,
+		MediaType: validReferenceMediatype,
 		Config:    oci.Descriptor{},
 		Layers:    []oci.Descriptor{},
 	}
@@ -311,8 +311,156 @@ func TestORASGetReferenceManifest_NotCachedDesc(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to get reference manifest: %v", err)
 	}
-	if manifest.MediaType != expectedReferenceMediatype {
-		t.Fatalf("expected media type %s, got %s", expectedReferenceMediatype, manifest.MediaType)
+	if manifest.MediaType != validReferenceMediatype {
+		t.Fatalf("expected media type %s, got %s", validReferenceMediatype, manifest.MediaType)
+	}
+}
+
+func TestORASGetReferenceManifest_CacheFetchManifestFailure(t *testing.T) {
+	conf := config.StorePluginConfig{
+		"name": "oras",
+	}
+	ctx := context.Background()
+	firstDigest := digest.FromString("testDigest")
+	artifactDigest := digest.FromString("testArtifactDigest")
+	store, err := createBaseStore("1.0.0", conf)
+	if err != nil {
+		t.Fatalf("failed to create oras store: %v", err)
+	}
+	manifestCached := oci.Manifest{
+		MediaType: wrongReferenceMediatype,
+		Config:    oci.Descriptor{},
+		Layers:    []oci.Descriptor{},
+	}
+	manifestCachedBytes, err := json.Marshal(manifestCached)
+	if err != nil {
+		t.Fatalf("failed to marshal cached manifest: %v", err)
+	}
+	manifestNotCached := oci.Manifest{
+		MediaType: validReferenceMediatype,
+		Config:    oci.Descriptor{},
+		Layers:    []oci.Descriptor{},
+	}
+	manifestNotCachedBytes, err := json.Marshal(manifestNotCached)
+	if err != nil {
+		t.Fatalf("failed to marshal not cached manifest: %v", err)
+	}
+	testRepo := mocks.TestRepository{
+		FetchMap: map[digest.Digest]io.ReadCloser{
+			artifactDigest: io.NopCloser(bytes.NewReader(manifestNotCachedBytes)),
+		},
+	}
+	store.createRepository = func(_ context.Context, _ *orasStore, _ common.Reference) (registry.Repository, error) {
+		return testRepo, nil
+	}
+	store.localCache = mocks.TestStorage{
+		ExistsMap: map[digest.Digest]io.Reader{
+			artifactDigest: bytes.NewReader(manifestCachedBytes),
+		},
+		FetchErr: errors.New("cache fetch error"),
+	}
+	inputRef := common.Reference{
+		Original: inputOriginalPath,
+		Digest:   firstDigest,
+	}
+	manifest, err := store.GetReferenceManifest(ctx, inputRef, ocispecs.ReferenceDescriptor{
+		Descriptor: oci.Descriptor{
+			MediaType: ocispecs.MediaTypeArtifactManifest,
+			Digest:    artifactDigest,
+		},
+	})
+	if err != nil {
+		t.Fatalf("failed to get reference manifest: %v", err)
+	}
+	if manifest.MediaType != validReferenceMediatype {
+		t.Fatalf("expected media type %s, got %s", validReferenceMediatype, manifest.MediaType)
+	}
+}
+
+func TestORASGetReferenceManifest_CacheExistsFailure(t *testing.T) {
+	conf := config.StorePluginConfig{
+		"name": "oras",
+	}
+	ctx := context.Background()
+	firstDigest := digest.FromString("testDigest")
+	artifactDigest := digest.FromString("testArtifactDigest")
+	store, err := createBaseStore("1.0.0", conf)
+	if err != nil {
+		t.Fatalf("failed to create oras store: %v", err)
+	}
+	if err != nil {
+		t.Fatalf("failed to marshal cached manifest: %v", err)
+	}
+
+	testRepo := mocks.TestRepository{
+		FetchMap: map[digest.Digest]io.ReadCloser{},
+	}
+	store.createRepository = func(_ context.Context, _ *orasStore, _ common.Reference) (registry.Repository, error) {
+		return testRepo, nil
+	}
+	store.localCache = mocks.TestStorage{
+		ExistsErr: errors.New("cache exists error"),
+	}
+	inputRef := common.Reference{
+		Original: inputOriginalPath,
+		Digest:   firstDigest,
+	}
+	_, err = store.GetReferenceManifest(ctx, inputRef, ocispecs.ReferenceDescriptor{
+		Descriptor: oci.Descriptor{
+			MediaType: ocispecs.MediaTypeArtifactManifest,
+			Digest:    artifactDigest,
+		},
+	})
+	if err == nil {
+		t.Fatalf("expected error fetching reference manifest")
+	}
+}
+
+func TestORASGetReferenceManifest_NotCachedDescAndFetchFailed(t *testing.T) {
+	conf := config.StorePluginConfig{
+		"name": "oras",
+	}
+	ctx := context.Background()
+	firstDigest := digest.FromString("testDigest")
+	artifactDigest := digest.FromString("testArtifactDigest")
+	artifactDigestNotCached := digest.FromString("testArtifactDigestNotCached")
+	store, err := createBaseStore("1.0.0", conf)
+	if err != nil {
+		t.Fatalf("failed to create oras store: %v", err)
+	}
+	manifestCached := oci.Manifest{
+		MediaType: wrongReferenceMediatype,
+		Config:    oci.Descriptor{},
+		Layers:    []oci.Descriptor{},
+	}
+	manifestCachedBytes, err := json.Marshal(manifestCached)
+	if err != nil {
+		t.Fatalf("failed to marshal cached manifest: %v", err)
+	}
+
+	testRepo := mocks.TestRepository{
+		FetchMap: map[digest.Digest]io.ReadCloser{},
+	}
+	store.createRepository = func(_ context.Context, _ *orasStore, _ common.Reference) (registry.Repository, error) {
+		return testRepo, nil
+	}
+	store.localCache = mocks.TestStorage{
+		ExistsMap: map[digest.Digest]io.Reader{
+			artifactDigestNotCached: bytes.NewReader(manifestCachedBytes),
+		},
+	}
+	inputRef := common.Reference{
+		Original: inputOriginalPath,
+		Digest:   firstDigest,
+	}
+	_, err = store.GetReferenceManifest(ctx, inputRef, ocispecs.ReferenceDescriptor{
+		Descriptor: oci.Descriptor{
+			MediaType: ocispecs.MediaTypeArtifactManifest,
+			Digest:    artifactDigest,
+		},
+	})
+	if err == nil {
+		t.Fatalf("expected error fetching reference manifest")
 	}
 }
 
diff --git a/pkg/verifier/cosign/cosign_test.go b/pkg/verifier/cosign/cosign_test.go
index fde624a..b4e3686 100644
--- a/pkg/verifier/cosign/cosign_test.go
+++ b/pkg/verifier/cosign/cosign_test.go
@@ -410,6 +410,9 @@ func TestErrorToVerifyResult(t *testing.T) {
 	if verifierResult.Message != "Verification failed" {
 		t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.Message, "Verification failed")
 	}
+	if verifierResult.ErrorReason != "test error" {
+		t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.ErrorReason, "test error")
+	}
 }
 
 // TestDecodeASN1Signature tests the decodeASN1Signature function
diff --git a/pkg/verifier/cosign/trustpolicy_test.go b/pkg/verifier/cosign/trustpolicy_test.go
index 5cb10d2..ea720c5 100644
--- a/pkg/verifier/cosign/trustpolicy_test.go
+++ b/pkg/verifier/cosign/trustpolicy_test.go
@@ -19,6 +19,7 @@ import (
 	"context"
 	"crypto"
 	"crypto/ecdsa"
+	"crypto/x509"
 	"fmt"
 	"testing"
 
@@ -171,7 +172,7 @@ func TestGetKeys(t *testing.T) {
 	inputMap := map[keymanagementprovider.KMPMapKey]crypto.PublicKey{
 		{Name: "key1"}: &ecdsa.PublicKey{},
 	}
-	keymanagementprovider.SetKeysInMap("ns/kmp", "", inputMap)
+	keymanagementprovider.SaveSecrets("ns/kmp", "", inputMap, map[keymanagementprovider.KMPMapKey][]*x509.Certificate{})
 	tc := []struct {
 		name    string
 		cfg     TrustPolicyConfig
diff --git a/pkg/verifier/notation/notation_test.go b/pkg/verifier/notation/notation_test.go
index c320a82..3bbebe0 100644
--- a/pkg/verifier/notation/notation_test.go
+++ b/pkg/verifier/notation/notation_test.go
@@ -367,6 +367,26 @@ func TestVerify(t *testing.T) {
 			expect:    failedResult,
 			expectErr: true,
 		},
+		{
+			name:    "multiple signature blobs",
+			ref:     validRef2,
+			refBlob: testRefBlob2,
+			manifest: ocispecs.ReferenceManifest{
+				Blobs: []ocispec.Descriptor{validBlobDesc, validBlobDesc2},
+			},
+			expect:    failedResult,
+			expectErr: true,
+		},
+		{
+			name:    "get blob content failed",
+			ref:     validRef,
+			refBlob: nil,
+			manifest: ocispecs.ReferenceManifest{
+				Blobs: []ocispec.Descriptor{validBlobDesc},
+			},
+			expect:    failedResult,
+			expectErr: true,
+		},
 		{
 			name:    "verified successfully",
 			ref:     validRef,
diff --git a/pkg/verifier/notation/truststore_test.go b/pkg/verifier/notation/truststore_test.go
index 15bbea4..393103a 100644
--- a/pkg/verifier/notation/truststore_test.go
+++ b/pkg/verifier/notation/truststore_test.go
@@ -17,11 +17,13 @@ import (
 	"context"
 	"crypto/x509"
 	"encoding/pem"
+	"errors"
 	"os"
 	"reflect"
 	"testing"
 
 	"github.com/notaryproject/notation-go/verifier/truststore"
+	re "github.com/ratify-project/ratify/errors"
 	"github.com/ratify-project/ratify/pkg/controllers"
 )
 
@@ -33,10 +35,14 @@ const (
 )
 
 type mockCertStores struct {
-	certMap map[string][]*x509.Certificate
+	certMap      map[string][]*x509.Certificate
+	reconcileErr error
 }
 
 func (m *mockCertStores) GetCertsFromStore(_ context.Context, storeName string) ([]*x509.Certificate, error) {
+	if m.reconcileErr != nil {
+		return nil, m.reconcileErr
+	}
 	if m.certMap == nil {
 		return nil, nil
 	}
@@ -47,6 +53,59 @@ func (m *mockCertStores) AddStore(_ string, _ []*x509.Certificate) {}
 
 func (m *mockCertStores) DeleteStore(_ string) {}
 
+func (m *mockCertStores) AddStoreError(_ string, _ error) {}
+
+func TestIsReconcileError(t *testing.T) {
+	err := errors.New("reconcile error")
+	if !isReconcileError(err) {
+		t.Fatalf("expected to be a reconcile error")
+	}
+
+	err = re.ErrorCodeNotFound.WithDetail("not found")
+	if isReconcileError(err) {
+		t.Fatalf("expected not to be a reconcile error")
+	}
+}
+
+func TestParseErrFromKmpAndCertStore(t *testing.T) {
+	reconcileErr := errors.New("reconcile error")
+	notFoundErr := re.ErrorCodeNotFound.WithDetail("not found")
+	tests := []struct {
+		name         string
+		kmpErr       error
+		certStoreErr error
+		expectedErr  error
+	}{
+		{
+			name:         "nil errors",
+			kmpErr:       nil,
+			certStoreErr: nil,
+			expectedErr:  nil,
+		},
+		{
+			name:         "kmpErr should be returned",
+			kmpErr:       reconcileErr,
+			certStoreErr: notFoundErr,
+			expectedErr:  reconcileErr,
+		},
+		{
+			name:         "certStoreErr should be returned",
+			kmpErr:       notFoundErr,
+			certStoreErr: reconcileErr,
+			expectedErr:  reconcileErr,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := parseErrFromKmpAndCertStore(tt.kmpErr, tt.certStoreErr)
+			if !errors.Is(result, tt.expectedErr) {
+				t.Fatalf("expected %+v, got %+v", tt.expectedErr, result)
+			}
+		})
+	}
+}
+
 func TestGetCertificates_EmptyCertMap(t *testing.T) {
 	resetCertStore()
 	certStore := verificationCertStores{
@@ -64,6 +123,29 @@ func TestGetCertificates_EmptyCertMap(t *testing.T) {
 	}
 }
 
+func TestGetCertificates_ErrorFromKMPReconcile(t *testing.T) {
+	resetCertStore()
+	certStore := verificationCertStores{
+		trustStoreTypeCA: verificationCertStores{
+			"certstore1": []interface{}{"default/kv1"},
+			"certstore2": []interface{}{"projecta/kv2"},
+		},
+	}
+	store, err := newTrustStore(nil, certStore)
+	if err != nil {
+		t.Fatalf("failed to parse verificationCertStores: " + err.Error())
+	}
+
+	controllers.NamespacedCertStores = &mockCertStores{
+		reconcileErr: errors.New("reconcile error"),
+	}
+
+	// only the certificate in the specified namedStore should be returned
+	if _, err := store.getCertificatesInternal(context.Background(), truststore.TypeCA, "certstore1"); err == nil {
+		t.Fatalf("error expected if error from KMP reconcile")
+	}
+}
+
 func TestGetCertificates_NamedStore(t *testing.T) {
 	resetCertStore()
 	certStore := verificationCertStores{
diff --git a/pkg/verifier/result_test.go b/pkg/verifier/result_test.go
new file mode 100644
index 0000000..64efd2c
--- /dev/null
+++ b/pkg/verifier/result_test.go
@@ -0,0 +1,83 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package verifier
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/ratify-project/ratify/errors"
+)
+
+const (
+	testMsg1        = "test message 1"
+	testMsg2        = "test message 2"
+	testErrReason   = "test error reason"
+	testRemediation = "test remediation"
+)
+
+func TestNewVerifierResult(t *testing.T) {
+	tests := []struct {
+		name                string
+		message             string
+		err                 errors.Error
+		expectedMsg         string
+		expectedErrReason   string
+		expectedRemediation string
+	}{
+		{
+			name:        "nil error",
+			message:     testMsg1,
+			err:         errors.Error{},
+			expectedMsg: testMsg1,
+		},
+		{
+			name:                "error without detail",
+			message:             testMsg1,
+			err:                 errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation),
+			expectedMsg:         testMsg1,
+			expectedErrReason:   testErrReason,
+			expectedRemediation: testRemediation,
+		},
+		{
+			name:                "error with detail",
+			message:             testMsg1,
+			err:                 errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation).WithDetail(testMsg2),
+			expectedMsg:         testMsg2,
+			expectedErrReason:   testErrReason,
+			expectedRemediation: testRemediation,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := &tt.err
+			if tt.err == (errors.Error{}) {
+				err = nil
+			}
+
+			result := NewVerifierResult("", "", "", tt.message, false, err, nil)
+			if result.Message != tt.expectedMsg {
+				t.Errorf("expected message %s, got %s", tt.expectedMsg, result.Message)
+			}
+			if result.ErrorReason != tt.expectedErrReason {
+				t.Errorf("expected error reason %s, got %s", tt.expectedErrReason, result.ErrorReason)
+			}
+			if result.Remediation != tt.expectedRemediation {
+				t.Errorf("expected remediation %s, got %s", tt.expectedRemediation, result.Remediation)
+			}
+		})
+	}
+}
diff --git a/pkg/verifier/types/types_test.go b/pkg/verifier/types/types_test.go
new file mode 100644
index 0000000..ce1cd39
--- /dev/null
+++ b/pkg/verifier/types/types_test.go
@@ -0,0 +1,83 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package types
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/ratify-project/ratify/errors"
+)
+
+const (
+	testMsg1        = "test message 1"
+	testMsg2        = "test message 2"
+	testErrReason   = "test error reason"
+	testRemediation = "test remediation"
+)
+
+func TestCreateVerifierResult(t *testing.T) {
+	tests := []struct {
+		name                string
+		message             string
+		err                 errors.Error
+		expectedMsg         string
+		expectedErrReason   string
+		expectedRemediation string
+	}{
+		{
+			name:        "nil error",
+			message:     testMsg1,
+			err:         errors.Error{},
+			expectedMsg: testMsg1,
+		},
+		{
+			name:                "error without detail",
+			message:             testMsg1,
+			err:                 errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation),
+			expectedMsg:         testMsg1,
+			expectedErrReason:   testErrReason,
+			expectedRemediation: testRemediation,
+		},
+		{
+			name:                "error with detail",
+			message:             testMsg1,
+			err:                 errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation).WithDetail(testMsg2),
+			expectedMsg:         testMsg2,
+			expectedErrReason:   testErrReason,
+			expectedRemediation: testRemediation,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := &tt.err
+			if tt.err == (errors.Error{}) {
+				err = nil
+			}
+
+			result := CreateVerifierResult("", "", tt.message, false, err)
+			if result.Message != tt.expectedMsg {
+				t.Errorf("expected message %s, got %s", tt.expectedMsg, result.Message)
+			}
+			if result.ErrorReason != tt.expectedErrReason {
+				t.Errorf("expected error reason %s, got %s", tt.expectedErrReason, result.ErrorReason)
+			}
+			if result.Remediation != tt.expectedRemediation {
+				t.Errorf("expected remediation %s, got %s", tt.expectedRemediation, result.Remediation)
+			}
+		})
+	}
+}
diff --git a/plugins/verifier/sbom/sbom_test.go b/plugins/verifier/sbom/sbom_test.go
index 8dc5a4c..8609fc5 100644
--- a/plugins/verifier/sbom/sbom_test.go
+++ b/plugins/verifier/sbom/sbom_test.go
@@ -15,14 +15,24 @@ limitations under the License.
 package main
 
 import (
+	"errors"
+	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
 	"testing"
 
+	"github.com/opencontainers/go-digest"
+	oci "github.com/opencontainers/image-spec/specs-go/v1"
+	"github.com/ratify-project/ratify/pkg/common"
+	"github.com/ratify-project/ratify/pkg/ocispecs"
+	"github.com/ratify-project/ratify/pkg/referrerstore/mocks"
+	"github.com/ratify-project/ratify/pkg/verifier/plugin/skel"
 	"github.com/ratify-project/ratify/plugins/verifier/sbom/utils"
 )
 
+const mediaType string = "application/vnd.syft+json"
+
 func TestProcessSPDXJsonMediaType(t *testing.T) {
 	b, err := os.ReadFile(filepath.Join("testdata", "bom.json"))
 	if err != nil {
@@ -41,8 +51,178 @@ func TestProcessInvalidSPDXJsonMediaType(t *testing.T) {
 	}
 	report := processSpdxJSONMediaType("test", "", b, nil, nil)
 
-	if !strings.Contains(report.Message, "SBOM failed to parse") {
-		t.Fatalf("expected to have an error processing spdx json file: %s", filepath.Join("testdata", "bom.json"))
+	if !strings.Contains(report.Message, "failed to verify artifact") {
+		t.Fatalf("report message: %s does not contain expected error message", report.Message)
+	}
+	if report.ErrorReason != "JSON document does not contain spdxVersion field" {
+		t.Fatalf("expected error reason: %s, got: %s", "JSON document does not contain spdxVersion field", report.ErrorReason)
+	}
+}
+
+func TestVerifyReference(t *testing.T) {
+	manifestDigest := digest.FromString("test_manifest_digest")
+	manifestDigest2 := digest.FromString("test_manifest_digest_2")
+	blobDigest := digest.FromString("test_blob_digest")
+	blobDigest2 := digest.FromString("test_blob_digest_2")
+	type args struct {
+		stdinData         string
+		referenceManifest ocispecs.ReferenceManifest
+		blobContent       string
+		refDesc           ocispecs.ReferenceDescriptor
+	}
+	type want struct {
+		message     string
+		errorReason string
+		err         error
+	}
+
+	tests := []struct {
+		name string
+		args args
+		want want
+	}{
+		{
+			name: "invalid stdin data",
+			args: args{},
+			want: want{
+				err: errors.New("failed to parse stdin for the input: unexpected end of JSON input"),
+			},
+		},
+		{
+			name: "failed to get reference manifest",
+			args: args{
+				stdinData:         `{"config":{"name":"sbom","type":"sbom"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest2,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				message:     "Failed to fetch reference manifest for subject: test_subject reference descriptor: { sha256:b55e209647d87fcd95a94c59ff4d342e42bf10f02a7c10b5192131f8d959ff5a 0 [] map[] [] <nil> }",
+				errorReason: "manifest not found",
+			},
+		},
+		{
+			name: "empty blobs",
+			args: args{
+				stdinData:         `{"config":{"name":"sbom","type":"sbom"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				message:     "SBOM validation failed",
+				errorReason: fmt.Sprintf("No layers found in manifest for referrer %s@%s", "test_subject_path", manifestDigest.String()),
+			},
+		},
+		{
+			name: "get blob content error",
+			args: args{
+				stdinData: `{"config":{"name":"sbom","type":"sbom"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{
+					Blobs: []oci.Descriptor{
+						{
+							MediaType: mediaType,
+							Digest:    blobDigest2,
+						},
+					},
+				},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				message:     fmt.Sprintf("Failed to fetch blob for subject: test_subject digest: %s", blobDigest2.String()),
+				errorReason: "blob not found",
+			},
+		},
+		{
+			name: "unsupported artifactType",
+			args: args{
+				stdinData: `{"config":{"name":"sbom","type":"sbom"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{
+					Blobs: []oci.Descriptor{
+						{
+							MediaType: mediaType,
+							Digest:    blobDigest,
+						},
+					},
+				},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				message:     "Failed to process SBOM blobs.",
+				errorReason: "Unsupported artifactType: application/vnd.syft+json",
+			},
+		},
+		{
+			name: "process spdx json mediaType error",
+			args: args{
+				stdinData: `{"config":{"name":"sbom","type":"sbom"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{
+					Blobs: []oci.Descriptor{
+						{
+							MediaType: mediaType,
+							Digest:    blobDigest,
+						},
+					},
+				},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: SpdxJSONMediaType,
+				},
+			},
+			want: want{
+				message:     "failed to verify artifact: sbom",
+				errorReason: "unexpected end of JSON input",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cmdArgs := &skel.CmdArgs{
+				Version:   "1.0.0",
+				Subject:   "test_subject",
+				StdinData: []byte(tt.args.stdinData),
+			}
+			testStore := &mocks.MemoryTestStore{
+				Manifests: map[digest.Digest]ocispecs.ReferenceManifest{manifestDigest: tt.args.referenceManifest},
+				Blobs:     map[digest.Digest][]byte{blobDigest: []byte(tt.args.blobContent)},
+			}
+			subjectRef := common.Reference{
+				Path:     "test_subject_path",
+				Original: "test_subject",
+			}
+			verifierResult, err := VerifyReference(cmdArgs, subjectRef, tt.args.refDesc, testStore)
+			if err != nil && err.Error() != tt.want.err.Error() {
+				t.Fatalf("verifyReference() error = %v, wantErr %v", err, tt.want.err)
+			}
+			if verifierResult != nil {
+				if verifierResult.Message != tt.want.message {
+					t.Fatalf("verifyReference() verifier report message = %s, want = %s", verifierResult.Message, tt.want.message)
+				}
+				if verifierResult.ErrorReason != tt.want.errorReason {
+					t.Fatalf("verifyReference() verifier report error reason = %s, want = %s", verifierResult.ErrorReason, tt.want.errorReason)
+				}
+			}
+		})
 	}
 }
 
diff --git a/plugins/verifier/schemavalidator/schema_validator_test.go b/plugins/verifier/schemavalidator/schema_validator_test.go
new file mode 100644
index 0000000..3448ec8
--- /dev/null
+++ b/plugins/verifier/schemavalidator/schema_validator_test.go
@@ -0,0 +1,170 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+
+	"github.com/opencontainers/go-digest"
+	oci "github.com/opencontainers/image-spec/specs-go/v1"
+	"github.com/ratify-project/ratify/pkg/common"
+	"github.com/ratify-project/ratify/pkg/ocispecs"
+	"github.com/ratify-project/ratify/pkg/referrerstore/mocks"
+	"github.com/ratify-project/ratify/pkg/verifier/plugin/skel"
+)
+
+const mediaType string = "application/schema+json"
+
+func TestVerifyReference(t *testing.T) {
+	manifestDigest := digest.FromString("test_manifest_digest")
+	manifestDigest2 := digest.FromString("test_manifest_digest_2")
+	blobDigest := digest.FromString("test_blob_digest")
+	blobDigest2 := digest.FromString("test_blob_digest_2")
+	type args struct {
+		stdinData         string
+		referenceManifest ocispecs.ReferenceManifest
+		blobContent       string
+		refDesc           ocispecs.ReferenceDescriptor
+	}
+	type want struct {
+		message     string
+		errorReason string
+		err         error
+	}
+	tests := []struct {
+		name string
+		args args
+		want want
+	}{
+		{
+			name: "invalid stdin data",
+			args: args{},
+			want: want{
+				err: errors.New("failed to parse stdin for the input: unexpected end of JSON input"),
+			},
+		},
+		{
+			name: "failed to get reference manifest",
+			args: args{
+				stdinData:         `{"config":{"name":"schemavalidator","type":"schemavalidator"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest2,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				err: errors.New("error fetching reference manifest for subject: test_subject reference descriptor: { sha256:b55e209647d87fcd95a94c59ff4d342e42bf10f02a7c10b5192131f8d959ff5a 0 [] map[] [] <nil> }"),
+			},
+		},
+		{
+			name: "empty blobs",
+			args: args{
+				stdinData:         `{"config":{"name":"schemavalidator","type":"schemavalidator"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				errorReason: fmt.Sprintf("No blobs found for referrer %s@%s.", "test_subject_path", manifestDigest.String()),
+			},
+		},
+		{
+			name: "get blob content error",
+			args: args{
+				stdinData: `{"config":{"name":"schemavalidator","type":"schemavalidator"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{
+					Blobs: []oci.Descriptor{
+						{
+							MediaType: mediaType,
+							Digest:    blobDigest2,
+						},
+					},
+				},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				err: fmt.Errorf("error fetching blob for subject:[%s] digest:[%s]", "test_subject", blobDigest2.String()),
+			},
+		},
+		{
+			name: "process mediaType error",
+			args: args{
+				stdinData: `{"config":{"name":"schemavalidator","type":"schemavalidator"}}`,
+				referenceManifest: ocispecs.ReferenceManifest{
+					Blobs: []oci.Descriptor{
+						{
+							MediaType: mediaType,
+							Digest:    blobDigest,
+						},
+					},
+				},
+				refDesc: ocispecs.ReferenceDescriptor{
+					Descriptor: oci.Descriptor{
+						Digest: manifestDigest,
+					},
+					ArtifactType: mediaType,
+				},
+			},
+			want: want{
+				message:     fmt.Sprintf("schema validation failed for digest:[%s], media type:[%s].", blobDigest.String(), mediaType),
+				errorReason: "media type not configured for plugin:[application/schema+json]",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cmdArgs := &skel.CmdArgs{
+				Version:   "1.0.0",
+				Subject:   "test_subject",
+				StdinData: []byte(tt.args.stdinData),
+			}
+			testStore := &mocks.MemoryTestStore{
+				Manifests: map[digest.Digest]ocispecs.ReferenceManifest{manifestDigest: tt.args.referenceManifest},
+				Blobs:     map[digest.Digest][]byte{blobDigest: []byte(tt.args.blobContent)},
+			}
+			subjectRef := common.Reference{
+				Path:     "test_subject_path",
+				Original: "test_subject",
+			}
+			verifierResult, err := VerifyReference(cmdArgs, subjectRef, tt.args.refDesc, testStore)
+			if err != nil && err.Error() != tt.want.err.Error() {
+				t.Fatalf("verifyReference() error = %v, wantErr %v", err, tt.want.err)
+			}
+			if verifierResult != nil {
+				if verifierResult.Message != tt.want.message {
+					t.Fatalf("verifyReference() verifier report message = %s, want = %s", verifierResult.Message, tt.want.message)
+				}
+				if verifierResult.ErrorReason != tt.want.errorReason {
+					t.Fatalf("verifyReference() verifier report error reason = %s, want = %s", verifierResult.ErrorReason, tt.want.errorReason)
+				}
+			}
+		})
+	}
+}
diff --git a/plugins/verifier/vulnerabilityreport/vulnerability_report_test.go b/plugins/verifier/vulnerabilityreport/vulnerability_report_test.go
index e6341bf..0823d2a 100644
--- a/plugins/verifier/vulnerabilityreport/vulnerability_report_test.go
+++ b/plugins/verifier/vulnerabilityreport/vulnerability_report_test.go
@@ -76,8 +76,9 @@ func TestVerifyReference(t *testing.T) {
 		blobContent       string
 	}
 	type want struct {
-		message string
-		err     error
+		message     string
+		errorReason string
+		err         error
 	}
 	tests := []struct {
 		name string
@@ -103,7 +104,8 @@ func TestVerifyReference(t *testing.T) {
 				blobContent:       sampleSarifReport,
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: error extracting create timestamp annotation:[%s]", "no annotations found for descriptor:[{{ sha256:b2f67b016d3c646f025099b363b4f83a56a44d067a846be74e8866342c56f216 0 [] map[] [] <nil> } application/sarif+json}]"),
+				message:     "Failed to create timestamp annotation.",
+				errorReason: "no annotations found for descriptor:[{{ sha256:b2f67b016d3c646f025099b363b4f83a56a44d067a846be74e8866342c56f216 0 [] map[] [] <nil> } application/sarif+json}]",
 			},
 		},
 		{
@@ -118,7 +120,8 @@ func TestVerifyReference(t *testing.T) {
 				blobContent: sampleSarifReport,
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: error validating maximum age:[%s]", "error parsing maximum age:[1d]"),
+				message:     "Failed to validate maximum age.",
+				errorReason: "error parsing maximum age:[1d]",
 			},
 		},
 		{
@@ -133,7 +136,7 @@ func TestVerifyReference(t *testing.T) {
 				blobContent: sampleSarifReport,
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: report is older than maximum age:[%s]", "24h"),
+				errorReason: "Report is older than maximum age:[24h].",
 			},
 		},
 		{
@@ -148,7 +151,7 @@ func TestVerifyReference(t *testing.T) {
 				blobContent: sampleSarifReport,
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: no layers found in manifest for referrer %s@%s", "test_subject_path", manifestDigest.String()),
+				errorReason: fmt.Sprintf("No layers found in manifest for referrer %s@%s.", "test_subject_path", manifestDigest.String()),
 			},
 		},
 		{
@@ -189,7 +192,8 @@ func TestVerifyReference(t *testing.T) {
 				blobContent: "{}",
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: schema validation failed for digest:[%s],artifact type:[%s],parse errors:[%v]", blobDigest, SarifArtifactType, "version is required: runs is required: "),
+				message:     fmt.Sprintf("Schema validation failed for digest:[%s],artifact type:[%s].", blobDigest, SarifArtifactType),
+				errorReason: "version is required: runs is required: ",
 			},
 		},
 		{
@@ -238,12 +242,15 @@ func TestVerifyReference(t *testing.T) {
 			}
 			verifierResult, err := VerifyReference(&cmdArgs, subjectRef, refDesc, testStore)
 			if err != nil && err.Error() != tt.want.err.Error() {
-				t.Errorf("verifyReference() error = %v, wantErr %v", err, tt.want.err)
-				return
+				t.Fatalf("verifyReference() error = %v, wantErr %v", err, tt.want.err)
 			}
-			if verifierResult != nil && verifierResult.Message != tt.want.message {
-				t.Errorf("verifyReference() verifier report message = %s, want %s", verifierResult.Message, tt.want.message)
-				return
+			if verifierResult != nil {
+				if verifierResult.Message != tt.want.message {
+					t.Fatalf("verifyReference() verifier report message = %s, want = %s", verifierResult.Message, tt.want.message)
+				}
+				if verifierResult.ErrorReason != tt.want.errorReason {
+					t.Fatalf("verifyReference() verifier report error reason = %s, want = %s", verifierResult.ErrorReason, tt.want.errorReason)
+				}
 			}
 		})
 	}
@@ -313,8 +320,9 @@ func TestProcessSarifReport(t *testing.T) {
 		blobContent string
 	}
 	type want struct {
-		message string
-		err     error
+		message     string
+		errorReason string
+		err         error
 	}
 	tests := []struct {
 		name string
@@ -328,8 +336,9 @@ func TestProcessSarifReport(t *testing.T) {
 				blobContent: "invalid",
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: error parsing sarif report:[%s]", "invalid character 'i' looking for beginning of value"),
-				err:     nil,
+				message:     "Failed to parse sarif report.",
+				errorReason: "invalid character 'i' looking for beginning of value",
+				err:         nil,
 			},
 		},
 		{
@@ -343,7 +352,7 @@ func TestProcessSarifReport(t *testing.T) {
 }`,
 			},
 			want: want{
-				message: "Validation failed: no runs found in sarif report",
+				message: "No runs found in sarif report.",
 				err:     nil,
 			},
 		},
@@ -357,7 +366,7 @@ func TestProcessSarifReport(t *testing.T) {
 				blobContent: sampleSarifReport,
 			},
 			want: want{
-				message: "Validation failed: found denied CVEs. See extensions field for details.",
+				message: "Found denied CVEs. See extensions field for details.",
 				err:     nil,
 			},
 		},
@@ -374,7 +383,7 @@ func TestProcessSarifReport(t *testing.T) {
 				blobContent: sampleSarifReport,
 			},
 			want: want{
-				message: "Validation failed: found disallowed severities. See extensions field for details.",
+				message: "Found disallowed severities. See extensions field for details.",
 				err:     nil,
 			},
 		},
@@ -407,6 +416,9 @@ func TestProcessSarifReport(t *testing.T) {
 				t.Errorf("processSarifReport() verifier report message = %s, want %s", verifierReport.Message, tt.want.message)
 				return
 			}
+			if verifierReport.ErrorReason != tt.want.errorReason {
+				t.Errorf("processSarifReport() verifier report error reason = %s, want %s", verifierReport.ErrorReason, tt.want.errorReason)
+			}
 		})
 	}
 }
@@ -419,8 +431,9 @@ func TestVerifyDenyListCVEs(t *testing.T) {
 		sarifReport  sarif.Report
 	}
 	type want struct {
-		message string
-		err     error
+		message     string
+		errorReason string
+		err         error
 	}
 	tests := []struct {
 		name string
@@ -452,7 +465,7 @@ func TestVerifyDenyListCVEs(t *testing.T) {
 				},
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: rule id not found for result:[%v]", &sarif.Result{}),
+				message: fmt.Sprintf("Rule id not found for result:[%v].", &sarif.Result{}),
 				err:     nil,
 			},
 		},
@@ -483,7 +496,7 @@ func TestVerifyDenyListCVEs(t *testing.T) {
 				},
 			},
 			want: want{
-				message: "Validation failed: found denied CVEs. See extensions field for details.",
+				message: "Found denied CVEs. See extensions field for details.",
 				err:     nil,
 			},
 		},
@@ -523,12 +536,13 @@ func TestVerifyDenyListCVEs(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			verifierReport, err := verifyDenyListCVEs("test_verifier", "", TrivyScannerName, &tests[i].args.sarifReport, tt.args.denyListCVEs, time.Now())
 			if err != nil && err.Error() != tt.want.err.Error() {
-				t.Errorf("verifyDenyListCVEs() error = %v, wantErr %v", err, tt.want.err)
-				return
+				t.Fatalf("verifyDenyListCVEs() error = %v, wantErr %v", err, tt.want.err)
 			}
 			if verifierReport.Message != tt.want.message {
-				t.Errorf("verifyDenyListCVEs() verifier report message = %s, want %s", verifierReport.Message, tt.want.message)
-				return
+				t.Fatalf("verifyDenyListCVEs() verifier report message = %s, want %s", verifierReport.Message, tt.want.message)
+			}
+			if verifierReport.ErrorReason != tt.want.errorReason {
+				t.Fatalf("verifyDenyListCVEs() verifier report error reaon = %s, want = %s", verifierReport.ErrorReason, tt.want.errorReason)
 			}
 		})
 	}
@@ -545,8 +559,9 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 		sarifReport          sarif.Report
 	}
 	type want struct {
-		message string
-		err     error
+		message     string
+		errorReason string
+		err         error
 	}
 	tests := []struct {
 		name string
@@ -582,7 +597,7 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 				},
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: rule id not found for result:[%v]", &sarif.Result{}),
+				message: fmt.Sprintf("Rule id not found for result:[%v].", &sarif.Result{}),
 				err:     nil,
 			},
 		},
@@ -617,7 +632,7 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 				},
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: rule not found for result:[%v]", &sarif.Result{RuleID: &invalidRuleID}),
+				message: fmt.Sprintf("Rule not found for result:[%v].", &sarif.Result{RuleID: &invalidRuleID}),
 				err:     nil,
 			},
 		},
@@ -652,8 +667,9 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 				},
 			},
 			want: want{
-				message: fmt.Sprintf("Validation failed: error extracting severity:[severity not found in help text:[%s]]", invalidSeverityText),
-				err:     nil,
+				message:     "Failed to extract severity.",
+				errorReason: fmt.Sprintf("severity not found in help text:[%s]", invalidSeverityText),
+				err:         nil,
 			},
 		},
 		{
@@ -687,7 +703,7 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 				},
 			},
 			want: want{
-				message: "Validation failed: found disallowed severities. See extensions field for details.",
+				message: "Found disallowed severities. See extensions field for details.",
 				err:     nil,
 			},
 		},
@@ -731,13 +747,16 @@ func TestVerifyDisallowedSeverities(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			verifierReport, err := verifyDisallowedSeverities("test_verifier", "", TrivyScannerName, &tests[i].args.sarifReport, tt.args.disallowedSeverities, time.Now())
 			if err != nil && err.Error() != tt.want.err.Error() {
-				t.Errorf("verifyDisallowedSeverities() error = %v, wantErr %v", err, tt.want.err)
+				t.Fatalf("verifyDisallowedSeverities() error = %v, wantErr %v", err, tt.want.err)
 				return
 			}
 			if verifierReport.Message != tt.want.message {
-				t.Errorf("verifyDisallowedSeverities() verifier report message = %s, want %s", verifierReport.Message, tt.want.message)
+				t.Fatalf("verifyDisallowedSeverities() verifier report message = %s, want %s", verifierReport.Message, tt.want.message)
 				return
 			}
+			if verifierReport.ErrorReason != tt.want.errorReason {
+				t.Fatalf("verifyDisalowedServerities() verifier report error reason = %s, want = %s", verifierReport.ErrorReason, tt.want.errorReason)
+			}
 		})
 	}
 }