diff --git a/packages/kms/.eslintignore b/packages/kms/.eslintignore
new file mode 100644
index 000000000..58464a086
--- /dev/null
+++ b/packages/kms/.eslintignore
@@ -0,0 +1,2 @@
+node_modules/**
+lib/
diff --git a/packages/kms/.gitignore b/packages/kms/.gitignore
new file mode 100644
index 000000000..de4d1f007
--- /dev/null
+++ b/packages/kms/.gitignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/kms/.mocharc.js b/packages/kms/.mocharc.js
new file mode 100644
index 000000000..6d4c063de
--- /dev/null
+++ b/packages/kms/.mocharc.js
@@ -0,0 +1,4 @@
+const baseConfig = require('../../.mocharc');
+module.exports = {
+  ...baseConfig,
+};
diff --git a/packages/kms/CHANGELOG.md b/packages/kms/CHANGELOG.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kms/README.md b/packages/kms/README.md
new file mode 100644
index 000000000..604a8e6f0
--- /dev/null
+++ b/packages/kms/README.md
@@ -0,0 +1,4 @@
+# steveo-kms
+
+A AWS KMS provider for [Steveo](https://github.com/ordermentum/steveo) pub/pub background processing
+library.
diff --git a/packages/kms/package.json b/packages/kms/package.json
new file mode 100644
index 000000000..e95118484
--- /dev/null
+++ b/packages/kms/package.json
@@ -0,0 +1,39 @@
+{
+  "name": "@steveojs/kms",
+  "version": "7.1.3",
+  "main": "lib/index.js",
+  "repository": "git@github.com:ordermentum/steveo.git",
+  "license": "Apache-2.0",
+  "author": "Ordermentum <engineering@ordermentum.com>",
+  "scripts": {
+    "build": "yarn run tsc",
+    "test": "NODE_ENV=test nyc yarn spec",
+    "spec": "tsc && yarn mocha",
+    "lint": "yarn eslint 'src/**/*.{ts,js}'",
+    "lint:test": "yarn eslint 'test/**/*.{ts,js}'",
+    "autotest": "yarn run mocha --watch",
+    "prepublish": "yarn run build",
+    "typecheck": "yarn run tsc --noEmit"
+  },
+  "files": [
+    "lib"
+  ],
+  "devDependencies": {
+    "@ordermentum/eslint-config-ordermentum": "^2.3.3",
+    "@types/chai": "^4.3.11",
+    "chai": "4.4.1",
+    "mocha": "^10.2.0",
+    "nyc": "^15.1.0",
+    "prettier": "2.8.8",
+    "source-map-support": "0.5.21",
+    "steveo": "*",
+    "ts-node": "10.9.2",
+    "typescript": "^5.3.3"
+  },
+  "dependencies": {
+    "aws-sdk": "2.1692.0"
+  },
+  "peerDependencies": {
+    "steveo": "*"
+  }
+}
\ No newline at end of file
diff --git a/packages/kms/src/index.ts b/packages/kms/src/index.ts
new file mode 100644
index 000000000..529cc71a0
--- /dev/null
+++ b/packages/kms/src/index.ts
@@ -0,0 +1,61 @@
+import { Middleware, MiddlewareCallback, MiddlewareContext } from 'steveo';
+import AWS from 'aws-sdk';
+
+export class KMSMiddleware implements Middleware {
+  client: AWS.KMS;
+
+  keyId: string;
+
+  constructor(keyId: string) {
+    this.client = new AWS.KMS();
+    this.keyId = keyId;
+  }
+
+  public async publish<Ctx = any, C = MiddlewareCallback>(
+    context: MiddlewareContext<Ctx>,
+    _next: C
+  ) {
+    context.payload = await this.encrypt(context.payload);
+  }
+
+  private async encrypt<T = any>(data: T): Promise<string> {
+    const value = JSON.stringify(data);
+
+    const params = {
+      KeyId: this.keyId,
+      Plaintext: Buffer.from(value),
+    };
+
+    const result = await this.client.encrypt(params).promise();
+
+    if (!result.CiphertextBlob) {
+      throw new Error('Failed to encrypt message');
+    }
+
+    return result.CiphertextBlob.toString('base64');
+  }
+
+  private async decrypt<T = any>(data: string): Promise<T> {
+    const params = {
+      KeyId: this.keyId,
+      CiphertextBlob: Buffer.from(data),
+    };
+
+    const result = await this.client.decrypt(params).promise();
+
+    if (!result.Plaintext) {
+      throw new Error('Failed to decrypt message');
+    }
+
+    return JSON.parse(result.Plaintext.toString());
+  }
+
+  public async consume<Ctx = any, C = MiddlewareCallback>(
+    context: MiddlewareContext<Ctx>,
+    _next: C
+  ) {
+    context.payload = await this.decrypt(context.payload);
+  }
+}
+
+export default KMSMiddleware;
diff --git a/packages/kms/test/.eslintrc b/packages/kms/test/.eslintrc
new file mode 100644
index 000000000..d9c04d4b1
--- /dev/null
+++ b/packages/kms/test/.eslintrc
@@ -0,0 +1,6 @@
+{
+    "extends": [
+      "@ordermentum/ordermentum/mocha",
+      "@ordermentum/ordermentum/jest"
+    ]
+  }
\ No newline at end of file
diff --git a/packages/kms/test/index_test.ts b/packages/kms/test/index_test.ts
new file mode 100644
index 000000000..108e8336a
--- /dev/null
+++ b/packages/kms/test/index_test.ts
@@ -0,0 +1,49 @@
+import { expect } from 'chai';
+import { Steveo } from 'steveo';
+
+import sinon from 'sinon';
+import KMSMiddleware from '../src';
+
+describe('KMSMiddleware', () => {
+  let sandbox;
+  let middleware;
+
+  beforeEach(() => {
+    sandbox = sinon.createSandbox();
+  });
+
+  afterEach(() => {
+    sinon.restore();
+  });
+
+  it('add to steveo', () => {
+    middleware = new KMSMiddleware('keyId');
+
+    const steveo = new Steveo({
+      engine: 'dummy',
+      middleware: [middleware],
+    });
+
+    expect(steveo.middleware.length).to.equal(1);
+  });
+
+  describe('publish', () => {
+    it('should encrypt message', async () => {
+      sandbox.stub(middleware, 'encrypt').resolves('encrypted');
+      const context = { message: 'message' };
+      const next = sinon.stub().resolves();
+      await middleware.publish(context, next);
+      expect(context.message).to.equal('encrypted');
+    });
+  });
+
+  describe('consume', () => {
+    it('should decrypt message', async () => {
+      sandbox.stub(middleware, 'decrypt').resolves({ message: 'message' });
+      const context = { message: 'data' };
+      const next = sinon.stub().resolves();
+      await middleware.consume(context, next);
+      expect(context.message).to.deep.equal({ message: 'message' });
+    });
+  });
+});
diff --git a/packages/kms/tsconfig.json b/packages/kms/tsconfig.json
new file mode 100644
index 000000000..245f8f588
--- /dev/null
+++ b/packages/kms/tsconfig.json
@@ -0,0 +1,31 @@
+{
+  "compilerOptions": {
+    "lib": [
+      "es2022"
+    ],
+    "target": "es2022",
+    "module": "commonjs",
+    "allowJs": false,
+    "declaration": true,
+    "outDir": "lib",
+    "strict": true,
+    "alwaysStrict": true,
+    "noUnusedLocals": true,
+    "noImplicitAny": false,
+    "noImplicitThis": false,
+    "resolveJsonModule": true,
+    "noUnusedParameters": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true,
+    "allowSyntheticDefaultImports": true,
+    "moduleResolution": "node",
+    "esModuleInterop": true
+  },
+  "include": [
+    "src"
+  ],
+  "exclude": [
+    "lib",
+    "node_modules"
+  ]
+}
diff --git a/packages/steveo/src/index.ts b/packages/steveo/src/index.ts
index 3b9f314d5..54cc3d36b 100644
--- a/packages/steveo/src/index.ts
+++ b/packages/steveo/src/index.ts
@@ -46,7 +46,7 @@ export {
   DummyConfiguration,
 } from './common';
 
-export { Middleware };
+export { Middleware, MiddlewareContext, MiddlewareCallback } from './common';
 
 export class Steveo implements ISteveo {
   config: KafkaConfiguration | RedisConfiguration | SQSConfiguration;
diff --git a/yarn.lock b/yarn.lock
index fa181c9e0..f6ae53929 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3102,6 +3102,22 @@ available-typed-arrays@^1.0.7:
   dependencies:
     possible-typed-array-names "^1.0.0"
 
+aws-sdk@2.1692.0:
+  version "2.1692.0"
+  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1692.0.tgz#9dac5f7bfcc5ab45825cc8591b12753aa7d2902c"
+  integrity sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==
+  dependencies:
+    buffer "4.9.2"
+    events "1.1.1"
+    ieee754 "1.1.13"
+    jmespath "0.16.0"
+    querystring "0.2.0"
+    sax "1.2.1"
+    url "0.10.3"
+    util "^0.12.4"
+    uuid "8.0.0"
+    xml2js "0.6.2"
+
 aws-sdk@^2.1043.0:
   version "2.1691.0"
   resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1691.0.tgz#9d6ccdcbae03c806fc62667b76eb3e33e5294dcc"