diff --git a/.evergreen/auth_aws b/.evergreen/auth_aws
new file mode 120000
index 0000000000..60a22acdc3
--- /dev/null
+++ b/.evergreen/auth_aws
@@ -0,0 +1 @@
+../.mod/drivers-evergreen-tools/.evergreen/auth_aws
\ No newline at end of file
diff --git a/.evergreen/aws b/.evergreen/aws
index fe937b5bbf..0fc0ce616b 100755
--- a/.evergreen/aws
+++ b/.evergreen/aws
@@ -5,36 +5,32 @@ $: << File.join(File.dirname(__FILE__), '../spec')
 require 'support/aws_utils'
 require 'optparse'
 
-options = {}
-OptionParser.new do |opts|
-  opts.banner = "Usage: aws [options] command ..."
+def parse_options
+  options = {}
+  OptionParser.new do |opts|
+    opts.banner = "Usage: aws [options] command ..."
 
-  opts.on("-a", "--access-key-id=ID", "AWS access key ID") do |v|
-    options[:access_key_id] = v
-  end
-
-  opts.on("-s", "--secret-access-key=SECRET", "AWS secret access key") do |v|
-    options[:secret_access_key] = v
-  end
+    opts.on("-a", "--access-key-id=ID", "AWS access key ID") do |v|
+      options[:access_key_id] = v
+    end
 
-  opts.on("-r", "--region=REGION", "AWS region") do |v|
-    options[:region] = v
-  end
+    opts.on("-s", "--secret-access-key=SECRET", "AWS secret access key") do |v|
+      options[:secret_access_key] = v
+    end
 
-  # launch-ecs options
-  opts.on('--ec2', 'Use EC2 launch type instead of Fargate') do |v|
-    options[:ec2] = true
-  end
-end.parse!
+    opts.on("-r", "--region=REGION", "AWS region") do |v|
+      options[:region] = v
+    end
 
-case cmd = ARGV.shift
-when 'setup-resources'
-  AwsUtils::Provisioner.new.setup_aws_auth_resources
-when 'reset-keys'
-  AwsUtils::Provisioner.new.reset_keys
-when 'assume-role'
-  arn = ARGV.shift
+    # launch-ecs options
+    opts.on('--ec2', 'Use EC2 launch type instead of Fargate') do |v|
+      options[:ec2] = true
+    end
+  end.parse!
+  options
+end
 
+def assume_role(arn, options)
   orchestrator = AwsUtils::Orchestrator.new(**options)
   if arn.nil?
     arn = AwsUtils::Inspector.new(**options).assume_role_arn
@@ -43,39 +39,36 @@ when 'assume-role'
   puts "AWS_ACCESS_KEY_ID=#{credentials.access_key_id}"
   puts "AWS_SECRET_ACCESS_KEY=#{credentials.secret_access_key}"
   puts "AWS_SESSION_TOKEN=#{credentials.session_token}"
-  #puts "EXPIRATION=#{credentials.expiration.utc.strftime('%Y%m%dT%H%M%SZ')}"
   puts
-when 'set-instance-profile'
-  instance_id = ARGV.shift
+end
+
+def set_instance_profile(instance_id, options)
   unless instance_id
     raise 'Instance id is required'
   end
-
   orchestrator = AwsUtils::Orchestrator.new(**options)
   orchestrator.set_instance_profile(instance_id)
-when 'clear-instance-profile'
-  instance_id = ARGV.shift
+end
+
+def clear_instance_profile(instance_id, options)
   unless instance_id
     raise 'Instance id is required'
   end
 
   orchestrator = AwsUtils::Orchestrator.new(**options)
   orchestrator.clear_instance_profile(instance_id)
-when 'key-pairs'
-  AwsUtils::Inspector.new(**options).list_key_pairs
-when 'launch-ec2'
-  public_key_path, = ARGV
+end
+
+def launch_ec2(public_key_path, options)
   unless public_key_path
     raise "Public key path must be given"
   end
 
   orchestrator = AwsUtils::Orchestrator.new(**options)
   orchestrator.provision_auth_ec2_instance(public_key_path: public_key_path)
-when 'stop-ec2'
-  orchestrator = AwsUtils::Orchestrator.new(**options)
-  orchestrator.terminate_auth_ec2_instance
-when 'launch-ecs'
-  public_key_path, = ARGV
+end
+
+def launch_ecs(public_key_path, options)
   unless public_key_path
     raise "Public key path must be given"
   end
@@ -84,6 +77,35 @@ when 'launch-ecs'
   orchestrator.provision_auth_ecs_task(
     public_key_path: public_key_path,
   )
+end
+
+options = parse_options
+
+case cmd = ARGV.shift
+when 'setup-resources'
+  AwsUtils::Provisioner.new.setup_aws_auth_resources
+when 'reset-keys'
+  AwsUtils::Provisioner.new.reset_keys
+when 'assume-role'
+  arn = ARGV.shift
+  assume_role(arn, options)
+when 'set-instance-profile'
+  instance_id = ARGV.shift
+  set_instance_profile(instance_id, options)
+when 'clear-instance-profile'
+  instance_id = ARGV.shift
+  clear_instance_profile(instance_id, options)
+when 'key-pairs'
+  AwsUtils::Inspector.new(**options).list_key_pairs
+when 'launch-ec2'
+  public_key_path, = ARGV
+  launch_ec2(public_key_path, options)
+when 'stop-ec2'
+  orchestrator = AwsUtils::Orchestrator.new(**options)
+  orchestrator.terminate_auth_ec2_instance
+when 'launch-ecs'
+  public_key_path, = ARGV
+  launch_ecs(public_key_path, options)
 when 'stop-ecs'
   orchestrator = AwsUtils::Orchestrator.new(**options)
   orchestrator.terminate_auth_ecs_task
diff --git a/.evergreen/config-atlas.yml b/.evergreen/config-atlas.yml
deleted file mode 100644
index 6b8dcf2e28..0000000000
--- a/.evergreen/config-atlas.yml
+++ /dev/null
@@ -1,859 +0,0 @@
-# GENERATED FILE - DO NOT EDIT.
-# Run ./.evergreen/update-evergreen-configs to regenerate this file.
-
-# When a task that used to pass starts to fail, go through all versions that
-# may have been skipped to detect when the task started failing.
-stepback: true
-
-# Fail builds when pre tasks fail.
-pre_error_fails_task: true
-
-# Mark a failure as a system/bootstrap failure (purple box) rather then a task
-# failure by default.
-# Actual testing tasks are marked with `type: test`
-command_type: system
-
-# Protect ourself against rogue test case, or curl gone wild, that runs forever.
-exec_timeout_secs: 1800
-
-
-# What to do when evergreen hits the timeout (`post:` tasks are run automatically)
-timeout:
-  - command: shell.exec
-    params:
-      script: |
-        true
-
-functions:
-  "fetch source":
-    # Executes git clone and applies the submitted patch, if any
-    - command: git.get_project
-      params:
-        directory: "src"
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          set -ex
-
-          git submodule update --init --recursive
-
-  "fetch egos":
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          set -e
-
-          # Python version:
-          #curl -sfLo egos https://raw.githubusercontent.com/p-mongo/egos/master/egos
-
-          # C version:
-          curl --retry 3 -sfLo egos.c https://raw.githubusercontent.com/p-mongo/egos/master/egos.c
-          gcc -O2 -g -oegos -lrt egos.c
-
-  "create expansions":
-    # Make an evergreen expansion file with dynamic values
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          # Get the current unique version of this checkout
-          if [ "${is_patch}" = "true" ]; then
-            CURRENT_VERSION=$(git describe)-patch-${version_id}
-          else
-            CURRENT_VERSION=latest
-          fi
-
-          export DRIVERS_TOOLS="$(pwd)/../drivers-tools"
-
-          # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
-          if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
-            export DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)
-          fi
-
-          export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration"
-          export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
-          export UPLOAD_BUCKET="${project}"
-          export PROJECT_DIRECTORY="$(pwd)"
-
-          cat <<EOT > expansion.yml
-          CURRENT_VERSION: "$CURRENT_VERSION"
-          DRIVERS_TOOLS: "$DRIVERS_TOOLS"
-          MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME"
-          MONGODB_BINARIES: "$MONGODB_BINARIES"
-          UPLOAD_BUCKET: "$UPLOAD_BUCKET"
-          PROJECT_DIRECTORY: "$PROJECT_DIRECTORY"
-          PREPARE_SHELL: |
-            set -o errexit
-            set -o xtrace
-            export DRIVERS_TOOLS="$DRIVERS_TOOLS"
-            export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME"
-            export MONGODB_BINARIES="$MONGODB_BINARIES"
-            export UPLOAD_BUCKET="$UPLOAD_BUCKET"
-            export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
-
-            # TMPDIR cannot be too long, see
-            # https://github.com/broadinstitute/cromwell/issues/3647.
-            # Why is it even set at all?
-            #export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
-            export PATH="$MONGODB_BINARIES:$PATH"
-            export PROJECT="${project}"
-            export ATLAS_REPLICA_SET_URI="${atlas_replica_set_uri}"
-            export ATLAS_SHARDED_URI="${atlas_sharded_uri}"
-            export ATLAS_FREE_TIER_URI="${atlas_free_tier_uri}"
-            export ATLAS_TLS11_URI="${atlas_tls11_uri}"
-            export ATLAS_TLS12_URI="${atlas_tls12_uri}"
-            export ATLAS_SERVERLESS_URI="${atlas_serverless_uri}"
-            export ATLAS_SERVERLESS_LB_URI="${atlas_serverless_lb_uri}"
-            export RVM_RUBY="${RVM_RUBY}"
-            export SERVERLESS_DRIVERS_GROUP="${SERVERLESS_DRIVERS_GROUP}"
-            export SERVERLESS_API_PUBLIC_KEY="${SERVERLESS_API_PUBLIC_KEY}"
-            export SERVERLESS_API_PRIVATE_KEY="${SERVERLESS_API_PRIVATE_KEY}"
-            export SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}"
-            export SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}"
-          EOT
-          # See what we've done
-          cat expansion.yml
-
-    # Load the expansion file to make an evergreen variable with the current
-    # unique version
-    - command: expansions.update
-      params:
-        file: src/expansion.yml
-
-  "delete serverless instance":
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        shell: bash
-        script: |
-          ${PREPARE_SHELL}
-          if [[ -n "${SERVERLESS_INSTANCE_NAME}" ]]; then
-            SERVERLESS_INSTANCE_NAME=${SERVERLESS_INSTANCE_NAME} .evergreen/serverless/delete-instance.sh
-          fi
-
-  "export AWS auth credentials":
-    - command: shell.exec
-      type: test
-      params:
-        silent: true
-        working_dir: "src"
-        script: |
-          cat <<EOT > .env.private
-          IAM_AUTH_ASSUME_AWS_ACCOUNT="${iam_auth_assume_aws_account}"
-          IAM_AUTH_ASSUME_AWS_SECRET_ACCESS_KEY="${iam_auth_assume_aws_secret_access_key}"
-          IAM_AUTH_ASSUME_ROLE_NAME="${iam_auth_assume_role_name}"
-          IAM_AUTH_EC2_INSTANCE_ACCOUNT="${iam_auth_ec2_instance_account}"
-          IAM_AUTH_EC2_INSTANCE_PROFILE="${iam_auth_ec2_instance_profile}"
-          IAM_AUTH_EC2_INSTANCE_SECRET_ACCESS_KEY="${iam_auth_ec2_instance_secret_access_key}"
-          IAM_AUTH_ECS_ACCOUNT="${iam_auth_ecs_account}"
-          IAM_AUTH_ECS_ACCOUNT_ARN="${iam_auth_ecs_account_arn}"
-          IAM_AUTH_ECS_CLUSTER="${iam_auth_ecs_cluster}"
-          IAM_AUTH_ECS_SECRET_ACCESS_KEY="${iam_auth_ecs_secret_access_key}"
-          IAM_AUTH_ECS_SECURITY_GROUP="${iam_auth_ecs_security_group}"
-          IAM_AUTH_ECS_SUBNET_A="${iam_auth_ecs_subnet_a}"
-          IAM_AUTH_ECS_SUBNET_B="${iam_auth_ecs_subnet_b}"
-          IAM_AUTH_ECS_TASK_DEFINITION="${iam_auth_ecs_task_definition}"
-          EOT
-
-  "export FLE credentials":
-    - command: shell.exec
-      type: test
-      params:
-        silent: true
-        working_dir: "src"
-        script: |
-          cat <<EOT > .env.private
-          MONGO_RUBY_DRIVER_AWS_KEY="${fle_aws_key}"
-          MONGO_RUBY_DRIVER_AWS_SECRET="${fle_aws_secret}"
-          MONGO_RUBY_DRIVER_AWS_REGION="${fle_aws_region}"
-          MONGO_RUBY_DRIVER_AWS_ARN="${fle_aws_arn}"
-
-          MONGO_RUBY_DRIVER_AZURE_TENANT_ID="${fle_azure_tenant_id}"
-          MONGO_RUBY_DRIVER_AZURE_CLIENT_ID="${fle_azure_client_id}"
-          MONGO_RUBY_DRIVER_AZURE_CLIENT_SECRET="${fle_azure_client_secret}"
-          MONGO_RUBY_DRIVER_AZURE_IDENTITY_PLATFORM_ENDPOINT="${fle_azure_identity_platform_endpoint}"
-          MONGO_RUBY_DRIVER_AZURE_KEY_VAULT_ENDPOINT="${fle_azure_key_vault_endpoint}"
-          MONGO_RUBY_DRIVER_AZURE_KEY_NAME="${fle_azure_key_name}"
-
-          MONGO_RUBY_DRIVER_GCP_EMAIL="${fle_gcp_email}"
-          MONGO_RUBY_DRIVER_GCP_PRIVATE_KEY="${fle_gcp_private_key}"
-          MONGO_RUBY_DRIVER_GCP_PROJECT_ID="${fle_gcp_project_id}"
-          MONGO_RUBY_DRIVER_GCP_LOCATION="${fle_gcp_location}"
-          MONGO_RUBY_DRIVER_GCP_KEY_RING="${fle_gcp_key_ring}"
-          MONGO_RUBY_DRIVER_GCP_KEY_NAME="${fle_gcp_key_name}"
-          MONGO_RUBY_DRIVER_MONGOCRYPTD_PORT="${fle_mongocryptd_port}"
-          EOT
-
-  "export Kerberos credentials":
-    - command: shell.exec
-      type: test
-      params:
-        silent: true
-        working_dir: "src"
-        script: |
-          cat <<EOT > .env.private
-          SASL_HOST=${sasl_host}
-          SASL_PORT=${sasl_port}
-          SASL_USER=${sasl_user}
-          SASL_PASS=${sasl_pass}
-          SASL_DB=${sasl_db}
-          PRINCIPAL=${principal}
-          KERBEROS_DB=${kerberos_db}
-          KEYTAB_BASE64=${keytab_base64}
-          EOT
-
-  "exec script" :
-    - command: shell.exec
-      type: test
-      params:
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          sh ${PROJECT_DIRECTORY}/${file}
-
-  "upload mo artifacts":
-    - command: shell.exec
-      params:
-        script: |
-          ${PREPARE_SHELL}
-          find $MONGO_ORCHESTRATION_HOME -name \*.log\* | xargs tar czf mongodb-logs.tar.gz
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        local_file: mongodb-logs.tar.gz
-        remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz
-        bucket: mciuploads
-        permissions: public-read
-        content_type: ${content_type|application/x-gzip}
-        display_name: "mongodb-logs.tar.gz"
-
-  "upload working dir":
-    - command: archive.targz_pack
-      params:
-        target: "working-dir.tar.gz"
-        source_dir: ${PROJECT_DIRECTORY}/
-        include:
-          - "./**"
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        local_file: working-dir.tar.gz
-        remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-working-dir.tar.gz
-        bucket: mciuploads
-        permissions: public-read
-        content_type: ${content_type|application/x-gzip}
-        display_name: "working-dir.tar.gz"
-    - command: archive.targz_pack
-      params:
-        target: "drivers-dir.tar.gz"
-        source_dir: ${DRIVERS_TOOLS}
-        include:
-          - "./**"
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        local_file: drivers-dir.tar.gz
-        remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-drivers-dir.tar.gz
-        bucket: mciuploads
-        permissions: public-read
-        content_type: ${content_type|application/x-gzip}
-        display_name: "drivers-dir.tar.gz"
-
-  "upload test results to s3":
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        # src is the relative path to repo checkout,
-        # This is specified in this yaml file earlier.
-        local_file: ./src/tmp/rspec.json
-        display_name: rspec.json
-        remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/rspec.json
-        content_type: application/json
-        permissions: public-read
-        bucket: mciuploads
-    # AWS does not appear to support on-the-fly gzip encoding; compress
-    # the results manually and upload a compressed file.
-    # Typical size reduction: 50 MB -> 800 KB
-    - command: shell.exec
-      params:
-        script: |
-          gzip <src/tmp/rspec.json >src/tmp/rspec.json.gz
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        # src is the relative path to repo checkout,
-        # This is specified in this yaml file earlier.
-        local_file: ./src/tmp/rspec.json.gz
-        display_name: rspec.json.gz
-        remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/rspec.json.gz
-        content_type: application/gzip
-        permissions: public-read
-        bucket: mciuploads
-    - command: shell.exec
-      params:
-        script: |
-          xz -9 <src/tmp/rspec.json >src/tmp/rspec.json.xz
-    - command: s3.put
-      params:
-        aws_key: ${aws_key}
-        aws_secret: ${aws_secret}
-        # src is the relative path to repo checkout,
-        # This is specified in this yaml file earlier.
-        local_file: ./src/tmp/rspec.json.xz
-        display_name: rspec.json.xz
-        remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/rspec.json.xz
-        content_type: application/x-xz
-        permissions: public-read
-        bucket: mciuploads
-
-  "upload test results":
-    - command: attach.xunit_results
-      params:
-        file: ./src/tmp/rspec.xml
-
-  "delete private environment":
-    - command: shell.exec
-      type: test
-      params:
-        silent: true
-        working_dir: "src"
-        script: |
-          rm -f .env.private
-
-  "build and test docker image":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          set -x
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/test-on-docker -d ${os} MONGODB_VERSION=${mongodb-version} TOPOLOGY=${topology} RVM_RUBY=${ruby} -s .evergreen/run-tests.sh TEST_CMD=true ${PRELOAD_ARG}
-
-  "run tests":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests.sh
-
-  "run tests via docker":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-docker.sh
-
-  "run AWS auth tests":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-aws-auth.sh
-
-  "run Kerberos unit tests":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-kerberos-unit.sh
-
-  "run Kerberos integration tests":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-kerberos-integration.sh
-
-  "run Atlas tests":
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          AUTH=${AUTH} SSL=${SSL} TOPOLOGY=${TOPOLOGY} RVM_RUBY="${RVM_RUBY}" ATLAS_REPLICA_SET_URI=${atlas_replica_set_uri} ATLAS_SHARDED_URI=${atlas_sharded_uri} ATLAS_FREE_TIER_URI=${atlas_free_tier_uri} ATLAS_TLS11_URI=${atlas_tls11_uri} ATLAS_TLS12_URI=${atlas_tls12_uri} ATLAS_SERVERLESS_URI=${atlas_serverless_uri} ATLAS_SERVERLESS_LB_URI=${atlas_serverless_lb_uri} .evergreen/run-tests-atlas.sh
-
-  "run serverless tests":
-    - command: shell.exec
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          # This hack with PROJECT env is necessary while serverless
-          # rejects creating instances with too long names.
-          PROJECT="mongo-ruby-driver" LOADBALANCED="ON" .evergreen/serverless/create-instance.sh
-          cat serverless-expansion.yml
-
-    - command: expansions.update
-      params:
-        file: src/serverless-expansion.yml
-
-    - command: shell.exec
-      type: test
-      params:
-        shell: bash
-        working_dir: "src"
-        script: |
-          ${PREPARE_SHELL}
-          SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
-
-pre:
-  - func: "fetch source"
-  - func: "fetch egos"
-  - func: "create expansions"
-
-post:
-  - func: "delete private environment"
-  # Removed, causing timeouts
-  # - func: "upload working dir"
-  - func: "upload mo artifacts"
-  #- func: "upload test results"
-  - func: "upload test results to s3"
-  - func: "delete serverless instance"
-
-tasks:
-
-  - name: "test-atlas"
-    commands:
-      - func: "run Atlas tests"
-  - name: "test-serverless"
-    commands:
-      - func: "run serverless tests"
-axes:
-
-  - id: preload
-    display_name: Preload server
-    values:
-      - id: nopreload
-        display_name: Do not preload
-      - id: preload
-        display_name: Preload
-        variables:
-          PRELOAD_ARG: -p
-
-  - id: "mongodb-version"
-    display_name: MongoDB Version
-    values:
-      - id: "latest"
-        display_name: "latest"
-        variables:
-          MONGODB_VERSION: "latest"
-      - id: "6.0"
-        display_name: "6.0"
-        variables:
-          MONGODB_VERSION: "6.0"
-      - id: "5.3"
-        display_name: "5.3"
-        variables:
-          MONGODB_VERSION: "5.3"
-      - id: "5.0"
-        display_name: "5.0"
-        variables:
-          MONGODB_VERSION: "5.0"
-      - id: "4.4"
-        display_name: "4.4"
-        variables:
-          MONGODB_VERSION: "4.4"
-      - id: "4.2"
-        display_name: "4.2"
-        variables:
-          MONGODB_VERSION: "4.2"
-      - id: "4.0"
-        display_name: "4.0"
-        variables:
-          MONGODB_VERSION: "4.0"
-      - id: "3.6"
-        display_name: "3.6"
-        variables:
-          MONGODB_VERSION: "3.6"
-
-  - id: fcv
-    display_name: FCV
-    values:
-      - id: '3.4'
-        display_name: '3.4'
-        variables:
-          FCV: '3.4'
-
-  - id: "topology"
-    display_name: Topology
-    values:
-      - id: "standalone"
-        display_name: Standalone
-        variables:
-          TOPOLOGY: standalone
-      - id: "replica-set"
-        display_name: Replica Set
-        variables:
-          TOPOLOGY: replica-set
-      - id: "sharded-cluster"
-        display_name: Sharded
-        variables:
-          TOPOLOGY: sharded-cluster
-      - id: "load-balanced"
-        display_name: Load Balanced
-        variables:
-          TOPOLOGY: load-balanced
-
-  - id: "single-mongos"
-    display_name: Single Mongos
-    values:
-      - id: "single-mongos"
-        display_name: Single Mongos
-        variables:
-          SINGLE_MONGOS: 'true'
-
-  - id: "auth-and-ssl"
-    display_name: Authentication and SSL
-    values:
-      - id: "auth-and-ssl"
-        display_name: Auth SSL
-        variables:
-          AUTH: "auth"
-          SSL: "ssl"
-      - id: "auth-and-nossl"
-        display_name: Auth NoSSL
-        variables:
-          AUTH: "auth"
-      - id: "noauth-and-ssl"
-        display_name: NoAuth SSL
-        variables:
-          SSL: "ssl"
-      - id: "noauth-and-nossl"
-        display_name: NoAuth NoSSL
-      - id: "x509"
-        display_name: X.509
-        variables:
-          AUTH: "x509"
-          SSL: "ssl"
-      - id: kerberos
-        display_name: Kerberos
-        variables:
-          AUTH: kerberos
-      - id: aws-regular
-        display_name: AWS Auth Regular Credentials
-        variables:
-          AUTH: aws-regular
-      - id: aws-assume-role
-        display_name: AWS Auth Assume Role
-        variables:
-          AUTH: aws-assume-role
-      - id: aws-ec2
-        display_name: AWS Auth EC2 Role
-        variables:
-          AUTH: aws-ec2
-      - id: aws-ecs
-        display_name: AWS Auth ECS Task
-        variables:
-          AUTH: aws-ecs
-
-  - id: "ruby"
-    display_name: Ruby Version
-    values:
-      - id: "ruby-3.1"
-        display_name: ruby-3.1
-        variables:
-          RVM_RUBY: "ruby-3.1"
-      - id: "ruby-3.0"
-        display_name: ruby-3.0
-        variables:
-          RVM_RUBY: "ruby-3.0"
-      - id: "ruby-2.7"
-        display_name: ruby-2.7
-        variables:
-          RVM_RUBY: "ruby-2.7"
-      - id: "ruby-2.6"
-        display_name: ruby-2.6
-        variables:
-          RVM_RUBY: "ruby-2.6"
-      - id: "ruby-2.5"
-        display_name: ruby-2.5
-        variables:
-          RVM_RUBY: "ruby-2.5"
-      - id: "ruby-head"
-        display_name: ruby-head
-        variables:
-          RVM_RUBY: "ruby-head"
-      - id: "jruby-9.2"
-        display_name: jruby-9.2
-        variables:
-          RVM_RUBY: "jruby-9.2"
-      - id: "jruby-9.3"
-        display_name: jruby-9.3
-        variables:
-          RVM_RUBY: "jruby-9.3"
-
-  - id: "os"
-    display_name: OS
-    values:
-      - id: ubuntu2004
-        display_name: "Ubuntu 20.04"
-        run_on: ubuntu2004-small
-      - id: ubuntu1804
-        display_name: "Ubuntu 18.04"
-        run_on: ubuntu1804-small
-      - id: ubuntu1604
-        display_name: "Ubuntu 16.04"
-        run_on: ubuntu1604-small
-      - id: rhel80
-        display_name: "RHEL 8"
-        run_on: rhel80-small
-      - id: rhel70
-        display_name: "RHEL 7"
-        run_on: rhel70-small
-      - id: rhel62
-        display_name: "RHEL 6"
-        run_on: rhel62-small
-
-  - id: docker-distro
-    display_name: Docker Distro
-    values:
-        - id: debian92
-          display_name: debian92
-          variables:
-            DOCKER_DISTRO: debian92
-        - id: debian10
-          display_name: debian10
-          variables:
-            DOCKER_DISTRO: debian10
-        - id: ubuntu1804
-          display_name: ubuntu1804
-          variables:
-            DOCKER_DISTRO: ubuntu1804
-
-  - id: "compressor"
-    display_name: Compressor
-    values:
-      - id: "zlib"
-        display_name: Zlib
-        variables:
-          COMPRESSOR: "zlib"
-      - id: "snappy"
-        display_name: Snappy
-        variables:
-          COMPRESSOR: "snappy"
-      - id: "zstd"
-        display_name: Zstd
-        variables:
-          COMPRESSOR: "zstd"
-
-  - id: retry-reads
-    display_name: Retry Reads
-    values:
-      - id: no-retry-reads
-        display_name: No Retry Reads
-        variables:
-          RETRY_READS: 'false'
-
-  - id: retry-writes
-    display_name: Retry Writes
-    values:
-      - id: no-retry-writes
-        display_name: No Retry Writes
-        variables:
-          RETRY_WRITES: 'false'
-
-  - id: lint
-    display_name: Lint
-    values:
-      - id: on
-        display_name: On
-        variables:
-          LINT: '1'
-
-  - id: stress
-    display_name: Stress
-    values:
-      - id: on
-        display_name: On
-        variables:
-          STRESS: '1'
-
-  - id: fork
-    display_name: Fork
-    values:
-      - id: on
-        display_name: On
-        variables:
-          FORK: '1'
-
-  - id: solo
-    display_name: Solo
-    values:
-      - id: on
-        display_name: On
-        variables:
-          SOLO: '1'
-
-  - id: "as"
-    display_name: ActiveSupport
-    values:
-      - id: "as"
-        display_name: AS
-        variables:
-          WITH_ACTIVE_SUPPORT: true
-
-  - id: bson
-    display_name: BSON
-    values:
-      - id: master
-        display_name: master
-        variables:
-          BSON: master
-      - id: 4-stable
-        display_name: 4-stable
-        variables:
-          BSON: 4-stable
-      - id: min
-        display_name: min
-        variables:
-          BSON: min
-
-  - id: storage-engine
-    display_name: Storage Engine
-    values:
-      - id: mmapv1
-        display_name: MMAPv1
-        run_on: ubuntu1604-small
-        variables:
-          MMAPV1: 'true'
-
-  - id: "fle"
-    display_name: FLE
-    values:
-      - id: "helper"
-        display_name: via LMC helper
-        variables:
-          FLE: helper
-      - id: "path"
-        display_name: via LMC path
-        variables:
-          FLE: path
-
-  - id: ocsp-algorithm
-    display_name: OCSP Algorithm
-    values:
-      - id: rsa
-        display_name: RSA
-        variables:
-          OCSP_ALGORITHM: rsa
-      - id: ecdsa
-        display_name: ECDSA
-        variables:
-          OCSP_ALGORITHM: ecdsa
-
-  - id: ocsp-status
-    display_name: OCSP Status
-    values:
-      - id: valid
-        display_name: Valid
-      - id: revoked
-        display_name: Revoked
-        variables:
-          OCSP_STATUS: revoked
-      - id: unknown
-        display_name: Unknown
-        variables:
-          OCSP_STATUS: unknown
-
-  - id: ocsp-delegate
-    display_name: OCSP Delegate
-    values:
-      - id: on
-        display_name: on
-        variables:
-          OCSP_DELEGATE: 1
-
-  - id: ocsp-must-staple
-    display_name: OCSP Must Staple
-    values:
-      - id: on
-        display_name: on
-        variables:
-          OCSP_MUST_STAPLE: 1
-
-  - id: ocsp-verifier
-    display_name: OCSP Verifier
-    values:
-      - id: true
-        display_name: true
-        variables:
-          OCSP_VERIFIER: 1
-
-  - id: ocsp-connectivity
-    display_name: OCSP Connectivity
-    values:
-      - id: pass
-        display_name: pass
-        variables:
-          OCSP_CONNECTIVITY: pass
-      - id: fail
-        display_name: fail
-        variables:
-          OCSP_CONNECTIVITY: fail
-
-  - id: extra-uri-options
-    display_name: extra URI options
-    values:
-      - id: none
-        display_name: None
-      - id: "tlsInsecure=true"
-        variables:
-          EXTRA_URI_OPTIONS: "tlsInsecure=true"
-      - id: "tlsAllowInvalidCertificates=true"
-        variables:
-          EXTRA_URI_OPTIONS: "tlsAllowInvalidCertificates=true"
-
-  - id: api-version-required
-    display_name: API version required
-    values:
-      - id: yes
-        display_name: Yes
-        variables:
-          API_VERSION_REQUIRED: 1
-      - id: no
-        display_name: No
-buildvariants:
-  - matrix_name: "all"
-    matrix_spec:
-      ruby: ["ruby-2.5", "ruby-2.6", "ruby-2.7", "ruby-3.0", "ruby-3.1", "jruby-9.2"]
-    display_name: "Atlas tests ${ruby}"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-       - name: test-atlas
-
-  - matrix_name: "serverless"
-    matrix_spec:
-      ruby: ["ruby-2.5", "ruby-2.6", "ruby-2.7", "ruby-3.0", "ruby-3.1", "jruby-9.2"]
-    display_name: "Atlas serverless ${ruby} single mongos"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-       - name: test-serverless
diff --git a/.evergreen/config.yml b/.evergreen/config.yml
index ccf5cff9fd..a7204f93a5 100644
--- a/.evergreen/config.yml
+++ b/.evergreen/config.yml
@@ -14,8 +14,7 @@ pre_error_fails_task: true
 command_type: system
 
 # Protect ourself against rogue test case, or curl gone wild, that runs forever.
-exec_timeout_secs: 2700
-
+exec_timeout_secs: 3600
 
 # What to do when evergreen hits the timeout (`post:` tasks are run automatically)
 timeout:
@@ -38,20 +37,6 @@ functions:
 
           git submodule update --init --recursive
 
-  "fetch egos":
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          set -e
-
-          # Python version:
-          #curl -sfLo egos https://raw.githubusercontent.com/p-mongo/egos/master/egos
-
-          # C version:
-          curl --retry 3 -sfLo egos.c https://raw.githubusercontent.com/p-mongo/egos/master/egos.c
-          gcc -O2 -g -oegos -lrt egos.c
-
   "create expansions":
     # Make an evergreen expansion file with dynamic values
     - command: shell.exec
@@ -65,7 +50,7 @@ functions:
             CURRENT_VERSION=latest
           fi
 
-          export DRIVERS_TOOLS="$(pwd)/../drivers-tools"
+          export DRIVERS_TOOLS="$(pwd)/.mod/drivers-evergreen-tools"
 
           # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
           if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
@@ -106,6 +91,7 @@ functions:
             export COMPRESSOR=${COMPRESSOR}
             export RVM_RUBY="${RVM_RUBY}"
             export MONGODB_VERSION=${MONGODB_VERSION}
+            export CRYPT_SHARED_VERSION=${CRYPT_SHARED_VERSION}
             export FCV=${FCV}
             export MONGO_RUBY_DRIVER_LINT=${LINT}
             export RETRY_READS=${RETRY_READS}
@@ -128,6 +114,28 @@ functions:
             export OCSP_MUST_STAPLE="${OCSP_MUST_STAPLE}"
             export OCSP_CONNECTIVITY="${OCSP_CONNECTIVITY}"
             export OCSP_VERIFIER="${OCSP_VERIFIER}"
+
+            export ATLAS_REPLICA_SET_URI="${atlas_replica_set_uri}"
+            export ATLAS_SHARDED_URI="${atlas_sharded_uri}"
+            export ATLAS_FREE_TIER_URI="${atlas_free_tier_uri}"
+            export ATLAS_TLS11_URI="${atlas_tls11_uri}"
+            export ATLAS_TLS12_URI="${atlas_tls12_uri}"
+            export ATLAS_SERVERLESS_URI="${atlas_serverless_uri}"
+            export ATLAS_SERVERLESS_LB_URI="${atlas_serverless_lb_uri}"
+            export RVM_RUBY="${RVM_RUBY}"
+            export SERVERLESS_DRIVERS_GROUP="${SERVERLESS_DRIVERS_GROUP}"
+            export SERVERLESS_API_PUBLIC_KEY="${SERVERLESS_API_PUBLIC_KEY}"
+            export SERVERLESS_API_PRIVATE_KEY="${SERVERLESS_API_PRIVATE_KEY}"
+            export SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}"
+            export SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}"
+
+            # Needed for generating temporary aws credentials.
+            if [ -n "${FLE}" ];
+            then
+              export AWS_ACCESS_KEY_ID="${fle_aws_key}"
+              export AWS_SECRET_ACCESS_KEY="${fle_aws_secret}"
+              export AWS_DEFAULT_REGION="${fle_aws_region}"
+            fi
           EOT
 
           # See what we've done
@@ -172,6 +180,13 @@ functions:
           IAM_AUTH_ECS_SUBNET_A="${iam_auth_ecs_subnet_a}"
           IAM_AUTH_ECS_SUBNET_B="${iam_auth_ecs_subnet_b}"
           IAM_AUTH_ECS_TASK_DEFINITION="${iam_auth_ecs_task_definition}"
+
+          IAM_WEB_IDENTITY_ISSUER="${iam_web_identity_issuer}"
+          IAM_WEB_IDENTITY_JWKS_URI="${iam_web_identity_jwks_uri}"
+          IAM_WEB_IDENTITY_RSA_KEY="${iam_web_identity_rsa_key}"
+          IAM_WEB_IDENTITY_TOKEN_FILE="${iam_web_identity_token_file}"
+          IAM_AUTH_ASSUME_WEB_ROLE_NAME="${iam_auth_assume_web_role_name}"
+
           EOT
 
   "export FLE credentials":
@@ -353,7 +368,7 @@ functions:
         script: |
           ${PREPARE_SHELL}
           set -x
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/test-on-docker -d ${os} MONGODB_VERSION=${mongodb-version} TOPOLOGY=${topology} RVM_RUBY=${ruby} -s .evergreen/run-tests.sh TEST_CMD=true ${PRELOAD_ARG}
+          .evergreen/test-on-docker -d ${os} MONGODB_VERSION=${mongodb-version} TOPOLOGY=${topology} RVM_RUBY=${ruby} -s .evergreen/run-tests.sh TEST_CMD=true ${PRELOAD_ARG}
 
   "run tests":
     - command: shell.exec
@@ -363,7 +378,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests.sh
+          .evergreen/run-tests.sh
 
   "run tests via docker":
     - command: shell.exec
@@ -373,7 +388,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-docker.sh
+          .evergreen/run-tests-docker.sh
 
   "run AWS auth tests":
     - command: shell.exec
@@ -383,7 +398,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-aws-auth.sh
+          .evergreen/run-tests-aws-auth.sh
 
   "run Kerberos unit tests":
     - command: shell.exec
@@ -393,7 +408,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-kerberos-unit.sh
+          .evergreen/run-tests-kerberos-unit.sh
 
   "run Kerberos integration tests":
     - command: shell.exec
@@ -403,7 +418,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          ./egos stdbuf -i0 -o0 -e0 .evergreen/run-tests-kerberos-integration.sh
+          .evergreen/run-tests-kerberos-integration.sh
 
   "run Atlas tests":
     - command: shell.exec
@@ -438,11 +453,10 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
+          SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" FLE="${FLE}" SERVERLESS_MONGODB_VERSION="${SERVERLESS_MONGODB_VERSION}" .evergreen/run-tests-serverless.sh
 
 pre:
   - func: "fetch source"
-  - func: "fetch egos"
   - func: "create expansions"
 
 post:
@@ -452,9 +466,16 @@ post:
   - func: "upload mo artifacts"
   #- func: "upload test results"
   - func: "upload test results to s3"
+  - func: "delete serverless instance"
 
 tasks:
-
+  - name: "test-atlas"
+    commands:
+      - func: "run Atlas tests"
+  - name: "test-serverless"
+    commands:
+      - func: "export FLE credentials"
+      - func: "run serverless tests"
   - name: "test-docker"
     commands:
       - func: "build and test docker image"
@@ -502,6 +523,11 @@ axes:
         display_name: "latest"
         variables:
           MONGODB_VERSION: "latest"
+          CRYPT_SHARED_VERSION: "latest"
+      - id: "7.0"
+        display_name: "7.0"
+        variables:
+          MONGODB_VERSION: "7.0"
       - id: "6.0"
         display_name: "6.0"
         variables:
@@ -510,18 +536,22 @@ axes:
         display_name: "5.3"
         variables:
           MONGODB_VERSION: "5.3"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "5.0"
         display_name: "5.0"
         variables:
           MONGODB_VERSION: "5.0"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.4"
         display_name: "4.4"
         variables:
           MONGODB_VERSION: "4.4"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.2"
         display_name: "4.2"
         variables:
           MONGODB_VERSION: "4.2"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.0"
         display_name: "4.0"
         variables:
@@ -614,6 +644,14 @@ axes:
   - id: "ruby"
     display_name: Ruby Version
     values:
+      - id: "ruby-3.2"
+        display_name: ruby-3.2
+        variables:
+          RVM_RUBY: "ruby-3.2"
+      - id: "ruby-3.1"
+        display_name: ruby-3.1
+        variables:
+          RVM_RUBY: "ruby-3.1"
       - id: "ruby-3.1"
         display_name: ruby-3.1
         variables:
@@ -646,44 +684,38 @@ axes:
         display_name: jruby-9.3
         variables:
           RVM_RUBY: "jruby-9.3"
+      - id: "jruby-9.4"
+        display_name: jruby-9.4
+        variables:
+          RVM_RUBY: "jruby-9.4"
 
   - id: "os"
     display_name: OS
     values:
-      - id: ubuntu2004
-        display_name: "Ubuntu 20.04"
-        run_on: ubuntu2004-small
-      - id: ubuntu1804
-        display_name: "Ubuntu 18.04"
-        run_on: ubuntu1804-small
-      - id: ubuntu1604
-        display_name: "Ubuntu 16.04"
-        run_on: ubuntu1604-small
-      - id: rhel80
+      - id: debian11
+        display_name: "Debian 11"
+        run_on: debian11-small
+      - id: ubuntu2204
+        display_name: "Ubuntu 22.04"
+        run_on: ubuntu2204-small
+      - id: rhel8
         display_name: "RHEL 8"
         run_on: rhel80-small
-      - id: rhel70
-        display_name: "RHEL 7"
-        run_on: rhel70-small
-      - id: rhel62
-        display_name: "RHEL 6"
-        run_on: rhel62-small
+      - id: rhel8-arm
+        display_name: "RHEL 8 ARM64"
+        run_on: rhel82-arm64-small
 
   - id: docker-distro
     display_name: Docker Distro
     values:
-        - id: debian92
-          display_name: debian92
+        - id: debian11
+          display_name: debian11
           variables:
-            DOCKER_DISTRO: debian92
-        - id: debian10
-          display_name: debian10
+            DOCKER_DISTRO: debian11
+        - id: ubuntu2204
+          display_name: ubuntu2204
           variables:
-            DOCKER_DISTRO: debian10
-        - id: ubuntu1804
-          display_name: ubuntu1804
-          variables:
-            DOCKER_DISTRO: ubuntu1804
+            DOCKER_DISTRO: ubuntu2204
 
   - id: "compressor"
     display_name: Compressor
@@ -778,7 +810,7 @@ axes:
     values:
       - id: mmapv1
         display_name: MMAPv1
-        run_on: ubuntu1604-small
+        run_on: rhel80-small
         variables:
           MMAPV1: 'true'
 
@@ -879,148 +911,109 @@ axes:
         display_name: No
 
 buildvariants:
-  - matrix_name: "docker"
-    matrix_spec:
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-      preload: preload
-    display_name: "Docker ${os} ${mongodb-version} ${topology} ${ruby} ${preload}"
-    tasks:
-      - name: "test-docker"
-
-  - matrix_name: "docker"
-    matrix_spec:
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-      preload: nopreload
-    display_name: "Docker ${os} ${mongodb-version} ${topology} ${ruby} ${preload}"
-    tasks:
-      - name: "test-docker"
-
-  - matrix_name: "mongo-latest"
+  - matrix_name: "auth/ssl"
     matrix_spec:
       auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['latest']
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
+      os: rhel8
+    display_name: ${auth-and-ssl} ${ruby} ${topology}
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "mongo-6.0"
+  - matrix_name: "mongo-recent"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['6.0']
+      ruby: ["ruby-3.2", "ruby-3.1", "jruby-9.3"]
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
+      os: ['rhel8']
+      # There is no latest for ubuntu2204, so we can't test it here.
+      # os: ['rhel8', 'ubuntu2204']
+    display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "mongo-5.3"
+  # Only JRuby-9.4 is built for arm in the current toolchain, but 9.4 has other
+  # issues. Either we fix those issues, or we build 9.3 for arm in the toolchain,
+  # and hope it fixes the issues... Until then, we can only test MRI ruby on arm.
+  #
+  # further: 'latest' is broken until the QE2 changes are merged (RUBY-3211,
+  # RUBY-3226)
+  #
+  # further: ARM specs on 2.18-stable are broken because nokogiri won't install
+  # a compatible binary. Disabling for now.
+  #
+  # - matrix_name: "mongo-recent-arm"
+  #   matrix_spec:
+  #     ruby: "ruby-3.2"
+  #     #mongodb-version: ['latest', '6.0']
+  #     mongodb-version: ['6.0']
+  #     topology: ["standalone", "replica-set", "sharded-cluster"]
+  #     os: 'rhel8-arm'
+  #   display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}"
+  #   tasks:
+  #     - name: "test-mlaunch"
+
+  - matrix_name: "mongo-5.x"
     matrix_spec:
-      auth-and-ssl: noauth-and-nossl
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['5.3']
-      topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "mongo-4.2"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-2.7", "ruby-2.5"]
-      mongodb-version: ['4.2']
+      ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+      mongodb-version: ['5.3', '5.0']
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "mongo-4.0"
+  - matrix_name: "mongo-4.x"
     matrix_spec:
-      auth-and-ssl: ["noauth-and-ssl", "auth-and-nossl"]
-      ruby: "ruby-2.6"
-      mongodb-version: ['4.0']
+      ruby: ["ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5"]
+      mongodb-version: ['4.4', '4.2', '4.0']
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "mongo-3.6"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
       ruby: "ruby-2.5"
       mongodb-version: ['3.6']
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1604
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "mongo-4.4"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: '4.4'
-      topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "mongo-5.0"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1", "ruby-2.7"]
-      mongodb-version: '5.0'
-      topology: '*'
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "single-lb"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.0"]
-      mongodb-version: '5.0'
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: load-balanced
       single-mongos: single-mongos
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} single-lb ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "mongo-5.0-api-version"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
+      ruby: "ruby-3.2"
       mongodb-version: '5.0'
       topology: standalone
       api-version-required: yes
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} api-version-required ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "single-mongos"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: "ruby-2.6"
-      mongodb-version: ['4.0', '4.2', '4.4']
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: "sharded-cluster"
       single-mongos: single-mongos
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} single-mongos ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1028,10 +1021,10 @@ buildvariants:
   - matrix_name: "no-retry-reads"
     matrix_spec:
       retry-reads: no-retry-reads
-      ruby: "ruby-2.6"
-      mongodb-version: ["4.0"]
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${retry-reads} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1039,10 +1032,10 @@ buildvariants:
   - matrix_name: "no-retry-writes"
     matrix_spec:
       retry-writes: no-retry-writes
-      ruby: ["ruby-2.7"]
-      mongodb-version: ["4.0"]
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: [replica-set, sharded-cluster]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${retry-writes} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1053,18 +1046,18 @@ buildvariants:
       mongodb-version: ['3.6', '4.0']
       topology: ["standalone", "replica-set", "sharded-cluster"]
       storage-engine: mmapv1
-      os: ubuntu1604
+      os: rhel8
     display_name: "${mongodb-version} ${topology} mmapv1 ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "lint-5.0"
+  - matrix_name: "lint"
     matrix_spec:
       lint: on
-      ruby: ["ruby-3.1", "ruby-2.7"]
-      mongodb-version: ["5.0"]
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: '*'
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${lint} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1072,10 +1065,10 @@ buildvariants:
   - matrix_name: "fork"
     matrix_spec:
       fork: on
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: ["4.4"]
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} fork ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1083,10 +1076,10 @@ buildvariants:
   - matrix_name: "solo"
     matrix_spec:
       solo: on
-      ruby: [ruby-3.1, ruby-3.0, ruby-2.7, ruby-2.6, ruby-2.5, jruby-9.2, jruby-9.3]
-      mongodb-version: ["4.4"]
+      ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5", "jruby-9.3", "jruby-9.2"]
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} solo ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1097,7 +1090,7 @@ buildvariants:
       ruby: ["ruby-2.7"]
       mongodb-version: ['4.2', '4.0', '3.6']
       topology: replica-set
-      os: ubuntu1604
+      os: rhel8
     display_name: "${mongodb-version} ${topology} stress ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -1105,374 +1098,175 @@ buildvariants:
   - matrix_name: "stress"
     matrix_spec:
       stress: on
-      ruby: ["ruby-2.7"]
-      mongodb-version: ['5.0', '4.4']
+      ruby: "ruby-3.2"
+      mongodb-version: ["6.0", "5.3"]
       topology: replica-set
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} stress ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "x509-tests"
-    matrix_spec:
-      auth-and-ssl: "x509"
-      ruby: "ruby-2.7"
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
+  # The X.509 tests are failing with exceptions being expected, but never
+  # raised. (RUBY-3267)
+  #
+  # - matrix_name: "x509-tests"
+  #   matrix_spec:
+  #     auth-and-ssl: "x509"
+  #     ruby: "ruby-3.2"
+  #     # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure
+  #     # the server for certain auth mechanisms. Once run-tests.sh is made smart
+  #     # enough to install mongosh, and then use either mongo or mongosh
+  #     # (depending on server version and what's available), we can bump this to
+  #     # the latest stable db version.
+  #     mongodb-version: "5.3"
+  #     topology: standalone
+  #     os: rhel8
+  #   display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
+  #   tasks:
+  #     - name: "test-mlaunch"
 
   - matrix_name: "jruby-auth"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: "jruby-9.3"
-      mongodb-version: "4.2"
-      topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: rhel80
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "jruby-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "jruby-9.2"
-      mongodb-version: "3.6"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: jruby-9.3
+      mongodb-version: "6.0"
       topology: ["standalone", "replica-set", "sharded-cluster"]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "zlib-auth"
+  - matrix_name: "zlib"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+      mongodb-version: "6.0"
       topology: "replica-set"
       compressor: 'zlib'
-      os: ubuntu1804
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "zlib-auth-3.0"
+  - matrix_name: "snappy"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.0"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'zlib'
-      os: ubuntu2004
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zlib-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: "3.6"
-      topology: replica-set
-      compressor: 'zlib'
-      os: ubuntu1604
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "snappy-auth"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+      mongodb-version: "6.0"
       topology: "replica-set"
       compressor: 'snappy'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "snappy-auth-3"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.1"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'snappy'
-      os: ubuntu2004
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "snappy-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: ["3.6"]
-      topology: replica-set
-      compressor: 'snappy'
-      os: ubuntu1604
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-#  https://jira.mongodb.org/browse/RUBY-2723
-#  Enable or remove when the ticket is resolved.
-#  - matrix_name: "snappy-jruby"
-#    matrix_spec:
-#      ruby: jruby-9.2
-#      mongodb-version: "4.4"
-#      topology: replica-set
-#      compressor: 'snappy'
-#      os: ubuntu1804
-#    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#    tasks:
-#      - name: "test-mlaunch"
-
+  # the zstd-ruby gem does not support JRuby (explicitly). However, there is
+  # apparently a zstd-jni gem for JRuby that we could investigate here; if
+  # this test is ever supported to support jruby, the `sample_mri_rubies`
+  # reference should be replaced with `sample_rubies`.
   - matrix_name: "zstd-auth"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: ["ruby-3.2", "ruby-2.7"]
+      mongodb-version: "6.0"
       topology: "replica-set"
       compressor: 'zstd'
-      os: ubuntu1804
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "zstd-auth-3.0"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.0"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'zstd'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zstd-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: "4.2"
-      topology: replica-set
-      compressor: 'zstd'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-#  - matrix_name: "zstd-jruby"
-#    matrix_spec:
-#      ruby: jruby-9.2
-#      mongodb-version: "4.4"
-#      topology: replica-set
-#      compressor: 'zstd'
-#      os: ubuntu1804
-#    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#    tasks:
-#      - name: "test-mlaunch"
-
-#   - matrix_name: "ruby-head"
-#     matrix_spec:
-#       auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-#       ruby: ["ruby-head"]
-#       mongodb-version: "4.2"
-#       topology: "*"
-#       os: ubuntu1804
-#     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#     tasks:
-#       - name: "test-mlaunch"
-
   - matrix_name: "activesupport"
     matrix_spec:
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: '4.4'
+      ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+      mongodb-version: "6.0"
       topology: replica-set
       as: as
-      os: ubuntu1804
+      os: rhel8
     display_name: "AS ${mongodb-version} ${topology} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "bson"
     matrix_spec:
-      ruby: [ruby-3.1, ruby-2.7, jruby-9.2]
-      mongodb-version: '4.4'
+      ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+      mongodb-version: "6.0"
       topology: replica-set
       bson: "*"
-      os: rhel80
+      os: rhel8
     display_name: "AS ${mongodb-version} ${topology} ${ruby} ${bson}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "kerberos-integration-ubuntu"
-    matrix_spec:
-      # Other Rubies segfault
-      ruby: ruby-2.5
-    display_name: "Kerberos integration Ubuntu ${ruby}"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-      - name: "test-kerberos-integration"
-
-  # All skipped because of segfaults
-  #- matrix_name: "kerberos-integration-rhel"
-  #  matrix_spec:
-  #    # Other Rubies segfault
-  #    ruby: [ruby-2.7]
-  #    os: ubuntu1804
-  #  display_name: "Kerberos integration RHEL ${ruby} ${os}"
-  #  tasks:
-  #    - name: "test-kerberos-integration"
-
-  # Kerberos integration tests on debian are omitted due to segfaults:
-  # https://jira.mongodb.org/browse/RUBY-1534
+  # kerberos integration tests are broken (RUBY-3266)
+  # - matrix_name: "kerberos-integration"
+  #   matrix_spec:
+  #     ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.3"]
+  #     os: rhel8
+  #   display_name: "Kerberos integration ${os} ${ruby}"
+  #   tasks:
+  #     - name: "test-kerberos-integration"
 
   - matrix_name: "kerberos-unit"
     matrix_spec:
-      ruby: "ruby-2.6"
-      mongodb-version: "4.2"
+      ruby: "ruby-3.2"
+      mongodb-version: "6.0"
       topology: standalone
-      os: ubuntu1804
+      os: rhel8
       auth-and-ssl: kerberos
     display_name: "Kerberos Tests"
     tasks:
       - name: "test-kerberos"
 
-  - matrix_name: "fle-6.0"
+  - matrix_name: "fle"
     matrix_spec:
       auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-3.1, ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: [replica-set, sharded-cluster]
-      mongodb-version: ['6.0']
-      os: ubuntu2004
+      mongodb-version: [ '6.0', '4.4', '4.2', '4.0' ]
+      os: rhel8
       fle: helper
-    display_name: "FLE: ${mongodb-version} ${ruby}"
+    display_name: "FLE: ${mongodb-version} ${topology} ${ruby}"
     tasks:
       - name: "test-fle"
 
-  - matrix_name: "fle-4.4"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-3.1]
-      topology: standalone
-      mongodb-version: ['4.4']
-      os: ubuntu1804
-      fle: helper
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-4.2"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-2.5]
-      topology: [standalone, replica-set, sharded-cluster]
-      mongodb-version: ["4.2"]
-      os: ubuntu1804
-      fle: helper
-    display_name: "FLE: ${topology} ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-4.0"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-2.6]
-      topology: standalone
-      mongodb-version: ["4.0"]
-      os: ubuntu1804
-      fle: path
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-jruby"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [jruby-9.2, jruby-9.3]
-      topology: standalone
-      mongodb-version: ['5.0']
-      os: ubuntu1804
-      fle: helper
-      docker-distro: ubuntu1804
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle-via-docker"
-
   - matrix_name: aws-auth-regular
     matrix_spec:
-      auth-and-ssl: aws-regular
-      ruby: [ruby-2.7]
+      auth-and-ssl: [ aws-regular, aws-assume-role, aws-ec2, aws-ecs ]
+      ruby: "ruby-3.2"
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu1804
-    display_name: "AWS auth regular: ${mongodb-version} ${ruby}"
+      # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure
+      # the server for certain auth mechanisms. Once run-tests.sh is made smart
+      # enough to install mongosh, and then use either mongo or mongosh
+      # (depending on server version and what's available), we can bump this to
+      # the latest stable db version.
+      mongodb-version: "5.3"
+      os: rhel8
+    display_name: "AWS ${auth-and-ssl} ${mongodb-version} ${ruby}"
     tasks:
       - name: "test-aws-auth"
 
-  - matrix_name: aws-auth-temporary
-    matrix_spec:
-      auth-and-ssl: [aws-assume-role, aws-ec2, aws-ecs]
-      ruby: [ruby-3.1]
-      topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu1804
-    display_name: "AWS auth temporary: ${auth-and-ssl} ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-aws-auth"
-
-  # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210
-  # TODO Add ruby-3.0: RUBY-2717
-  
-  - matrix_name: ocsp-verifier - ruby-2.5
-    matrix_spec:
-      ocsp-verifier: true
-      ruby: ruby-2.5
-      topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
-      docker-distro: debian10
-    display_name: 'OCSP verifier: ${mongodb-version} ${ruby}'
-    tasks: &1
-    - name: test-via-docker
-  - matrix_name: ocsp-verifier - ruby-2.6
-    matrix_spec:
-      ocsp-verifier: true
-      ruby: ruby-2.6
-      topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
-      docker-distro: debian10
-    display_name: 'OCSP verifier: ${mongodb-version} ${ruby}'
-    tasks: *1
-  - matrix_name: ocsp-verifier - ruby-2.7
+  - matrix_name: ocsp-verifier
     matrix_spec:
       ocsp-verifier: true
-      ruby: ruby-2.7
+      # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210
+      ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
-      docker-distro: debian10
-    display_name: 'OCSP verifier: ${mongodb-version} ${ruby}'
-    tasks: *1
-  
+      mongodb-version: "6.0"
+      os: rhel8
+    display_name: "OCSP verifier: ${mongodb-version} ${ruby}"
+    tasks:
+      - name: test-mlaunch
 
   - matrix_name: ocsp-must-staple
     matrix_spec:
       ocsp-algorithm: ecdsa
       ocsp-must-staple: on
       ocsp-delegate: on
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
       auth-and-ssl: noauth-and-ssl
     display_name: "OCSP integration - must staple: ${mongodb-version} ${ruby}"
     tasks:
@@ -1482,10 +1276,10 @@ buildvariants:
     matrix_spec:
       ocsp-algorithm: rsa
       ocsp-status: unknown
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
       auth-and-ssl: noauth-and-ssl
     display_name: "OCSP integration - unknown: ${mongodb-version} ${ruby}"
     tasks:
@@ -1498,10 +1292,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "none"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1512,10 +1306,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "none"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1526,10 +1320,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: fail
       extra-uri-options: "none"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1540,10 +1334,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsInsecure=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1554,10 +1348,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsInsecure=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1568,10 +1362,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsInsecure=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1582,10 +1376,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsAllowInvalidCertificates=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1596,10 +1390,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsAllowInvalidCertificates=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1610,10 +1404,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: pass
       extra-uri-options: "tlsAllowInvalidCertificates=true"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: ["ruby-3.2", "ruby-2.7"]
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -1630,10 +1424,29 @@ buildvariants:
       ocsp-status: [valid, unknown]
       ocsp-delegate: '*'
       ocsp-connectivity: pass
-      ruby: jruby-9.2
+      ruby: jruby-9.3
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: "6.0"
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
+
+  - matrix_name: "atlas"
+    matrix_spec:
+      ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5", "jruby-9.3", "jruby-9.2"]
+      os: rhel8
+    display_name: "Atlas tests ${ruby}"
+    tasks:
+       - name: test-atlas
+
+  - matrix_name: "serverless"
+    matrix_spec:
+      # https://jira.mongodb.org/browse/RUBY-3217
+      # ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5", "jruby-9.3", "jruby-9.2"]
+      ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "ruby-2.6", "ruby-2.5"]
+      fle: path
+      os: rhel8
+    display_name: "Atlas serverless ${ruby} single mongos"
+    tasks:
+       - name: test-serverless
diff --git a/.evergreen/config/atlas.yml.erb b/.evergreen/config/atlas.yml.erb
deleted file mode 100644
index bc7f63a1ac..0000000000
--- a/.evergreen/config/atlas.yml.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-buildvariants:
-  - matrix_name: "all"
-    matrix_spec:
-      ruby: ["ruby-2.5", "ruby-2.6", "ruby-2.7", "ruby-3.0", "ruby-3.1", "jruby-9.2"]
-    display_name: "Atlas tests ${ruby}"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-       - name: test-atlas
-
-  - matrix_name: "serverless"
-    matrix_spec:
-      ruby: ["ruby-2.5", "ruby-2.6", "ruby-2.7", "ruby-3.0", "ruby-3.1", "jruby-9.2"]
-    display_name: "Atlas serverless ${ruby} single mongos"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-       - name: test-serverless
diff --git a/.evergreen/config/axes.yml.erb b/.evergreen/config/axes.yml.erb
index ad4d8b25b9..5d1b7c439b 100644
--- a/.evergreen/config/axes.yml.erb
+++ b/.evergreen/config/axes.yml.erb
@@ -17,6 +17,11 @@ axes:
         display_name: "latest"
         variables:
           MONGODB_VERSION: "latest"
+          CRYPT_SHARED_VERSION: "latest"
+      - id: "7.0"
+        display_name: "7.0"
+        variables:
+          MONGODB_VERSION: "7.0"
       - id: "6.0"
         display_name: "6.0"
         variables:
@@ -25,18 +30,22 @@ axes:
         display_name: "5.3"
         variables:
           MONGODB_VERSION: "5.3"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "5.0"
         display_name: "5.0"
         variables:
           MONGODB_VERSION: "5.0"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.4"
         display_name: "4.4"
         variables:
           MONGODB_VERSION: "4.4"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.2"
         display_name: "4.2"
         variables:
           MONGODB_VERSION: "4.2"
+          CRYPT_SHARED_VERSION: "6.0.5"
       - id: "4.0"
         display_name: "4.0"
         variables:
@@ -129,6 +138,14 @@ axes:
   - id: "ruby"
     display_name: Ruby Version
     values:
+      - id: "ruby-3.2"
+        display_name: ruby-3.2
+        variables:
+          RVM_RUBY: "ruby-3.2"
+      - id: "ruby-3.1"
+        display_name: ruby-3.1
+        variables:
+          RVM_RUBY: "ruby-3.1"
       - id: "ruby-3.1"
         display_name: ruby-3.1
         variables:
@@ -161,33 +178,31 @@ axes:
         display_name: jruby-9.3
         variables:
           RVM_RUBY: "jruby-9.3"
+      - id: "jruby-9.4"
+        display_name: jruby-9.4
+        variables:
+          RVM_RUBY: "jruby-9.4"
 
   - id: "os"
     display_name: OS
     values:
-      - id: ubuntu2004
-        display_name: "Ubuntu 20.04"
-        run_on: ubuntu2004-small
-      - id: ubuntu1804
-        display_name: "Ubuntu 18.04"
-        run_on: ubuntu1804-small
-      - id: ubuntu1604
-        display_name: "Ubuntu 16.04"
-        run_on: ubuntu1604-small
-      - id: rhel80
+      - id: debian11
+        display_name: "Debian 11"
+        run_on: debian11-small
+      - id: ubuntu2204
+        display_name: "Ubuntu 22.04"
+        run_on: ubuntu2204-small
+      - id: rhel8
         display_name: "RHEL 8"
         run_on: rhel80-small
-      - id: rhel70
-        display_name: "RHEL 7"
-        run_on: rhel70-small
-      - id: rhel62
-        display_name: "RHEL 6"
-        run_on: rhel62-small
+      - id: rhel8-arm
+        display_name: "RHEL 8 ARM64"
+        run_on: rhel82-arm64-small
 
   - id: docker-distro
     display_name: Docker Distro
     values:
-      <% %w(debian92 debian10 ubuntu1804).each do |distro| %>
+      <% %w(debian11 ubuntu2204).each do |distro| %>
         - id: <%= distro %>
           display_name: <%= distro %>
           variables:
@@ -287,7 +302,7 @@ axes:
     values:
       - id: mmapv1
         display_name: MMAPv1
-        run_on: ubuntu1604-small
+        run_on: rhel80-small
         variables:
           MMAPV1: 'true'
 
diff --git a/.evergreen/config/common.yml.erb b/.evergreen/config/common.yml.erb
index b3bde64c2c..23c702e25c 100644
--- a/.evergreen/config/common.yml.erb
+++ b/.evergreen/config/common.yml.erb
@@ -11,13 +11,7 @@ pre_error_fails_task: true
 command_type: system
 
 # Protect ourself against rogue test case, or curl gone wild, that runs forever.
-<% if @atlas %>
-exec_timeout_secs: 1800
-<% else %>
-exec_timeout_secs: 2700
-<% end %>
-
-<% egos_wrap = "./egos stdbuf -i0 -o0 -e0" %>
+exec_timeout_secs: 3600
 
 # What to do when evergreen hits the timeout (`post:` tasks are run automatically)
 timeout:
@@ -40,85 +34,6 @@ functions:
 
           git submodule update --init --recursive
 
-  "fetch egos":
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          set -e
-
-          # Python version:
-          #curl -sfLo egos https://raw.githubusercontent.com/p-mongo/egos/master/egos
-
-          # C version:
-          curl --retry 3 -sfLo egos.c https://raw.githubusercontent.com/p-mongo/egos/master/egos.c
-          gcc -O2 -g -oegos -lrt egos.c
-
-  <% if @atlas %>
-  "create expansions":
-    # Make an evergreen expansion file with dynamic values
-    - command: shell.exec
-      params:
-        working_dir: "src"
-        script: |
-          # Get the current unique version of this checkout
-          if [ "${is_patch}" = "true" ]; then
-            CURRENT_VERSION=$(git describe)-patch-${version_id}
-          else
-            CURRENT_VERSION=latest
-          fi
-
-          export DRIVERS_TOOLS="$(pwd)/../drivers-tools"
-
-          # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
-          if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
-            export DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)
-          fi
-
-          export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration"
-          export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
-          export UPLOAD_BUCKET="${project}"
-          export PROJECT_DIRECTORY="$(pwd)"
-
-          cat <<EOT > expansion.yml
-          CURRENT_VERSION: "$CURRENT_VERSION"
-          DRIVERS_TOOLS: "$DRIVERS_TOOLS"
-          MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME"
-          MONGODB_BINARIES: "$MONGODB_BINARIES"
-          UPLOAD_BUCKET: "$UPLOAD_BUCKET"
-          PROJECT_DIRECTORY: "$PROJECT_DIRECTORY"
-          PREPARE_SHELL: |
-            set -o errexit
-            set -o xtrace
-            export DRIVERS_TOOLS="$DRIVERS_TOOLS"
-            export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME"
-            export MONGODB_BINARIES="$MONGODB_BINARIES"
-            export UPLOAD_BUCKET="$UPLOAD_BUCKET"
-            export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
-
-            # TMPDIR cannot be too long, see
-            # https://github.com/broadinstitute/cromwell/issues/3647.
-            # Why is it even set at all?
-            #export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
-            export PATH="$MONGODB_BINARIES:$PATH"
-            export PROJECT="${project}"
-            export ATLAS_REPLICA_SET_URI="${atlas_replica_set_uri}"
-            export ATLAS_SHARDED_URI="${atlas_sharded_uri}"
-            export ATLAS_FREE_TIER_URI="${atlas_free_tier_uri}"
-            export ATLAS_TLS11_URI="${atlas_tls11_uri}"
-            export ATLAS_TLS12_URI="${atlas_tls12_uri}"
-            export ATLAS_SERVERLESS_URI="${atlas_serverless_uri}"
-            export ATLAS_SERVERLESS_LB_URI="${atlas_serverless_lb_uri}"
-            export RVM_RUBY="${RVM_RUBY}"
-            export SERVERLESS_DRIVERS_GROUP="${SERVERLESS_DRIVERS_GROUP}"
-            export SERVERLESS_API_PUBLIC_KEY="${SERVERLESS_API_PUBLIC_KEY}"
-            export SERVERLESS_API_PRIVATE_KEY="${SERVERLESS_API_PRIVATE_KEY}"
-            export SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}"
-            export SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}"
-          EOT
-          # See what we've done
-          cat expansion.yml
-  <% else %>
   "create expansions":
     # Make an evergreen expansion file with dynamic values
     - command: shell.exec
@@ -132,7 +47,7 @@ functions:
             CURRENT_VERSION=latest
           fi
 
-          export DRIVERS_TOOLS="$(pwd)/../drivers-tools"
+          export DRIVERS_TOOLS="$(pwd)/.mod/drivers-evergreen-tools"
 
           # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
           if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
@@ -173,6 +88,7 @@ functions:
             export COMPRESSOR=${COMPRESSOR}
             export RVM_RUBY="${RVM_RUBY}"
             export MONGODB_VERSION=${MONGODB_VERSION}
+            export CRYPT_SHARED_VERSION=${CRYPT_SHARED_VERSION}
             export FCV=${FCV}
             export MONGO_RUBY_DRIVER_LINT=${LINT}
             export RETRY_READS=${RETRY_READS}
@@ -195,11 +111,32 @@ functions:
             export OCSP_MUST_STAPLE="${OCSP_MUST_STAPLE}"
             export OCSP_CONNECTIVITY="${OCSP_CONNECTIVITY}"
             export OCSP_VERIFIER="${OCSP_VERIFIER}"
+
+            export ATLAS_REPLICA_SET_URI="${atlas_replica_set_uri}"
+            export ATLAS_SHARDED_URI="${atlas_sharded_uri}"
+            export ATLAS_FREE_TIER_URI="${atlas_free_tier_uri}"
+            export ATLAS_TLS11_URI="${atlas_tls11_uri}"
+            export ATLAS_TLS12_URI="${atlas_tls12_uri}"
+            export ATLAS_SERVERLESS_URI="${atlas_serverless_uri}"
+            export ATLAS_SERVERLESS_LB_URI="${atlas_serverless_lb_uri}"
+            export RVM_RUBY="${RVM_RUBY}"
+            export SERVERLESS_DRIVERS_GROUP="${SERVERLESS_DRIVERS_GROUP}"
+            export SERVERLESS_API_PUBLIC_KEY="${SERVERLESS_API_PUBLIC_KEY}"
+            export SERVERLESS_API_PRIVATE_KEY="${SERVERLESS_API_PRIVATE_KEY}"
+            export SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}"
+            export SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}"
+
+            # Needed for generating temporary aws credentials.
+            if [ -n "${FLE}" ];
+            then
+              export AWS_ACCESS_KEY_ID="${fle_aws_key}"
+              export AWS_SECRET_ACCESS_KEY="${fle_aws_secret}"
+              export AWS_DEFAULT_REGION="${fle_aws_region}"
+            fi
           EOT
 
           # See what we've done
           cat expansion.yml
-  <% end %>
 
     # Load the expansion file to make an evergreen variable with the current
     # unique version
@@ -240,6 +177,13 @@ functions:
           IAM_AUTH_ECS_SUBNET_A="${iam_auth_ecs_subnet_a}"
           IAM_AUTH_ECS_SUBNET_B="${iam_auth_ecs_subnet_b}"
           IAM_AUTH_ECS_TASK_DEFINITION="${iam_auth_ecs_task_definition}"
+
+          IAM_WEB_IDENTITY_ISSUER="${iam_web_identity_issuer}"
+          IAM_WEB_IDENTITY_JWKS_URI="${iam_web_identity_jwks_uri}"
+          IAM_WEB_IDENTITY_RSA_KEY="${iam_web_identity_rsa_key}"
+          IAM_WEB_IDENTITY_TOKEN_FILE="${iam_web_identity_token_file}"
+          IAM_AUTH_ASSUME_WEB_ROLE_NAME="${iam_auth_assume_web_role_name}"
+
           EOT
 
   "export FLE credentials":
@@ -421,7 +365,7 @@ functions:
         script: |
           ${PREPARE_SHELL}
           set -x
-          <%= egos_wrap %> .evergreen/test-on-docker -d ${os} MONGODB_VERSION=${mongodb-version} TOPOLOGY=${topology} RVM_RUBY=${ruby} -s .evergreen/run-tests.sh TEST_CMD=true ${PRELOAD_ARG}
+          .evergreen/test-on-docker -d ${os} MONGODB_VERSION=${mongodb-version} TOPOLOGY=${topology} RVM_RUBY=${ruby} -s .evergreen/run-tests.sh TEST_CMD=true ${PRELOAD_ARG}
 
   "run tests":
     - command: shell.exec
@@ -431,7 +375,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          <%= egos_wrap %> .evergreen/run-tests.sh
+          .evergreen/run-tests.sh
 
   "run tests via docker":
     - command: shell.exec
@@ -441,7 +385,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          <%= egos_wrap %> .evergreen/run-tests-docker.sh
+          .evergreen/run-tests-docker.sh
 
   "run AWS auth tests":
     - command: shell.exec
@@ -451,7 +395,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          <%= egos_wrap %> .evergreen/run-tests-aws-auth.sh
+          .evergreen/run-tests-aws-auth.sh
 
   "run Kerberos unit tests":
     - command: shell.exec
@@ -461,7 +405,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          <%= egos_wrap %> .evergreen/run-tests-kerberos-unit.sh
+          .evergreen/run-tests-kerberos-unit.sh
 
   "run Kerberos integration tests":
     - command: shell.exec
@@ -471,7 +415,7 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          <%= egos_wrap %> .evergreen/run-tests-kerberos-integration.sh
+          .evergreen/run-tests-kerberos-integration.sh
 
   "run Atlas tests":
     - command: shell.exec
@@ -506,11 +450,10 @@ functions:
         working_dir: "src"
         script: |
           ${PREPARE_SHELL}
-          SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
+          SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" FLE="${FLE}" SERVERLESS_MONGODB_VERSION="${SERVERLESS_MONGODB_VERSION}" .evergreen/run-tests-serverless.sh
 
 pre:
   - func: "fetch source"
-  - func: "fetch egos"
   - func: "create expansions"
 
 post:
@@ -520,20 +463,16 @@ post:
   - func: "upload mo artifacts"
   #- func: "upload test results"
   - func: "upload test results to s3"
-  <% if @atlas %>
   - func: "delete serverless instance"
-  <% end %>
 
 tasks:
-
-  <% if @atlas %>
   - name: "test-atlas"
     commands:
       - func: "run Atlas tests"
   - name: "test-serverless"
     commands:
+      - func: "export FLE credentials"
       - func: "run serverless tests"
-  <% else %>
   - name: "test-docker"
     commands:
       - func: "build and test docker image"
@@ -562,4 +501,3 @@ tasks:
     commands:
       - func: "export AWS auth credentials"
       - func: "run AWS auth tests"
-  <% end %>
diff --git a/.evergreen/config/standard.yml.erb b/.evergreen/config/standard.yml.erb
index e2e73448cb..93d5185cdb 100644
--- a/.evergreen/config/standard.yml.erb
+++ b/.evergreen/config/standard.yml.erb
@@ -1,148 +1,148 @@
-<% tt = %w(standalone replica-set sharded-cluster) %>
+<%
+  topologies = %w( standalone replica-set sharded-cluster )
 
-buildvariants:
-  - matrix_name: "docker"
-    matrix_spec:
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-      preload: preload
-    display_name: "Docker ${os} ${mongodb-version} ${topology} ${ruby} ${preload}"
-    tasks:
-      - name: "test-docker"
+  # jruby-9.4 is available in the toolchain, but tests break. It will
+  # require some investigation. Once available:
+  #  1. replace jruby-9.3 with jruby-9.4 in `recent_rubies` and
+  #     `sample_rubies`.
+  #  2. add jruby-9.4 to the front of `jrubies`.
 
-  - matrix_name: "docker"
-    matrix_spec:
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-      preload: nopreload
-    display_name: "Docker ${os} ${mongodb-version} ${topology} ${ruby} ${preload}"
-    tasks:
-      - name: "test-docker"
+  # latest_ruby = the most recently released, stable version of Ruby
+  #    (make sure this version is being built by 10gen/mongo-ruby-toolchain)
+  latest_ruby = "ruby-3.2".inspect # so it gets quoted as a string
 
-  - matrix_name: "mongo-latest"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['latest']
-      topology: <%= tt %>
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
+  # these are used for testing against a few recent ruby versions
+  recent_rubies = %w( ruby-3.2 ruby-3.1 jruby-9.3 )
 
-  - matrix_name: "mongo-6.0"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['6.0']
-      topology: <%= tt %>
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
+  # this is a list of the most most recent 3.x and 2.x MRI ruby versions
+  sample_mri_rubies = %w( ruby-3.2 ruby-2.7 )
 
-  - matrix_name: "mongo-5.3"
-    matrix_spec:
-      auth-and-ssl: noauth-and-nossl
-      ruby: ["ruby-3.1"]
-      mongodb-version: ['5.3']
-      topology: <%= tt %>
-      os: ubuntu2004
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
+  # as above, but including the most recent JRuby release
+  sample_rubies = sample_mri_rubies + %w( jruby-9.3 )
+
+  # older Ruby versions provided by 10gen/mongo-ruby-toolchain
+  older_rubies = %w( ruby-3.0 ruby-2.7 ruby-2.6 ruby-2.5 )
+
+  # all supported JRuby versions provided by 10gen/mongo-ruby-toolchain
+  jrubies = %w( jruby-9.3 jruby-9.2 )
 
-  - matrix_name: "mongo-4.2"
+  supported_mri_rubies = %w( ruby-3.2 ruby-3.1 ruby-3.0
+                             ruby-2.7 ruby-2.6 ruby-2.5 )
+
+  supported_rubies = supported_mri_rubies + jrubies
+
+  # The latest stable version of MongoDB
+  latest_stable_mdb = "6.0".inspect # so it gets quoted as a string
+
+  # A few of the most recent MongoDB versions
+  recent_mdb = %w( 6.0 5.3 )
+  latest_5x_mdb = "5.3".inspect # so it gets quoted as a string
+%>
+
+buildvariants:
+  - matrix_name: "auth/ssl"
     matrix_spec:
       auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-2.7", "ruby-2.5"]
-      mongodb-version: ['4.2']
-      topology: <%= tt %>
-      os: ubuntu1804
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: rhel8
+    display_name: ${auth-and-ssl} ${ruby} ${topology}
+    tasks:
+      - name: "test-mlaunch"
+
+  - matrix_name: "mongo-recent"
+    matrix_spec:
+      ruby: <%= recent_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: ['rhel8']
+      # There is no latest for ubuntu2204, so we can't test it here.
+      # os: ['rhel8', 'ubuntu2204']
+    display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}"
+    tasks:
+      - name: "test-mlaunch"
+
+  # Only JRuby-9.4 is built for arm in the current toolchain, but 9.4 has other
+  # issues. Either we fix those issues, or we build 9.3 for arm in the toolchain,
+  # and hope it fixes the issues... Until then, we can only test MRI ruby on arm.
+  #
+  # further: 'latest' is broken until the QE2 changes are merged (RUBY-3211,
+  # RUBY-3226)
+  #
+  # further: ARM specs on 2.18-stable are broken because nokogiri won't install
+  # a compatible binary. Disabling for now.
+  #
+  # - matrix_name: "mongo-recent-arm"
+  #   matrix_spec:
+  #     ruby: <%= latest_ruby %>
+  #     #mongodb-version: ['latest', '6.0']
+  #     mongodb-version: ['6.0']
+  #     topology: <%= topologies %>
+  #     os: 'rhel8-arm'
+  #   display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}"
+  #   tasks:
+  #     - name: "test-mlaunch"
+
+  - matrix_name: "mongo-5.x"
+    matrix_spec:
+      ruby: <%= sample_rubies %>
+      mongodb-version: ['5.3', '5.0']
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "mongo-4.0"
+  - matrix_name: "mongo-4.x"
     matrix_spec:
-      auth-and-ssl: ["noauth-and-ssl", "auth-and-nossl"]
-      ruby: "ruby-2.6"
-      mongodb-version: ['4.0']
-      topology: <%= tt %>
-      os: ubuntu1804
+      ruby: <%= older_rubies %>
+      mongodb-version: ['4.4', '4.2', '4.0']
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "mongo-3.6"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
       ruby: "ruby-2.5"
       mongodb-version: ['3.6']
-      topology: <%= tt %>
-      os: ubuntu1604
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "mongo-4.4"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: '4.4'
-      topology: <%= tt %>
-      os: ubuntu1804
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "mongo-5.0"
-    matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1", "ruby-2.7"]
-      mongodb-version: '5.0'
-      topology: '*'
-      os: ubuntu1804
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "single-lb"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.0"]
-      mongodb-version: '5.0'
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: load-balanced
       single-mongos: single-mongos
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} single-lb ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "mongo-5.0-api-version"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: ["ruby-3.1"]
+      ruby: <%= latest_ruby %>
       mongodb-version: '5.0'
       topology: standalone
       api-version-required: yes
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} api-version-required ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "single-mongos"
     matrix_spec:
-      auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-      ruby: "ruby-2.6"
-      mongodb-version: ['4.0', '4.2', '4.4']
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: "sharded-cluster"
       single-mongos: single-mongos
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} single-mongos ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -150,10 +150,10 @@ buildvariants:
   - matrix_name: "no-retry-reads"
     matrix_spec:
       retry-reads: no-retry-reads
-      ruby: "ruby-2.6"
-      mongodb-version: ["4.0"]
-      topology: <%= tt %>
-      os: ubuntu1804
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${retry-reads} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -161,10 +161,10 @@ buildvariants:
   - matrix_name: "no-retry-writes"
     matrix_spec:
       retry-writes: no-retry-writes
-      ruby: ["ruby-2.7"]
-      mongodb-version: ["4.0"]
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: [replica-set, sharded-cluster]
-      os: ubuntu1804
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${retry-writes} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -173,20 +173,20 @@ buildvariants:
     matrix_spec:
       ruby: "ruby-2.5"
       mongodb-version: ['3.6', '4.0']
-      topology: <%= tt %>
+      topology: <%= topologies %>
       storage-engine: mmapv1
-      os: ubuntu1604
+      os: rhel8
     display_name: "${mongodb-version} ${topology} mmapv1 ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "lint-5.0"
+  - matrix_name: "lint"
     matrix_spec:
       lint: on
-      ruby: ["ruby-3.1", "ruby-2.7"]
-      mongodb-version: ["5.0"]
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: '*'
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${lint} ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -194,10 +194,10 @@ buildvariants:
   - matrix_name: "fork"
     matrix_spec:
       fork: on
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: ["4.4"]
-      topology: <%= tt %>
-      os: ubuntu1804
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} fork ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -205,10 +205,10 @@ buildvariants:
   - matrix_name: "solo"
     matrix_spec:
       solo: on
-      ruby: [ruby-3.1, ruby-3.0, ruby-2.7, ruby-2.6, ruby-2.5, jruby-9.2, jruby-9.3]
-      mongodb-version: ["4.4"]
-      topology: <%= tt %>
-      os: ubuntu1804
+      ruby: <%= supported_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} solo ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -219,7 +219,7 @@ buildvariants:
       ruby: ["ruby-2.7"]
       mongodb-version: ['4.2', '4.0', '3.6']
       topology: replica-set
-      os: ubuntu1604
+      os: rhel8
     display_name: "${mongodb-version} ${topology} stress ${ruby}"
     tasks:
       - name: "test-mlaunch"
@@ -227,354 +227,175 @@ buildvariants:
   - matrix_name: "stress"
     matrix_spec:
       stress: on
-      ruby: ["ruby-2.7"]
-      mongodb-version: ['5.0', '4.4']
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= recent_mdb %>
       topology: replica-set
-      os: ubuntu2004
+      os: rhel8
     display_name: "${mongodb-version} ${topology} stress ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "x509-tests"
-    matrix_spec:
-      auth-and-ssl: "x509"
-      ruby: "ruby-2.7"
-      mongodb-version: "4.2"
-      topology: standalone
-      os: ubuntu1804
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
+  # The X.509 tests are failing with exceptions being expected, but never
+  # raised. (RUBY-3267)
+  #
+  # - matrix_name: "x509-tests"
+  #   matrix_spec:
+  #     auth-and-ssl: "x509"
+  #     ruby: <%= latest_ruby %>
+  #     # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure
+  #     # the server for certain auth mechanisms. Once run-tests.sh is made smart
+  #     # enough to install mongosh, and then use either mongo or mongosh
+  #     # (depending on server version and what's available), we can bump this to
+  #     # the latest stable db version.
+  #     mongodb-version: <%= latest_5x_mdb %>
+  #     topology: standalone
+  #     os: rhel8
+  #   display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
+  #   tasks:
+  #     - name: "test-mlaunch"
 
   - matrix_name: "jruby-auth"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: "jruby-9.3"
-      mongodb-version: "4.2"
-      topology: <%= tt %>
-      os: rhel80
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: <%= jrubies.first %>
+      mongodb-version: <%= latest_stable_mdb %>
+      topology: <%= topologies %>
+      os: rhel8
     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "jruby-noauth"
+  - matrix_name: "zlib"
     matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "jruby-9.2"
-      mongodb-version: "3.6"
-      topology: <%= tt %>
-      os: ubuntu1804
-    display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zlib-auth"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: <%= sample_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: "replica-set"
       compressor: 'zlib'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zlib-auth-3.0"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.0"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'zlib'
-      os: ubuntu2004
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zlib-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: "3.6"
-      topology: replica-set
-      compressor: 'zlib'
-      os: ubuntu1604
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "snappy-auth"
+  - matrix_name: "snappy"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: <%= sample_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: "replica-set"
       compressor: 'snappy'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "snappy-auth-3"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.1"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'snappy'
-      os: ubuntu2004
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "snappy-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: ["3.6"]
-      topology: replica-set
-      compressor: 'snappy'
-      os: ubuntu1604
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-#  https://jira.mongodb.org/browse/RUBY-2723
-#  Enable or remove when the ticket is resolved.
-#  - matrix_name: "snappy-jruby"
-#    matrix_spec:
-#      ruby: jruby-9.2
-#      mongodb-version: "4.4"
-#      topology: replica-set
-#      compressor: 'snappy'
-#      os: ubuntu1804
-#    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#    tasks:
-#      - name: "test-mlaunch"
-
+  # the zstd-ruby gem does not support JRuby (explicitly). However, there is
+  # apparently a zstd-jni gem for JRuby that we could investigate here; if
+  # this test is ever supported to support jruby, the `sample_mri_rubies`
+  # reference should be replaced with `sample_rubies`.
   - matrix_name: "zstd-auth"
     matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-2.7"]
-      mongodb-version: "4.4"
+      auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ]
+      ruby: <%= sample_mri_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: "replica-set"
       compressor: 'zstd'
-      os: ubuntu1804
+      os: rhel8
     display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "zstd-auth-3.0"
-    matrix_spec:
-      auth-and-ssl: "auth-and-ssl"
-      ruby: ["ruby-3.0"]
-      mongodb-version: "4.4"
-      topology: "standalone"
-      compressor: 'zstd'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-  - matrix_name: "zstd-noauth"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: "ruby-2.5"
-      mongodb-version: "4.2"
-      topology: replica-set
-      compressor: 'zstd'
-      os: ubuntu1804
-    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-    tasks:
-      - name: "test-mlaunch"
-
-#  - matrix_name: "zstd-jruby"
-#    matrix_spec:
-#      ruby: jruby-9.2
-#      mongodb-version: "4.4"
-#      topology: replica-set
-#      compressor: 'zstd'
-#      os: ubuntu1804
-#    display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#    tasks:
-#      - name: "test-mlaunch"
-
-#   - matrix_name: "ruby-head"
-#     matrix_spec:
-#       auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
-#       ruby: ["ruby-head"]
-#       mongodb-version: "4.2"
-#       topology: "*"
-#       os: ubuntu1804
-#     display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}"
-#     tasks:
-#       - name: "test-mlaunch"
-
   - matrix_name: "activesupport"
     matrix_spec:
-      ruby: ["ruby-3.0", "ruby-2.7"]
-      mongodb-version: '4.4'
+      ruby: <%= sample_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: replica-set
       as: as
-      os: ubuntu1804
+      os: rhel8
     display_name: "AS ${mongodb-version} ${topology} ${ruby}"
     tasks:
       - name: "test-mlaunch"
 
   - matrix_name: "bson"
     matrix_spec:
-      ruby: [ruby-3.1, ruby-2.7, jruby-9.2]
-      mongodb-version: '4.4'
+      ruby: <%= sample_rubies %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: replica-set
       bson: "*"
-      os: rhel80
+      os: rhel8
     display_name: "AS ${mongodb-version} ${topology} ${ruby} ${bson}"
     tasks:
       - name: "test-mlaunch"
 
-  - matrix_name: "kerberos-integration-ubuntu"
-    matrix_spec:
-      # Other Rubies segfault
-      ruby: ruby-2.5
-    display_name: "Kerberos integration Ubuntu ${ruby}"
-    run_on:
-      - ubuntu1804-small
-    tasks:
-      - name: "test-kerberos-integration"
-
-  # All skipped because of segfaults
-  #- matrix_name: "kerberos-integration-rhel"
-  #  matrix_spec:
-  #    # Other Rubies segfault
-  #    ruby: [ruby-2.7]
-  #    os: ubuntu1804
-  #  display_name: "Kerberos integration RHEL ${ruby} ${os}"
-  #  tasks:
-  #    - name: "test-kerberos-integration"
-
-  # Kerberos integration tests on debian are omitted due to segfaults:
-  # https://jira.mongodb.org/browse/RUBY-1534
+  # kerberos integration tests are broken (RUBY-3266)
+  # - matrix_name: "kerberos-integration"
+  #   matrix_spec:
+  #     ruby: <%= sample_rubies %>
+  #     os: rhel8
+  #   display_name: "Kerberos integration ${os} ${ruby}"
+  #   tasks:
+  #     - name: "test-kerberos-integration"
 
   - matrix_name: "kerberos-unit"
     matrix_spec:
-      ruby: "ruby-2.6"
-      mongodb-version: "4.2"
+      ruby: <%= latest_ruby %>
+      mongodb-version: <%= latest_stable_mdb %>
       topology: standalone
-      os: ubuntu1804
+      os: rhel8
       auth-and-ssl: kerberos
     display_name: "Kerberos Tests"
     tasks:
       - name: "test-kerberos"
 
-  - matrix_name: "fle-6.0"
+  - matrix_name: "fle"
     matrix_spec:
       auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-3.1, ruby-3.0, ruby-2.7]
+      ruby: <%= sample_mri_rubies %>
       topology: [replica-set, sharded-cluster]
-      mongodb-version: ['6.0']
-      os: ubuntu2004
+      mongodb-version: [ '6.0', '4.4', '4.2', '4.0' ]
+      os: rhel8
       fle: helper
-    display_name: "FLE: ${mongodb-version} ${ruby}"
+    display_name: "FLE: ${mongodb-version} ${topology} ${ruby}"
     tasks:
       - name: "test-fle"
 
-  - matrix_name: "fle-4.4"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-3.1]
-      topology: standalone
-      mongodb-version: ['4.4']
-      os: ubuntu1804
-      fle: helper
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-4.2"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-2.5]
-      topology: [standalone, replica-set, sharded-cluster]
-      mongodb-version: ["4.2"]
-      os: ubuntu1804
-      fle: helper
-    display_name: "FLE: ${topology} ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-4.0"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [ruby-2.6]
-      topology: standalone
-      mongodb-version: ["4.0"]
-      os: ubuntu1804
-      fle: path
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle"
-
-  - matrix_name: "fle-jruby"
-    matrix_spec:
-      auth-and-ssl: "noauth-and-nossl"
-      ruby: [jruby-9.2, jruby-9.3]
-      topology: standalone
-      mongodb-version: ['5.0']
-      os: ubuntu1804
-      fle: helper
-      docker-distro: ubuntu1804
-    display_name: "FLE: ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-fle-via-docker"
-
   - matrix_name: aws-auth-regular
     matrix_spec:
-      auth-and-ssl: aws-regular
-      ruby: [ruby-2.7]
+      auth-and-ssl: [ aws-regular, aws-assume-role, aws-ec2, aws-ecs ]
+      ruby: <%= latest_ruby %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu1804
-    display_name: "AWS auth regular: ${mongodb-version} ${ruby}"
+      # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure
+      # the server for certain auth mechanisms. Once run-tests.sh is made smart
+      # enough to install mongosh, and then use either mongo or mongosh
+      # (depending on server version and what's available), we can bump this to
+      # the latest stable db version.
+      mongodb-version: <%= latest_5x_mdb %>
+      os: rhel8
+    display_name: "AWS ${auth-and-ssl} ${mongodb-version} ${ruby}"
     tasks:
       - name: "test-aws-auth"
 
-  - matrix_name: aws-auth-temporary
-    matrix_spec:
-      auth-and-ssl: [aws-assume-role, aws-ec2, aws-ecs]
-      ruby: [ruby-3.1]
-      topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu1804
-    display_name: "AWS auth temporary: ${auth-and-ssl} ${mongodb-version} ${ruby}"
-    tasks:
-      - name: "test-aws-auth"
-
-  # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210
-  # TODO Add ruby-3.0: RUBY-2717
-  <%|= standard_debian_rubies(%w(ruby-2.5 ruby-2.6 ruby-2.7), key: 'docker-distro') do %>
-    matrix_name: ocsp-verifier
+  - matrix_name: ocsp-verifier
     matrix_spec:
       ocsp-verifier: true
       # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210
-      ruby: [ruby-3.0, ruby-2.7, ruby-2.6, ruby-2.5]
+      ruby: <%= supported_mri_rubies %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: <%= latest_stable_mdb %>
+      os: rhel8
     display_name: "OCSP verifier: ${mongodb-version} ${ruby}"
     tasks:
-      - name: test-via-docker
-  <%| end %>
+      - name: test-mlaunch
 
   - matrix_name: ocsp-must-staple
     matrix_spec:
       ocsp-algorithm: ecdsa
       ocsp-must-staple: on
       ocsp-delegate: on
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: <%= sample_mri_rubies %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: <%= latest_stable_mdb %>
+      os: rhel8
       auth-and-ssl: noauth-and-ssl
     display_name: "OCSP integration - must staple: ${mongodb-version} ${ruby}"
     tasks:
@@ -584,10 +405,10 @@ buildvariants:
     matrix_spec:
       ocsp-algorithm: rsa
       ocsp-status: unknown
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: <%= sample_mri_rubies %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: <%= latest_stable_mdb %>
+      os: rhel8
       auth-and-ssl: noauth-and-ssl
     display_name: "OCSP integration - unknown: ${mongodb-version} ${ruby}"
     tasks:
@@ -614,10 +435,10 @@ buildvariants:
       ocsp-delegate: '*'
       ocsp-connectivity: <%= outcome %>
       extra-uri-options: "<%= extra_uri_options %>"
-      ruby: [ruby-3.0, ruby-2.7]
+      ruby: <%= sample_mri_rubies %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: <%= latest_stable_mdb %>
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
@@ -635,10 +456,29 @@ buildvariants:
       ocsp-status: [valid, unknown]
       ocsp-delegate: '*'
       ocsp-connectivity: pass
-      ruby: jruby-9.2
+      ruby: <%= jrubies.first %>
       topology: standalone
-      mongodb-version: '4.4'
-      os: ubuntu2004
+      mongodb-version: <%= latest_stable_mdb %>
+      os: rhel8
     display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${mongodb-version} ${ruby}"
     tasks:
       - name: test-mlaunch
+
+  - matrix_name: "atlas"
+    matrix_spec:
+      ruby: <%= supported_rubies %>
+      os: rhel8
+    display_name: "Atlas tests ${ruby}"
+    tasks:
+       - name: test-atlas
+
+  - matrix_name: "serverless"
+    matrix_spec:
+      # https://jira.mongodb.org/browse/RUBY-3217
+      # ruby: <%= supported_rubies %>
+      ruby: <%= supported_mri_rubies %>
+      fle: path
+      os: rhel8
+    display_name: "Atlas serverless ${ruby} single mongos"
+    tasks:
+       - name: test-serverless
diff --git a/.evergreen/download-mongodb.sh b/.evergreen/download-mongodb.sh
new file mode 120000
index 0000000000..d0b2306cc8
--- /dev/null
+++ b/.evergreen/download-mongodb.sh
@@ -0,0 +1 @@
+../.mod/drivers-evergreen-tools/.evergreen/download-mongodb.sh
\ No newline at end of file
diff --git a/.evergreen/run-tests-atlas.sh b/.evergreen/run-tests-atlas.sh
index fd49553c43..bd1732ce27 100755
--- a/.evergreen/run-tests-atlas.sh
+++ b/.evergreen/run-tests-atlas.sh
@@ -21,7 +21,7 @@ do
   # ${!foo} syntax is bash specific:
   # https://stackoverflow.com/questions/14049057/bash-expand-variable-in-a-variable
   export ATLAS_URI="${!uri}"
-  
+
   if test -z "$ATLAS_URI"; then
     echo "The \$$uri environment variable was not set" 1>&2
     test_status=1
diff --git a/.evergreen/run-tests-aws-auth.sh b/.evergreen/run-tests-aws-auth.sh
index b2f7d52a0e..a0904018d2 100755
--- a/.evergreen/run-tests-aws-auth.sh
+++ b/.evergreen/run-tests-aws-auth.sh
@@ -27,16 +27,16 @@ case "$AUTH" in
     export MONGO_RUBY_DRIVER_AWS_AUTH_SECRET_ACCESS_KEY="`get_var IAM_AUTH_ECS_SECRET_ACCESS_KEY`"
     export MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN="`get_var IAM_AUTH_ECS_ACCOUNT_ARN`"
     ;;
-    
+
   aws-assume-role)
     export MONGO_RUBY_DRIVER_AWS_AUTH_ACCESS_KEY_ID="`get_var IAM_AUTH_ASSUME_AWS_ACCOUNT`"
     export MONGO_RUBY_DRIVER_AWS_AUTH_SECRET_ACCESS_KEY="`get_var IAM_AUTH_ASSUME_AWS_SECRET_ACCESS_KEY`"
-    
+
     # This is the ARN provided in the AssumeRole request. It is different
     # from the ARN that the credentials returned by the AssumeRole request
     # resolve to.
     export MONGO_RUBY_DRIVER_AWS_AUTH_ASSUME_ROLE_ARN="`get_var IAM_AUTH_ASSUME_ROLE_NAME`"
-    
+
     # This is the ARN that the credentials obtained by the AssumeRole
     # request resolve to. It is hardcoded in
     # https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_aws/aws_e2e_assume_role.js
@@ -45,7 +45,7 @@ case "$AUTH" in
     # obtained from STS. See https://jira.mongodb.org/browse/RUBY-2425.
     export MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN="arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*"
     ;;
-    
+
   aws-ec2)
     export MONGO_RUBY_DRIVER_AWS_AUTH_ACCESS_KEY_ID="`get_var IAM_AUTH_EC2_INSTANCE_ACCOUNT`"
     export MONGO_RUBY_DRIVER_AWS_AUTH_SECRET_ACCESS_KEY="`get_var IAM_AUTH_EC2_INSTANCE_SECRET_ACCESS_KEY`"
@@ -53,7 +53,7 @@ case "$AUTH" in
     # Region is not specified in Evergreen but can be specified when
     # testing locally.
     export MONGO_RUBY_DRIVER_AWS_AUTH_REGION=${MONGO_RUBY_DRIVER_AWS_AUTH_REGION:=us-east-1}
-    
+
     if test -z "$MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN"; then
       # This is the ARN that the credentials obtained via EC2 instance metadata
       # resolve to. It is hardcoded in
@@ -64,10 +64,10 @@ case "$AUTH" in
       # variable manually.
       export MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN="arn:aws:sts::557821124784:assumed-role/authtest_instance_profile_role/*"
     fi
-    
+
     export TEST_CMD=${TEST_CMD:=rspec spec/integration/aws*spec.rb spec/integration/client_construction_aws*spec.rb}
     ;;
-    
+
   aws-ecs)
     export MONGO_RUBY_DRIVER_AWS_AUTH_ACCESS_KEY_ID="`get_var IAM_AUTH_ECS_ACCOUNT`"
     export MONGO_RUBY_DRIVER_AWS_AUTH_SECRET_ACCESS_KEY="`get_var IAM_AUTH_ECS_SECRET_ACCESS_KEY`"
@@ -78,7 +78,7 @@ case "$AUTH" in
     # Region is not specified in Evergreen but can be specified when
     # testing locally.
     export MONGO_RUBY_DRIVER_AWS_AUTH_REGION=${MONGO_RUBY_DRIVER_AWS_AUTH_REGION:=us-east-1}
-    
+
     if test -z "$MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN"; then
       # This is the ARN that the credentials obtained via ECS task metadata
       # resolve to. It is hardcoded in
@@ -89,11 +89,11 @@ case "$AUTH" in
       # variable manually.
       export MONGO_RUBY_DRIVER_AWS_AUTH_USER_ARN="arn:aws:sts::557821124784:assumed-role/ecsTaskExecutionRole/*"
     fi
-    
+
     export TEST_CMD=${TEST_CMD:=rspec spec/integration/aws*spec.rb spec/integration/client_construction_aws*spec.rb}
     exec `dirname $0`/run-tests-ecs.sh
     ;;
-    
+
   *)
     echo "Unknown AUTH value $AUTH" 1>&2
     exit 1
diff --git a/.evergreen/run-tests-azure.sh b/.evergreen/run-tests-azure.sh
new file mode 100755
index 0000000000..93d522f155
--- /dev/null
+++ b/.evergreen/run-tests-azure.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -ex
+
+. `dirname "$0"`/../spec/shared/shlib/distro.sh
+. `dirname "$0"`/../spec/shared/shlib/set_env.sh
+. `dirname "$0"`/functions.sh
+
+set_env_vars
+set_env_python
+set_env_ruby
+
+sudo apt-get -y install libyaml-dev cmake
+
+bundle_install
+
+echo "Running specs"
+export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH}
+bundle exec rake spec:prepare
+bundle exec rspec spec/integration/client_side_encryption/on_demand_azure_credentials_spec.rb
+
+exit ${test_status}
diff --git a/.evergreen/run-tests-docker.sh b/.evergreen/run-tests-docker.sh
index f3605ebb96..b04c48de5d 100755
--- a/.evergreen/run-tests-docker.sh
+++ b/.evergreen/run-tests-docker.sh
@@ -13,7 +13,8 @@ fi
 params=
 for var in MONGODB_VERSION TOPOLOGY RVM_RUBY \
   OCSP_ALGORITHM OCSP_STATUS OCSP_DELEGATE OCSP_MUST_STAPLE \
-  OCSP_CONNECTIVITY OCSP_VERIFIER FLE
+  OCSP_CONNECTIVITY OCSP_VERIFIER FLE \
+  AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION CRYPT_SHARED_VERSION MONGO_RUBY_DRIVER_AZURE_METADATA_HOST
 do
   value="${!var}"
   if test -n "$value"; then
diff --git a/.evergreen/run-tests-gcp.sh b/.evergreen/run-tests-gcp.sh
new file mode 100755
index 0000000000..baccf6404b
--- /dev/null
+++ b/.evergreen/run-tests-gcp.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -ex
+
+. `dirname "$0"`/../spec/shared/shlib/distro.sh
+. `dirname "$0"`/../spec/shared/shlib/set_env.sh
+. `dirname "$0"`/../spec/shared/shlib/server.sh
+. `dirname "$0"`/functions.sh
+
+set_env_vars
+set_env_python
+set_env_ruby
+
+sudo apt-get -y install libyaml-dev cmake
+
+bundle_install
+
+echo "Running specs"
+export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH}
+bundle exec rake spec:prepare
+bundle exec rspec spec/integration/client_side_encryption/on_demand_gcp_credentials_spec.rb
+
+exit ${test_status}
diff --git a/.evergreen/run-tests-kerberos-integration.sh b/.evergreen/run-tests-kerberos-integration.sh
index 3d9eb49e73..e7002d898e 100755
--- a/.evergreen/run-tests-kerberos-integration.sh
+++ b/.evergreen/run-tests-kerberos-integration.sh
@@ -61,6 +61,22 @@ configure_kerberos_ip_addr
 echo "Install dependencies"
 export BUNDLE_GEMFILE=gemfiles/mongo_kerberos.gemfile
 bundle_install
+
+# need to build the native extension, since it doesn't seem to build correctly
+# when installed via github.
+curdir=`pwd`
+cd `bundle info --path mongo_kerberos`
+
+# unset the BUNDLE_GEMFILE variable so the mongo_kerberos rakefile doesn't
+# get confused by it...
+saved_gemfile=$BUNDLE_GEMFILE
+unset BUNDLE_GEMFILE
+
+bundle install
+rake compile
+cd $curdir
+export BUNDLE_GEMFILE=$saved_gemfile
+
 bundle list
 
 export MONGO_RUBY_DRIVER_KERBEROS=1
diff --git a/.evergreen/run-tests-serverless.sh b/.evergreen/run-tests-serverless.sh
index 4f96a3ea33..2f97acaef5 100755
--- a/.evergreen/run-tests-serverless.sh
+++ b/.evergreen/run-tests-serverless.sh
@@ -16,16 +16,86 @@ export MONGODB_URI=`echo ${SERVERLESS_URI} | sed -r 's/mongodb\+srv:\/\//mongodb
 
 export TOPOLOGY="load-balanced"
 
+python3 -u .evergreen/mongodl.py --component crypt_shared -V ${SERVERLESS_MONGODB_VERSION} --out `pwd`/csfle_lib  --target `host_distro` || true
+if test -f `pwd`/csfle_lib/lib/mongo_crypt_v1.so
+then
+    echo Usinn crypt shared library version ${SERVERLESS_MONGODB_VERSION}
+    export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=`pwd`/csfle_lib/lib/mongo_crypt_v1.so
+else
+    echo Failed to download crypt shared library
+    exit -1
+fi
+
+if ! ( test -f /etc/os-release & grep -q ^ID.*rhel /etc/os-release & grep -q ^VERSION_ID.*8.0 /etc/os-release ); then
+    echo Serverless tests assume rhel80
+    echo If this has changed, update .evergreen/run-tests-serverless.sh as necessary
+    exit -1
+fi
+
+mkdir libmongocrypt
+cd libmongocrypt
+curl --retry 3 -fLo libmongocrypt-all.tar.gz "https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz"
+tar xf libmongocrypt-all.tar.gz
+# We assume that serverless tests always use rhel80
+export LIBMONGOCRYPT_PATH=`pwd`/rhel-80-64-bit/nocrypto/lib64/libmongocrypt.so
+cd -
+
+cd .evergreen/csfle
+. ./activate-kmstlsvenv.sh
+
+pip install boto3~=1.19 'cryptography<3.4' pykmip~=0.10.0 'sqlalchemy<2.0.0'
+
+python -u ./kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 7999 &
+python -u ./kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
+python -u ./kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
+python -u ./kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert &
+python -u ./kms_kmip_server.py &
+
+echo "Waiting for mock KMS servers to start..."
+wait_for_kms_server() {
+    for i in $(seq 60); do
+        if curl -s "localhost:$1"; test $? -ne 7; then
+        return 0
+        else
+        sleep 1
+        fi
+    done
+    echo "Could not detect mock KMS server on port $1"
+    return 1
+}
+wait_for_kms_server 8000
+wait_for_kms_server 8001
+wait_for_kms_server 8002
+wait_for_kms_server 5698
+echo "Waiting for mock KMS servers to start... done."
+
+# Obtain temporary AWS credentials
+. ./set-temp-creds.sh
+cd -
+
 echo "Running specs"
 
 bundle exec rspec \
     spec/spec_tests/crud_spec.rb \
-    spec/spec_tests/crud_unified_spec.rb \
     spec/spec_tests/retryable_reads_spec.rb \
     spec/spec_tests/retryable_writes_spec.rb \
     spec/spec_tests/transactions_spec.rb \
+    spec/spec_tests/change_streams_unified_spec.rb \
+    spec/spec_tests/client_side_encryption_unified_spec.rb \
+    spec/spec_tests/command_monitoring_unified_spec.rb \
+    spec/spec_tests/crud_unified_spec.rb \
+    spec/spec_tests/gridfs_unified_spec.rb \
+    spec/spec_tests/retryable_reads_unified_spec.rb \
+    spec/spec_tests/retryable_writes_unified_spec.rb \
+    spec/spec_tests/sessions_unified_spec.rb \
     spec/spec_tests/transactions_unified_spec.rb
 
+# https://jira.mongodb.org/browse/RUBY-3249
+# Add when fixed: spec/spec_tests/client_side_encryption_spec.rb \
+
 kill_jruby
+# Terminate all kmip servers... and whatever else happens to be running
+# that is a python script.
+pkill python
 
 exit ${test_status}
diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh
index 0e07e1f6fd..d5bd27df57 100755
--- a/.evergreen/run-tests.sh
+++ b/.evergreen/run-tests.sh
@@ -44,7 +44,13 @@ set_env_ruby
 prepare_server $arch
 
 if test "$DOCKER_PRELOAD" != 1; then
-  install_mlaunch_virtualenv
+  install_mlaunch_venv
+fi
+
+# Make sure cmake is installed (in case we need to install the libmongocrypt
+# helper)
+if [ "$FLE" = "helper" ]; then
+  install_cmake
 fi
 
 # Launching mongod under $MONGO_ORCHESTRATION_HOME
@@ -160,14 +166,24 @@ fi
 
 if test -n "$FLE"; then
   # Downloading crypt shared lib
-  crypt_shared_version=`${BINDIR}/mongod --version | grep -oP 'db version v\K.*'`
-  python3 -u .evergreen/mongodl.py --component crypt_shared -V ${crypt_shared_version} --out `pwd`/csfle_lib  --target `host_distro` || true
-  if test -f `pwd`/csfle_lib/lib/mongo_crypt_v1.so
-  then
-    export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=`pwd`/csfle_lib/lib/mongo_crypt_v1.so
+  if [ -z "$MONGO_CRYPT_SHARED_DOWNLOAD_URL" ]; then
+    crypt_shared_version=${CRYPT_SHARED_VERSION:-$("${BINDIR}"/mongod --version | grep -oP 'db version v\K.*')}
+    python3 -u .evergreen/mongodl.py --component crypt_shared -V ${crypt_shared_version} --out $(pwd)/csfle_lib  --target $(host_distro) || true
+    if test -f $(pwd)/csfle_lib/lib/mongo_crypt_v1.so
+    then
+      export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=$(pwd)/csfle_lib/lib/mongo_crypt_v1.so
+    else
+      echo 'Could not find crypt_shared library'
+    fi
+  else
+    echo "Downloading crypt_shared package from $MONGO_CRYPT_SHARED_DOWNLOAD_URL"
+    mkdir -p $(pwd)/csfle_lib
+    cd $(pwd)/csfle_lib
+    curl --retry 3 -fL $MONGO_CRYPT_SHARED_DOWNLOAD_URL | tar zxf -
+    export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=$(pwd)/lib/mongo_crypt_v1.so
+    cd -
   fi
 
-
   # Start the KMS servers first so that they are launching while we are
   # fetching libmongocrypt.
   if test "$DOCKER_PRELOAD" != 1; then
@@ -179,13 +195,17 @@ if test -n "$FLE"; then
     # cryptography 3.4 requires rust, see
     # https://github.com/pyca/cryptography/issues/5771.
     #pip install boto3~=1.19 cryptography~=3.4.8 pykmip~=0.10.0
-    pip3 install boto3~=1.19 'cryptography<3.4' pykmip~=0.10.0
+    pip3 install boto3~=1.19 'cryptography<3.4' pykmip~=0.10.0 'sqlalchemy<2.0.0'
   fi
   python3 -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 7999 &
   python3 -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/expired.pem --port 8000 &
   python3 -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/wrong-host.pem --port 8001 &
   python3 -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 8002 --require_client_cert &
   python3 -u .evergreen/csfle/kms_kmip_server.py &
+  python3 -u .evergreen/csfle/fake_azure.py &
+
+  # Obtain temporary AWS credentials
+  PYTHON=python3 . .evergreen/csfle/set-temp-creds.sh
 
   if test "$FLE" = helper; then
     echo "Using helper gem"
@@ -238,6 +258,7 @@ if test -n "$FLE"; then
    wait_for_kms_server 8001
    wait_for_kms_server 8002
    wait_for_kms_server 5698
+   wait_for_kms_server 8080
    echo "Waiting for mock KMS servers to start... done."
 fi
 
diff --git a/.evergreen/update-evergreen-configs b/.evergreen/update-evergreen-configs
index 8a070e8e5f..b1f4296649 100755
--- a/.evergreen/update-evergreen-configs
+++ b/.evergreen/update-evergreen-configs
@@ -8,9 +8,6 @@ autoload :YAML, 'yaml'
 class Runner
   def run
     transform('config.yml')
-
-    @atlas = true
-    transform('config-atlas.yml')
   end
 
   def transform(output_file_name)
@@ -27,13 +24,8 @@ EOT
     template_path = File.join(File.dirname(__FILE__), 'config/axes.yml.erb')
     contents << Tilt.new(template_path, engine_class: Erubi::CaptureEndEngine).render(self)
 
-    if @atlas
-      template_path = File.join(File.dirname(__FILE__), 'config/atlas.yml.erb')
-      contents << Tilt.new(template_path, engine_class: Erubi::CaptureEndEngine).render(self)
-    else
-      template_path = File.join(File.dirname(__FILE__), 'config/standard.yml.erb')
-      contents << Tilt.new(template_path, engine_class: Erubi::CaptureEndEngine).render(self)
-    end
+    template_path = File.join(File.dirname(__FILE__), 'config/standard.yml.erb')
+    contents << Tilt.new(template_path, engine_class: Erubi::CaptureEndEngine).render(self)
 
     output_path = File.join(File.dirname(__FILE__), output_file_name)
     File.open(output_path, 'w') do |f|
@@ -44,36 +36,6 @@ EOT
   def get_binding
     binding
   end
-
-  DEBIAN_FOR_RUBY = {
-    'ruby-2.3' => 'debian92',
-    'ruby-2.4' => 'debian92',
-    'ruby-2.5' => 'debian10',
-    'ruby-2.6' => 'debian10',
-    'ruby-2.7' => 'debian10',
-    'ruby-3.0' => 'debian10',
-  }
-
-  def standard_debian_rubies(rubies, key: nil, &block)
-    rubies.flatten!
-    text = block.call
-    contents = YAML.load(text)
-    out = rubies.map do |ruby|
-      contents.merge(
-        'matrix_name' => "#{contents['matrix_name']} - #{ruby}",
-        'matrix_spec' => contents['matrix_spec'].merge(
-          'ruby' => ruby,
-          key || 'os' => DEBIAN_FOR_RUBY.fetch(ruby),
-        ),
-      )
-    end.to_yaml
-    text =~ /\A\n?(\s+)/
-    unless text
-      raise "Couldn't figure out indentation level"
-    end
-    indent = ' ' * ($1.length - 2)
-    "\n" + out.sub(/\A---.*\n/, indent).gsub("\n", "\n#{indent}")
-  end
 end
 
 Runner.new.run
diff --git a/.evergreen/venv-utils.sh b/.evergreen/venv-utils.sh
new file mode 120000
index 0000000000..f544cf51bc
--- /dev/null
+++ b/.evergreen/venv-utils.sh
@@ -0,0 +1 @@
+../.mod/drivers-evergreen-tools/.evergreen/venv-utils.sh
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 28c7dd342f..44c7b6b2bb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,13 +10,13 @@ jobs:
     env:
       CI: true
       TESTOPTS: "-v"
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-20.04
     continue-on-error: true
     strategy:
       fail-fast: false
       matrix:
-        os: [ ubuntu-latest ]
-        ruby: ["2.5", "2.6", "2.7", "3.0", "3.1"]
+        os: [ ubuntu-20.04 ]
+        ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2"]
         mongodb: ["3.6", "4.4", "5.0"]
         topology: [replica_set, sharded_cluster]
         include:
@@ -41,11 +41,11 @@ jobs:
             mongodb: "5.0"
             topology: server
           - os: ubuntu-latest
-            ruby: "3.0"
+            ruby: "3.1"
             mongodb: "5.0"
             topology: server
           - os: ubuntu-latest
-            ruby: "3.1"
+            ruby: "3.2"
             mongodb: "5.0"
             topology: server
           - os: ubuntu-18.04
@@ -53,7 +53,7 @@ jobs:
             mongodb: "3.6"
             topology: replica_set
           - os: ubuntu-latest
-            ruby: "3.1"
+            ruby: "3.2"
             mongodb: "6.0"
             topology: replica_set
     steps:
diff --git a/.gitmodules b/.gitmodules
index 366d171c3a..6d428f359d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -3,4 +3,4 @@
 	url = https://github.com/mongodb-labs/drivers-evergreen-tools
 [submodule "spec/shared"]
 	path = spec/shared
-	url = https://github.com/mongodb-labs/mongo-ruby-spec-shared
+	url = git@github.com:mongodb-labs/mongo-ruby-spec-shared.git
diff --git a/.mod/drivers-evergreen-tools b/.mod/drivers-evergreen-tools
index 36de96a1f6..6b328a119f 160000
--- a/.mod/drivers-evergreen-tools
+++ b/.mod/drivers-evergreen-tools
@@ -1 +1 @@
-Subproject commit 36de96a1f65c6471d150d69aef7ea8b7f1e9ddc9
+Subproject commit 6b328a119fa0ffdcd13e62b2c1c2259873a9076d
diff --git a/README.md b/README.md
index dee1bf8bc9..4a3daa1dac 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ MongoDB Ruby Driver
 
 The officially supported Ruby driver for [MongoDB](https://www.mongodb.org/).
 
-The Ruby driver supports Ruby 2.5-3.0 and JRuby 9.2.
+The Ruby driver supports Ruby 2.5-3.2 and JRuby 9.2-9.3.
 
 ## Documentation
 
diff --git a/docs/contribute.txt b/docs/contribute.txt
index d25dda4e8d..a541b15fad 100644
--- a/docs/contribute.txt
+++ b/docs/contribute.txt
@@ -58,7 +58,7 @@ Contribute Code
 The MongoDB Ruby driver source is located
 `at GitHub <https://github.com/mongodb/mongo-ruby-driver>`_.
 
-The list of known issues in the driver is available 
+The list of known issues in the driver is available
 `in JIRA <https://jira.mongodb.org/browse/RUBY>`_.
 
 We recommend creating a JIRA ticket before starting work on a bug fix or
@@ -73,7 +73,7 @@ the changes to the stable branches, if needed.
 A MongoDB deployment is required to run the tests. Setup procedures and
 recommendations for various deployments, as well as how to configure the
 driver's test suite for the deployments, are covered in the `spec
-readme <https://github.com/mongodb/mongo-ruby-driver/blob/master/spec/README.md>`.
+readme <https://github.com/mongodb/mongo-ruby-driver/blob/master/spec/README.md>`__.
 
 The driver is tested on `Evergreen <https://github.com/evergreen-ci/evergreen>`_,
 MongoDB's in-house continuous integration platform. After a pull request
diff --git a/docs/index.txt b/docs/index.txt
index e126a4d50a..44e4e33fc8 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -25,7 +25,7 @@ BSON
 The Ruby BSON implementation is packaged in a separate gem with C and
 Java extensions for speed depending on the runtime enviroment.
 
-For reference on the Ruby BSON gem, see :doc:`/bson-tutorials`.
+For reference on the Ruby BSON gem, see the :doc:`/tutorials/bson-v4`.
 
 Object Mappers
 ==============
@@ -61,8 +61,7 @@ For tutorials on Mongoid, see the `Mongoid Manual <https://mongodb.com/docs/mong
     reference/connection-and-configuration
     reference/working-with-data
     reference/schema-operations
-    bson-tutorials
-    API <https://mongodb.com/docs/ruby-driver/master/api/>
+    API <https://mongodb.com/docs/ruby-driver/current/api/>
     release-notes
     reference/additional-resources
     contribute
diff --git a/docs/reference/client-side-encryption.txt b/docs/reference/client-side-encryption.txt
index 8191c226fb..bfe37d236e 100644
--- a/docs/reference/client-side-encryption.txt
+++ b/docs/reference/client-side-encryption.txt
@@ -128,12 +128,12 @@ enterprise-only feature. If you only intend to use explicit encryption, you may
 skip this step.
 
 Mongocryptd comes pre-packaged with enterprise builds of the MongoDB server
-(versions 4.2 and newer). For installation instructions, see
-`the MongoDB manual <https://mongodb.com/docs/manual/reference/security-client-side-encryption-appendix/#installation>`_.
+(versions 4.2 and newer). For installation instructions, see the
+`MongoDB manual <https://mongodb.com/docs/manual/reference/security-client-side-encryption-appendix/#installation>`_.
 
 In order to configure mongocryptd (for example, which port it listens on or the
 path used to spawn the daemon), it is necessary to pass different options to the
-``Mongo::Client`` performing automatic encryption. See the `:extra_options`_
+``Mongo::Client`` performing automatic encryption. See the :ref:`:extra_options <cse-extra-options>`
 section of this tutorial for more information.
 
 Automatic Encryption
@@ -274,8 +274,8 @@ The example above demonstrates using automatic encryption with a local master ke
 For more information about using other key management services to create a
 master key and create data keys, see the following sections of this tutorial:
 
-- `Creating A Master Key`_
-- `Creating A Data Key`_
+- :ref:`Creating A Master Key <creating-a-master-key>`
+- :ref:`Creating A Data Key <creating-a-data-key>`
 
 Explicit Encryption
 ===================
@@ -372,8 +372,8 @@ The example above demonstrates using explicit encryption with a local master key
 For more information about using other key management services to create a
 master key and create data keys, see the following sections of this tutorial:
 
-- `Creating A Master Key`_,
-- `Creating A Data Key`_,
+- :ref:`Creating A Master Key <creating-a-master-key>`
+- :ref:`Creating A Data Key <creating-a-data-key>`
 
 Queryable Encryption
 ====================
@@ -485,8 +485,8 @@ The example above demonstrates using automatic encryption with a local master ke
 For more information about using other key management services to create a
 master key and create data keys, see the following sections of this tutorial:
 
-- `Creating A Master Key`_
-- `Creating A Data Key`_
+- :ref:`Creating A Master Key <creating-a-master-key>`
+- :ref:`Creating A Data Key <creating-a-data-key>`
 
 Below is an example of explicit queryable encryption.
 
@@ -598,6 +598,7 @@ Below is an example of explicit queryable encryption.
   find_result = client['encryption_coll'].find(encrypted_field: find_payload).first['encrypted_field']
   # => 'sensitive data'
 
+.. _creating-a-master-key:
 
 Creating a Master Key
 =====================
@@ -608,6 +609,8 @@ local key, or by creating a key in a key management service. Currently
 Ruby driver supports AWS Key Management Service (KMS), Azure Key Vault, and
 Google Cloud Key Management (GCP KMS).
 
+.. _local-master-key:
+
 Local Master Key
 ~~~~~~~~~~~~~~~~
 
@@ -626,17 +629,21 @@ Run the following code to generate a local master key using Ruby:
   local_master_key = SecureRandom.random_bytes(96)
   # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." (a binary blob)
 
+.. _remote-master-key:
+
 Remote Master Key
 ~~~~~~~~~~~~~~~~~
 It is recommended that you use a remote Key Management Service to create and
 store your master key. To do so, follow steps of the
-:drivers:`"Set up a Remote Master Key" section</security/client-side-field-level-encryption-local-key-to-kms/#set-up-a-remote-master-key>`
+`"Set up a Remote Master Key" <https://www.mongodb.com/docs/manual/core/csfle/tutorials/#set-up-a-remote-master-key>`_
 in the MongoDB Client-Side Encryption documentation.
 
 For more information about creating a master key, see the
-:drivers:`Create a Master Key </security/client-side-field-level-encryption-guide/#a.-create-a-master-key>`
+`Create a Master Key <https://www.mongodb.com/docs/manual/core/csfle/#a.-create-a-master-key>`_
 section of the MongoDB manual.
 
+.. _creating-a-data-key:
+
 Creating a Data Key
 ===================
 
@@ -679,8 +686,8 @@ key with the following code snippet:
   data_key_id = client_encryption.create_data_key('local')
   # => <BSON::Binary... type=ciphertext...>
 
-See the `Local Master Key`_ section for more information about generating a new
-local master key.
+See the :ref:`Local Master Key <local-master-key>` section for more information
+about generating a new local master key.
 
 Create a Data Key Using a Remote Master Key
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -788,12 +795,12 @@ certificate and private key to authenticate to KMIP server.
   )
   # => <BSON::Binary... type=ciphertext...>
 
-See the `Remote Master Key`_ section of this tutorial  for more information about
-generating a new remote master key and finding the information you need to
-create data keys.
+See the :ref:`Remote Master Key <remote-master-key>` section of this tutorial
+for more information about generating a new remote master key and finding the
+information you need to create data keys.
 
 For more information about creating a data key, see the
-:drivers:`Create a Data Encryption Key </security/client-side-field-level-encryption-guide/#b.-create-a-data-encryption-key>`
+`Create a Data Encryption Key <https://www.mongodb.com/docs/manual/core/csfle/#b.-create-a-data-encryption-key>`_
 section of the MongoDB manual.
 
 For a list of possible KMS TLS options
@@ -974,7 +981,7 @@ When you intend to use your schema map, convert it to a Ruby ``Hash`` using the
 
 .. seealso::
 
-  :drivers:`Specify Encrypted Fields Using JSON Schema</security/client-side-field-level-encryption-guide/#c-specify-encrypted-fields-using-json-schema>`,
+  `Specify Encrypted Fields Using JSON Schema <https://www.mongodb.com/docs/manual/core/csfle/#c-specify-encrypted-fields-using-json-schema>`_,
   :manual:`Automatic Encryption Rules</reference/security-client-side-automatic-json-schema/>`
 
 .. _schema-map-path:
@@ -1012,6 +1019,8 @@ decryption of any previously-encrypted data.
     }
   )
 
+.. _cse-extra-options:
+
 ``:extra_options``
 ~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/reference/connection-and-configuration.txt b/docs/reference/connection-and-configuration.txt
index acc28b5250..7532e73f98 100644
--- a/docs/reference/connection-and-configuration.txt
+++ b/docs/reference/connection-and-configuration.txt
@@ -12,7 +12,7 @@ options the driver provides, including authentication.
 .. toctree::
   :titlesonly:
 
-  create-client
-  authentication
-  monitoring
-  user-management
+  /reference/create-client
+  /reference/authentication
+  /reference/monitoring
+  /reference/user-management
diff --git a/docs/reference/create-client.txt b/docs/reference/create-client.txt
index b410430648..5aeb87727c 100644
--- a/docs/reference/create-client.txt
+++ b/docs/reference/create-client.txt
@@ -329,6 +329,13 @@ URI option:
 
 
 
+Connect to MongoDB Atlas from AWS Lambda
+----------------------------------------
+
+To learn how to connect to Atlas from AWS Lambda, see the
+`Manage Connections with AWS Lambda <https://www.mongodb.com/docs/atlas/manage-connections-aws-lambda/>`__
+documentation.
+
 .. _srv-uri-notes:
 
 SRV URI Notes
@@ -896,6 +903,7 @@ URI options are explained in detail in the :manual:`Connection URI reference
 </reference/connection-string/>`.
 
 .. note::
+
   Options that are set in **milliseconds** in the URI are
   represented as a ``float`` in Ruby and the units are **seconds**.
 
@@ -1026,11 +1034,11 @@ URI options are explained in detail in the :manual:`Connection URI reference
        invalid values (e.g. negative and non-numeric values), invalid values
        provided via this URI option are ignored with a warning.
 
-  * - srvMaxHosts=Integer
-    - ``:srv_max_hosts => Integer``
+   * - srvMaxHosts=Integer
+     - ``:srv_max_hosts => Integer``
 
-  * - srvServiceName=String
-    - ``:srv_service_name => String``
+   * - srvServiceName=String
+     - ``:srv_service_name => String``
 
    * - ssl=Boolean
      - ``:ssl => true|false``
@@ -1341,7 +1349,7 @@ It is also possible to remove hooks from ``Mongo.tls_context_hooks`` by storing
 a reference to the Procs somewhere else in the application, and then using
 ``Array#delete_if`` to remove the desired hooks.
 
-..warning ::
+.. warning::
 
   TLS context hooks are global and will affect every instance of ``Mongo::Client``.
   Any library that allows applications to enable these hooks should expose methods to
diff --git a/docs/reference/crud-operations.txt b/docs/reference/crud-operations.txt
index 73262aa70b..13a8f0d8db 100644
--- a/docs/reference/crud-operations.txt
+++ b/docs/reference/crud-operations.txt
@@ -93,7 +93,7 @@ Specify a ``Decimal128`` number
 .. versionadded:: 3.4
 
 :manual:`Decimal128</tutorial/model-monetary-data>` is a
-:doc:`BSON datatype </bson-tutorials>`
+:doc:`BSON datatype </tutorials/bson-v4>`
 that employs 128-bit decimal-based floating-point values capable
 of emulating decimal rounding with exact precision. This
 functionality is intended for applications that handle
@@ -988,7 +988,7 @@ for more information on working with these types of fields.
 A Note about the BSON Symbol type
 =================================
 
-Because the BSON specification deprecated the BSON symbol type, the `bson` gem
+Because the BSON specification deprecated the BSON symbol type, the ``bson`` gem
 will serialize Ruby symbols into BSON strings when used on its own. However, in
 order to maintain backwards compatibility with older datasets, the Ruby driver
 overrides this behavior to serialize Ruby symbols as BSON symbols. This is
diff --git a/docs/reference/driver-compatibility.txt b/docs/reference/driver-compatibility.txt
index ce0af48381..179d21181a 100644
--- a/docs/reference/driver-compatibility.txt
+++ b/docs/reference/driver-compatibility.txt
@@ -39,8 +39,6 @@ version.
 
    * - Ruby Driver
      - MongoDB 6.0
-     - MongoDB 5.2
-     - MongoDB 5.1
      - MongoDB 5.0
      - MongoDB 4.4
      - MongoDB 4.2
@@ -58,30 +56,24 @@ version.
      - |checkmark|
      - |checkmark|
      - |checkmark|
-     - |checkmark|
-     - |checkmark|
      -
      -
      -
      -
 
    * - 2.17
-     -
      -
      - |checkmark|
      - |checkmark|
      - |checkmark|
      - |checkmark|
      - |checkmark|
-     - |checkmark|
      -
      -
      -
      -
 
    * - 2.16
-     -
-     -
      -
      - |checkmark|
      - |checkmark|
@@ -94,8 +86,6 @@ version.
      - D
 
    * - 2.15
-     -
-     -
      -
      -
      - |checkmark|
@@ -108,8 +98,6 @@ version.
      - |checkmark|
 
    * - 2.14
-     -
-     -
      -
      -
      - |checkmark|
@@ -122,8 +110,6 @@ version.
      - |checkmark|
 
    * - 2.13
-     -
-     -
      -
      -
      - |checkmark| [#ocsp]_
@@ -136,8 +122,6 @@ version.
      - |checkmark|
 
    * - 2.12
-     -
-     -
      -
      -
      -
@@ -150,8 +134,6 @@ version.
      - |checkmark|
 
    * - 2.11
-     -
-     -
      -
      -
      -
@@ -164,8 +146,6 @@ version.
      - |checkmark|
 
    * - 2.10
-     -
-     -
      -
      -
      -
@@ -182,8 +162,6 @@ version.
      -
      -
      -
-     -
-     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -196,8 +174,6 @@ version.
      -
      -
      -
-     -
-     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -210,8 +186,6 @@ version.
      -
      -
      -
-     -
-     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -224,8 +198,6 @@ version.
      -
      -
      -
-     -
-     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -239,70 +211,12 @@ version.
      -
      -
      -
-     -
-     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
      - |checkmark|
      - |checkmark|
 
-   * - 2.4
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     - |checkmark|
-     - |checkmark|
-     - |checkmark|
-     - |checkmark|
-
-   * - 2.3
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     - |checkmark|
-     - |checkmark|
-     - |checkmark|
-
-   * - 2.2
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     - |checkmark|
-     - |checkmark|
-     - |checkmark|
-
-   * - 2.0
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     -
-     - |checkmark|
-     - |checkmark|
-
 .. [#ocsp] OCSP verification is implemented as of driver version 2.14.
 
 .. [#srv-polling] Polling of SRV records in sharded topologies is
@@ -331,6 +245,7 @@ for that Ruby version is deprecated.
    :class: compatibility-large no-padding
 
    * - Ruby Driver
+     - Ruby 3.1
      - Ruby 3.0
      - Ruby 2.7
      - Ruby 2.6
@@ -350,6 +265,7 @@ for that Ruby version is deprecated.
      - |checkmark|
      - |checkmark|
      - |checkmark|
+     - |checkmark|
      -
      -
      -
@@ -365,6 +281,7 @@ for that Ruby version is deprecated.
      - |checkmark|
      - |checkmark|
      - |checkmark|
+     - |checkmark|
      -
      -
      -
@@ -376,6 +293,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.16
+     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -391,6 +309,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.15
+     -
      - |checkmark|
      - |checkmark|
      - |checkmark|
@@ -406,6 +325,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.14
+     -
      -
      - |checkmark|
      - |checkmark|
@@ -421,6 +341,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.13
+     -
      -
      - |checkmark|
      - |checkmark|
@@ -436,6 +357,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.12
+     -
      -
      - |checkmark|
      - |checkmark|
@@ -451,6 +373,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.11
+     -
      -
      - |checkmark|
      - |checkmark|
@@ -466,6 +389,7 @@ for that Ruby version is deprecated.
      -
 
    * - 2.10
+     -
      -
      - |checkmark|
      - |checkmark|
@@ -481,6 +405,7 @@ for that Ruby version is deprecated.
      - |checkmark|
 
    * - 2.9
+     -
      -
      -
      - |checkmark|
@@ -496,6 +421,7 @@ for that Ruby version is deprecated.
      - |checkmark|
 
    * - 2.8
+     -
      -
      -
      - |checkmark|
@@ -511,6 +437,7 @@ for that Ruby version is deprecated.
      - |checkmark|
 
    * - 2.7
+     -
      -
      -
      - |checkmark|
@@ -526,6 +453,7 @@ for that Ruby version is deprecated.
      - |checkmark|
 
    * - 2.6
+     -
      -
      -
      - |checkmark|
@@ -633,7 +561,7 @@ the driver.
    * - Ruby Driver
      - mongo_kerberos |nbsp| 2.1
 
-   * - 2.7 - 2.16
+   * - 2.7 - 2.18
      - |checkmark|
 
 
diff --git a/docs/reference/gridfs.txt b/docs/reference/gridfs.txt
index 267c4b1552..756750b18c 100644
--- a/docs/reference/gridfs.txt
+++ b/docs/reference/gridfs.txt
@@ -252,7 +252,7 @@ by default into the ``fs.chunks`` collection and file metadata is inserted into
 
   client.database.fs.insert_one(file)
 
-To insert into collections with a name prefix other than `fs`, access the
+To insert into collections with a name prefix other than ``fs``, access the
 filesystem with a ``:fs_name`` option.
 
 .. code-block:: ruby
diff --git a/docs/reference/monitoring.txt b/docs/reference/monitoring.txt
index 1721feb6a6..b4e1d6c7b1 100644
--- a/docs/reference/monitoring.txt
+++ b/docs/reference/monitoring.txt
@@ -287,9 +287,9 @@ Server Heartbeats
 
 The application can be notified of each server heartbeat by subscribing
 to SERVER_HEARTBEAT topic. A server heartbeat listener must implement
-three methods: `started`, `succeeded` and `failed`. Each heartbeat invokes
-the `started` method on the listener, and then either `succeeded` or `failed`
-method depending on the outcome of the heartbeat.
+three methods: ``started``, ``succeeded`` and ``failed``. Each heartbeat
+invokes the ``started`` method on the listener, and then either ``succeeded``
+or ``failed`` method depending on the outcome of the heartbeat.
 
 All heartbeat events contain the address of the server that the heartbeat
 was sent to. Succeeded and failed events contain the round trip time for
diff --git a/docs/reference/schema-operations.txt b/docs/reference/schema-operations.txt
index 713db0afc4..6d3617302a 100644
--- a/docs/reference/schema-operations.txt
+++ b/docs/reference/schema-operations.txt
@@ -12,7 +12,7 @@ including managing databases, collections, indexes and users.
 .. toctree::
   :titlesonly:
 
-  database-tasks
-  collection-tasks
-  indexing
-  collations
+  /reference/database-tasks
+  /reference/collection-tasks
+  /reference/indexing
+  /reference/collations
diff --git a/docs/reference/working-with-data.txt b/docs/reference/working-with-data.txt
index d6d76f82ed..3382428f8e 100644
--- a/docs/reference/working-with-data.txt
+++ b/docs/reference/working-with-data.txt
@@ -12,16 +12,16 @@ implements for inserting, updating and retrieving data from MongoDB.
 .. toctree::
   :titlesonly:
 
-  crud-operations
-  bulk-operations
-  projection
-  aggregation
-  map-reduce
-  text-search
-  geospatial-search
-  query-cache
-  gridfs
-  change-streams
-  sessions
-  transactions
-  client-side-encryption
+  /reference/crud-operations
+  /reference/bulk-operations
+  /reference/projection
+  /reference/aggregation
+  /reference/map-reduce
+  /reference/text-search
+  /reference/geospatial-search
+  /reference/query-cache
+  /reference/gridfs
+  /reference/change-streams
+  /reference/sessions
+  /reference/transactions
+  /reference/client-side-encryption
diff --git a/docs/release-notes.txt b/docs/release-notes.txt
index a4fa945f7b..ece1afdc39 100644
--- a/docs/release-notes.txt
+++ b/docs/release-notes.txt
@@ -51,7 +51,7 @@ This release includes the following new features:
   specify an arbitrary comment to help trace the operation through the
   database profiler, currentOp and logs.
 - The ``estimated_document_count`` method is now using the ``count`` server
-  command instead of `$collStats`` aggregation pipeline stage, to support
+  command instead of ``$collStats`` aggregation pipeline stage, to support
   operation on views. Applications using the Stable API should upgrade to
   server versions 5.0.8 (if using MongoDB 5.0) or 5.3.2 (if using MongoDB
   5.1/5.2/5.3) or newer to use the ``count`` command when API strict is enabled,
diff --git a/docs/tutorials.txt b/docs/tutorials.txt
index 00d8ce7b1e..1272795ed6 100644
--- a/docs/tutorials.txt
+++ b/docs/tutorials.txt
@@ -15,3 +15,5 @@ operations available in the Ruby driver.
 
   tutorials/quick-start
   tutorials/common-errors
+  tutorials/bson-v4
+
diff --git a/docs/tutorials/common-errors.txt b/docs/tutorials/common-errors.txt
index 4cb82b9fb6..4151e05b1c 100644
--- a/docs/tutorials/common-errors.txt
+++ b/docs/tutorials/common-errors.txt
@@ -91,8 +91,10 @@ This error was last reported on Ruby Driver version 2.5.1, so updating the drive
 can potentially solve this issue. A user has reported that they solved this issue
 as follows:
 
+.. blockquote::
+
   DNS servers on EC2 are generated in /etc/resolv.conf by default. Following
-  the answer `here <https://askubuntu.com/questions/157154/how-do-i-include-lines-in-resolv-conf-that-wont-get-lost-on-reboot?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa>`_
+  the `answer <https://askubuntu.com/questions/157154/how-do-i-include-lines-in-resolv-conf-that-wont-get-lost-on-reboot?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa>`_
   and setting the nameservers to Google NS I was able to fix this issue.
 
 You can find more information about this issue here:
@@ -116,5 +118,5 @@ authorization mechanism using the ``auth_mech`` option in your ``mongoid.yml``
 file.
 
 See more on this issue here:
-`RUBY-1281 <https://jira.mongodb.org/browse/RUBY-1684>`_.
+`RUBY-1684 <https://jira.mongodb.org/browse/RUBY-1684>`_.
 
diff --git a/gem-public_cert.pem b/gem-public_cert.pem
index 40d6d3807b..ab7c15c7ce 100644
--- a/gem-public_cert.pem
+++ b/gem-public_cert.pem
@@ -1,21 +1,26 @@
 -----BEGIN CERTIFICATE-----
-MIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMRQwEgYDVQQDDAtkcml2
-ZXItcnVieTEVMBMGCgmSJomT8ixkARkWBTEwZ2VuMRMwEQYKCZImiZPyLGQBGRYD
-Y29tMB4XDTIyMDMzMDE5MTcwMloXDTIzMDMzMDE5MTcwMlowQjEUMBIGA1UEAwwL
-ZHJpdmVyLXJ1YnkxFTATBgoJkiaJk/IsZAEZFgUxMGdlbjETMBEGCgmSJomT8ixk
-ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFdSAa8fRm1
-bAM9za6Z0fAH4g02bqM1NGnw8zJQrE/PFrFfY6IFCT2AsLfOwr1maVm7iU1+kdVI
-IQ+iI/9+E+ArJ+rbGV3dDPQ+SLl3mLT+vXjfjcxMqI2IW6UuVtt2U3Rxd4QU0kdT
-JxmcPYs5fDN6BgYc6XXgUjy3m+Kwha2pGctdciUOwEfOZ4RmNRlEZKCMLRHdFP8j
-4WTnJSGfXDiuoXICJb5yOPOZPuaapPSNXp93QkUdsqdKC32I+KMpKKYGBQ6yisfA
-5MyVPPCzLR1lP5qXVGJPnOqUAkvEUfCahg7EP9tI20qxiXrR6TSEraYhIFXL0EGY
-u8KAcPHm5KkCAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
-BBYEFFt3WbF+9JpUjAoj62cQBgNb8HzXMCAGA1UdEQQZMBeBFWRyaXZlci1ydWJ5
-QDEwZ2VuLmNvbTAgBgNVHRIEGTAXgRVkcml2ZXItcnVieUAxMGdlbi5jb20wDQYJ
-KoZIhvcNAQEFBQADggEBAM0s7jz2IGD8Ms035b1tMnbNP2CBPq3pen3KQj7IkGF7
-x8LPDdOqUj4pUMLeefntX/PkSvwROo677TnWK6+GLayGm5xLHrZH3svybC6QtqTR
-mVOLUoZ4TgUmtnMUa/ZvgrIsOeiCysjSf4WECuw7g+LE6jcpLepgYTLk2u1+5SgH
-JVENj0BMkdeZIKkc2G97DSx3Zrmz7QWAaH+99XlajJbfcgvhDso+ffQkTBlOgLBg
-+WyKQ+QTIdtDiyf2LQmxWnxt/W1CmScjdLS7/yXGkkB/D9Uy+sJD747O/B9P238Q
-XnerrtyOu04RsWDvaZkCwSDVzoqfICh4CP1mlde73Ts=
+MIIEeDCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBBMREwDwYDVQQDDAhkYngt
+cnVieTEXMBUGCgmSJomT8ixkARkWB21vbmdvZGIxEzARBgoJkiaJk/IsZAEZFgNj
+b20wHhcNMjMwMTMxMTE1NjM1WhcNMjQwMTMxMTE1NjM1WjBBMREwDwYDVQQDDAhk
+YngtcnVieTEXMBUGCgmSJomT8ixkARkWB21vbmdvZGIxEzARBgoJkiaJk/IsZAEZ
+FgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC0/Veq9l47cTfX
+tQ+kHq2NOCwJuJGt1iXWQ/vH/yp7pZ/bLej7gPDl2CfIngAXRjM7r1FkR9ya7VAm
+IneBFcVU3HhpIXWi4ByXGjBOXFD1Dfbz4C4zedIWRk/hNzXa+rQY4KPwpOwG/hZg
+id+rSXWSbNlkyN97XfonweVh7JsIa9X/2JY9ADYjhCfEZF+b0+Wl7+jgwzLWb46I
+0WH0bZBIZ0BbKAwUXIgvq5mQf9PzukmMVYCwnkJ/P4wrHO22HuwnbMyvJuGjVwqi
+j1NRp/2vjmKBFWxIfhlSXEIiqAmeEVNXzhPvTVeyo+rma+7R3Bo+4WHkcnPpXJJZ
+Jd63qXMvTB0GplEcMJPztWhrJOmcxIOVoQyigEPSQT8JpzFVXby4SGioizv2eT7l
+VYSiCHuc3yEDyq5M+98WGX2etbj6esYtzI3rDevpIAHPB6HQmtoJIA4dSl3gjFb+
+D+YQSuB2qYu021FI9zeY9sbZyWysEXBxhwrmTk+XUV0qz+OQZkMCAwEAAaN7MHkw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFH4nnr4tYlatU57RbExW
+jG86YM5nMB8GA1UdEQQYMBaBFGRieC1ydWJ5QG1vbmdvZGIuY29tMB8GA1UdEgQY
+MBaBFGRieC1ydWJ5QG1vbmdvZGIuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAVSlgM
+nFDWCCNLOCqG5/Lj4U62XoALkdCI+OZ30+WrA8qiRLSL9ZEziVK9AV7ylez+sriQ
+m8XKZKsCN5ON4+zXw1S+6Ftz/R4zDg7nTb9Wgw8ibzsoiP6e4pRW3Fls3ZdaG4pW
++qMTbae9OiSrgI2bxNTII+v+1FcbQjOlMu8HPZ3ZfXnurXPgN5GxSyyclZI1QONO
+HbUoKHRirZu0F7JCvQQq4EkSuLWPplRJfYEeJIYm05zhhFeEyqea2B/TTlCtXa42
+84vxXsxGzumuO8F2Q9m6/p95sNhqCp0B/SkKXIrRGJ7FBzupoORNRXHviS2OC3ty
+4lwUzOlLTF/yO0wwYYfmtQOALQwKnW838vbYthMXvTjxB0EgVZ5PKto99WbjsXzy
+wkeAWhd5b+5JS0zgDL4SvGB8/W2IY+y0zELkojBMgJPyrpAWHL/WSsSBMuhyI2Pv
+xxaBVLklnJJ/qCCOZ3lG2MyVc/Nb0Mmq8ygWNsfwHmKKYuuWcviit0D0Tek=
 -----END CERTIFICATE-----
diff --git a/gemfiles/standard.rb b/gemfiles/standard.rb
index 117366c1b3..b9e0fe1e5f 100644
--- a/gemfiles/standard.rb
+++ b/gemfiles/standard.rb
@@ -8,9 +8,7 @@ def standard_dependencies
   group :development, :testing do
     gem 'jruby-openssl', platforms: :jruby
     gem 'json', platforms: :jruby
-    # Explicitly specify each rspec dependency so that we can use
-    # rspec-mocks-diag instead of rspec-mocks
-    gem 'rspec-core', '~> 3.9'
+    gem 'rspec', '~> 3.12'
     gem 'activesupport', '<7.1'
     gem 'rake'
     gem 'webrick'
@@ -23,6 +21,7 @@ def standard_dependencies
     gem 'aws-sdk-ec2'
     gem 'aws-sdk-ecs'
     gem 'aws-sdk-iam'
+    gem 'aws-sdk-sts'
     gem 'paint'
 
     # for benchmark tests
@@ -43,14 +42,13 @@ def standard_dependencies
     gem 'ice_nine'
     gem 'rubydns', platforms: :mri
     gem 'rspec-retry'
-    gem 'rspec-expectations', '~> 3.9'
-    gem 'rspec-mocks-diag', '~> 3.9'
     gem 'rfc', '~> 0.2.0'
     gem 'fuubar'
     gem 'timeout-interrupt', platforms: :mri
     gem 'concurrent-ruby', platforms: :jruby
     gem 'dotenv'
     gem 'childprocess'
+    gem 'nokogiri'
   end
 
   group :development do
diff --git a/lib/mongo/bulk_write.rb b/lib/mongo/bulk_write.rb
index 6aae3aa520..ad05ef8f5f 100644
--- a/lib/mongo/bulk_write.rb
+++ b/lib/mongo/bulk_write.rb
@@ -115,7 +115,8 @@ def execute
     #   )
     #
     # @param [ Mongo::Collection ] collection The collection.
-    # @param [ Array<Hash, BSON::Document> ] requests The requests, cannot be empty.
+    # @param [ Enumerable<Hash, BSON::Document> ] requests The requests,
+    #   cannot be empty.
     # @param [ Hash, BSON::Document ] options The options.
     #
     # @since 2.1.0
@@ -330,10 +331,9 @@ def can_hint?(connection)
     #   ArgumentError ]
     #   if the document is invalid.
     def validate_requests!
-      if @requests.empty?
-        raise ArgumentError, "Bulk write requests cannot be empty"
-      end
+      requests_empty = true
       @requests.each do |req|
+        requests_empty = false
         if op = req.keys.first
           if [:update_one, :update_many].include?(op)
             if doc = maybe_first(req.dig(op, :update))
@@ -359,6 +359,8 @@ def validate_requests!
             end
           end
         end
+      end.tap do
+        raise ArgumentError, "Bulk write requests cannot be empty" if requests_empty
       end
     end
 
diff --git a/lib/mongo/client.rb b/lib/mongo/client.rb
index 6953fe9588..3216d001ce 100644
--- a/lib/mongo/client.rb
+++ b/lib/mongo/client.rb
@@ -1570,7 +1570,7 @@ def validate_read!(option, opts)
         # for custom classes implementing key access ([]).
         # Instead reject common cases of strings and symbols.
         if read.is_a?(String) || read.is_a?(Symbol)
-          raise Error::InvalidReadOption.new(read, 'must be a hash')
+          raise Error::InvalidReadOption.new(read, "the read preference must be specified as a hash: { mode: #{read.inspect} }")
         end
 
         if mode = read[:mode]
diff --git a/lib/mongo/collection.rb b/lib/mongo/collection.rb
index 8fe69fbbc0..7c5c67057f 100644
--- a/lib/mongo/collection.rb
+++ b/lib/mongo/collection.rb
@@ -83,7 +83,7 @@ class Collection
     #
     # @param [ Object ] other The object to check.
     #
-    # @return [ true, false ] If the objects are equal.
+    # @return [ true | false ] If the objects are equal.
     #
     # @since 2.0.0
     def ==(other)
@@ -100,19 +100,59 @@ def ==(other)
     # @param [ String, Symbol ] name The collection name.
     # @param [ Hash ] options The collection options.
     #
-    # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
+    # @option opts [ true | false ] :capped Create a fixed-sized collection.
+    # @option opts [ Hash ] :change_stream_pre_and_post_images Used to enable
+    #   pre- and post-images on the created collection.
+    #   The hash may have the following items:
+    #   - *:enabled* -- true or false.
+    # @option opts [ Hash ] :clustered_index Create a clustered index.
+    #   This option specifies how this collection should be clustered on _id.
+    #   The hash may have the following items:
+    #   - *:key* -- The clustered index key field. Must be set to { _id: 1 }.
+    #   - *:unique* -- Must be set to true. The collection will not accept
+    #     inserted or updated documents where the clustered index key value
+    #     matches an existing value in the index.
+    #   - *:name* -- Optional. A name that uniquely identifies the clustered index.
+    # @option opts [ Hash ] :collation The collation to use.
+    # @option opts [ Hash ] :encrypted_fields Hash describing encrypted fields
+    #   for queryable encryption.
+    # @option opts [ Integer ] :expire_after Number indicating
+    #   after how many seconds old time-series data should be deleted.
+    # @option opts [ Integer ] :max The maximum number of documents in a
+    #   capped collection. The size limit takes precedents over max.
+    # @option opts [ Array<Hash> ] :pipeline An array of pipeline stages.
+    #   A view will be created by applying this pipeline to the view_on
+    #   collection or view.
+    # @option options [ Hash ] :read_concern The read concern options hash,
+    #   with the following optional keys:
+    #   - *:level* -- the read preference level as a symbol; valid values
+    #      are *:local*, *:majority*, and *:snapshot*
+    # @option options [ Hash ] :read The read preference options.
+    #   The hash may have the following items:
+    #   - *:mode* -- read preference specified as a symbol; valid values are
+    #     *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
+    #     and *:nearest*.
+    #   - *:tag_sets* -- an array of hashes.
+    #   - *:local_threshold*.
+    # @option opts [ Session ] :session The session to use for the operation.
+    # @option opts [ Integer ] :size The size of the capped collection.
+    # @option opts [ Hash ] :time_series Create a time-series collection.
+    #   The hash may have the following items:
+    #   - *:timeField* -- The name of the field which contains the date in each
+    #     time series document.
+    #   - *:metaField* -- The name of the field which contains metadata in each
+    #     time series document.
+    #   - *:granularity* -- Set the granularity to the value that is the closest
+    #     match to the time span between consecutive incoming measurements.
+    #     Possible values are "seconds" (default), "minutes", and "hours".
+    # @option opts [ Hash ] :validator Hash describing document validation
+    #   options for the collection.
+    # @option opts [ String ] :view_on The name of the source collection or
+    #   view from which to create a view.
+    # @option opts [ Hash ] :write Deprecated. Equivalent to :write_concern
     #   option.
-    # @option options [ Hash ] :write_concern The write concern options.
+    # @option opts [ Hash ] :write_concern The write concern options.
     #   Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
-    # @option options [ Hash ] :time_series Create a time-series collection.
-    #   See https://mongodb.com/docs/manual/core/timeseries-collections/ for more
-    #   information about time-series collection.
-    # @option options [ Integer ] :expire_after Number indicating
-    #   after how many seconds old time-series data should be deleted.
-    # @options clustered_index [ Hash ] :clustered_index Create a clustered index.
-    #   This option specifies how this collection should be clustered on _id.
-    #   See https://www.mongodb.com/docs/v5.3/reference/method/db.createCollection/#std-label-db.createCollection.clusteredIndex
-    #   for more information about this option.
     #
     # @since 2.0.0
     def initialize(database, name, options = {})
@@ -204,17 +244,37 @@ def write_concern_with_session(session)
       wc
     end
 
-    # Provides a new collection with either a new read preference or new write concern
-    # merged over the existing read preference / write concern.
+    # Provides a new collection with either a new read preference, new read
+    # concern or new write concern merged over the existing read preference /
+    # read concern / write concern.
     #
     # @example Get a collection with a changed read preference.
     #   collection.with(read: { mode: :primary_preferred })
+
+    # @example Get a collection with a changed read concern.
+    #   collection.with(read_concern: { level: :majority })
     #
     # @example Get a collection with a changed write concern.
     #   collection.with(write_concern: { w:  3 })
-
+    #
     # @param [ Hash ] new_options The new options to use.
     #
+    # @option new_options [ Hash ] :read The read preference options.
+    #   The hash may have the following items:
+    #   - *:mode* -- read preference specified as a symbol; valid values are
+    #     *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
+    #     and *:nearest*.
+    #   - *:tag_sets* -- an array of hashes.
+    #   - *:local_threshold*.
+    # @option new_options [ Hash ] :read_concern The read concern options hash,
+    #   with the following optional keys:
+    #   - *:level* -- the read preference level as a symbol; valid values
+    #      are *:local*, *:majority*, and *:snapshot*
+    # @option new_options [ Hash ] :write Deprecated. Equivalent to :write_concern
+    #   option.
+    # @option new_options [ Hash ] :write_concern The write concern options.
+    #   Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
+    #
     # @return [ Mongo::Collection ] A new collection instance.
     #
     # @since 2.1.0
@@ -237,7 +297,7 @@ def with(new_options)
     # @example Is the collection capped?
     #   collection.capped?
     #
-    # @return [ true, false ] If the collection is capped.
+    # @return [ true | false ] If the collection is capped.
     #
     # @since 2.0.0
     def capped?
@@ -251,17 +311,48 @@ def capped?
     #
     # @param [ Hash ] opts The options for the create operation.
     #
-    # @option opts [ Session ] :session The session to use for the operation.
-    # @option opts [ Hash ] :write_concern The write concern options.
-    # @option opts [ Hash ] :time_series Create a time-series collection.
-    # @option opts [ Integer ] :expire_after Number indicating
-    #   after how many seconds old time-series data should be deleted.
+    # @option opts [ true | false ] :capped Create a fixed-sized collection.
     # @option opts [ Hash ] :change_stream_pre_and_post_images Used to enable
     #   pre- and post-images on the created collection.
+    #   The hash may have the following items:
+    #   - *:enabled* -- true or false.
+    # @option opts [ Hash ] :clustered_index Create a clustered index.
+    #   This option specifies how this collection should be clustered on _id.
+    #   The hash may have the following items:
+    #   - *:key* -- The clustered index key field. Must be set to { _id: 1 }.
+    #   - *:unique* -- Must be set to true. The collection will not accept
+    #     inserted or updated documents where the clustered index key value
+    #     matches an existing value in the index.
+    #   - *:name* -- Optional. A name that uniquely identifies the clustered index.
+    # @option opts [ Hash ] :collation The collation to use.
     # @option opts [ Hash ] :encrypted_fields Hash describing encrypted fields
     #   for queryable encryption.
+    # @option opts [ Integer ] :expire_after Number indicating
+    #   after how many seconds old time-series data should be deleted.
+    # @option opts [ Integer ] :max The maximum number of documents in a
+    #   capped collection. The size limit takes precedents over max.
+    # @option opts [ Array<Hash> ] :pipeline An array of pipeline stages.
+    #   A view will be created by applying this pipeline to the view_on
+    #   collection or view.
+    # @option opts [ Session ] :session The session to use for the operation.
+    # @option opts [ Integer ] :size The size of the capped collection.
+    # @option opts [ Hash ] :time_series Create a time-series collection.
+    #   The hash may have the following items:
+    #   - *:timeField* -- The name of the field which contains the date in each
+    #     time series document.
+    #   - *:metaField* -- The name of the field which contains metadata in each
+    #     time series document.
+    #   - *:granularity* -- Set the granularity to the value that is the closest
+    #     match to the time span between consecutive incoming measurements.
+    #     Possible values are "seconds" (default), "minutes", and "hours".
     # @option opts [ Hash ] :validator Hash describing document validation
     #   options for the collection.
+    # @option opts [ String ] :view_on The name of the source collection or
+    #   view from which to create a view.
+    # @option opts [ Hash ] :write Deprecated. Equivalent to :write_concern
+    #   option.
+    # @option opts [ Hash ] :write_concern The write concern options.
+    #   Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
     #
     # @return [ Result ] The result of the command.
     #
@@ -318,8 +409,10 @@ def create(opts = {})
     #
     # @param [ Hash ] opts The options for the drop operation.
     #
-    # @option options [ Session ] :session The session to use for the operation.
+    # @option opts [ Session ] :session The session to use for the operation.
     # @option opts [ Hash ] :write_concern The write concern options.
+    # @option opts [ Hash | nil ] :encrypted_fields Encrypted fields hash that
+    #   was provided to `create` collection helper.
     #
     # @return [ Result ] The result of the command.
     #
@@ -356,27 +449,27 @@ def drop(opts = {})
     # @param [ Hash ] filter The filter to use in the find.
     # @param [ Hash ] options The options for the find.
     #
-    # @option options [ true, false ] :allow_disk_use When set to true, the
+    # @option options [ true | false ] :allow_disk_use When set to true, the
     #   server can write temporary data to disk while executing the find
     #   operation. This option is only available on MongoDB server versions
     #   4.4 and newer.
-    # @option options [ true, false ] :allow_partial_results Allows the query to get partial
+    # @option options [ true | false ] :allow_partial_results Allows the query to get partial
     #   results if some shards are down.
     # @option options [ Integer ] :batch_size The number of documents returned in each batch
     #   of results from MongoDB.
     # @option options [ Hash ] :collation The collation to use.
-    # @option options [ Object ] :comment A user-provided
-    #   comment to attach to this command.
+    # @option options [ Object ] :comment A user-provided comment to attach to
+    #   this command.
     # @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use.
     # @option options [ Integer ] :limit The max number of docs to return from the query.
     # @option options [ Integer ] :max_time_ms
     #   The maximum amount of time to allow the query to run, in milliseconds.
     # @option options [ Hash ] :modifiers A document containing meta-operators modifying the
     #   output or behavior of a query.
-    # @option options [ true, false ] :no_cursor_timeout The server normally times out idle
+    # @option options [ true | false ] :no_cursor_timeout The server normally times out idle
     #   cursors after an inactivity period (10 minutes) to prevent excess memory use.
     #   Set this option to prevent that.
-    # @option options [ true, false ] :oplog_replay For internal replication
+    # @option options [ true | false ] :oplog_replay For internal replication
     #   use only, applications should not set this option.
     # @option options [ Hash ] :projection The fields to include or exclude from each doc
     #   in the result set.
@@ -402,11 +495,11 @@ def find(filter = nil, options = {})
     # @param [ Array<Hash> ] pipeline The aggregation pipeline.
     # @param [ Hash ] options The aggregation options.
     #
-    # @option options [ true, false ] :allow_disk_use Set to true if disk
+    # @option options [ true | false ] :allow_disk_use Set to true if disk
     #   usage is allowed during the aggregation.
     # @option options [ Integer ] :batch_size The number of documents to return
     #   per batch.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :collation The collation to use.
     # @option options [ Object ] :comment A user-provided
@@ -416,7 +509,7 @@ def find(filter = nil, options = {})
     #   See the server documentation for details.
     # @option options [ Integer ] :max_time_ms The maximum amount of time in
     #   milliseconds to allow the aggregation to run.
-    # @option options [ true, false ] :use_cursor Indicates whether the command
+    # @option options [ true | false ] :use_cursor Indicates whether the command
     #   will request that the server provide results using a cursor. Note that
     #   as of server version 3.6, aggregations always provide results using a
     #   cursor and this option is therefore not valid.
@@ -642,7 +735,13 @@ def inspect
     # @param [ Hash ] document The document to insert.
     # @param [ Hash ] opts The insert options.
     #
+    # @option opts [ true | false ] :bypass_document_validation Whether or
+    #   not to skip document level validation.
+    # @option opts [ Object ] :comment A user-provided comment to attach to
+    #   this command.
     # @option opts [ Session ] :session The session to use for the operation.
+    # @option opts [ Hash ] :write_concern The write concern options.
+    #   Can be :w => Integer, :fsync => Boolean, :j => Boolean.
     #
     # @return [ Result ] The database response wrapper.
     #
@@ -684,12 +783,18 @@ def insert_one(document, opts = {})
     # @example Insert documents into the collection.
     #   collection.insert_many([{ name: 'test' }])
     #
-    # @param [ Array<Hash> ] documents The documents to insert.
+    # @param [ Enumerable<Hash> ] documents The documents to insert.
     # @param [ Hash ] options The insert options.
     #
+    # @option options [ true | false ] :bypass_document_validation Whether or
+    #   not to skip document level validation.
+    # @option options [ Object ] :comment A user-provided comment to attach to
+    #   this command.
     # @option options [ true | false ] :ordered Whether the operations
     #   should be executed in order.
     # @option options [ Session ] :session The session to use for the operation.
+    # @option options [ Hash ] :write_concern The write concern options.
+    #   Can be :w => Integer, :fsync => Boolean, :j => Boolean.
     #
     # @return [ Result ] The database response wrapper.
     #
@@ -706,14 +811,14 @@ def insert_many(documents, options = {})
     # @example Execute a bulk write.
     #   collection.bulk_write(operations, options)
     #
-    # @param [ Array<Hash> ] requests The bulk write requests.
+    # @param [ Enumerable<Hash> ] requests The bulk write requests.
     # @param [ Hash ] options The options.
     #
-    # @option options [ true, false ] :ordered Whether the operations
+    # @option options [ true | false ] :ordered Whether the operations
     #   should be executed in order.
     # @option options [ Hash ] :write_concern The write concern options.
     #   Can be :w => Integer, :fsync => Boolean, :j => Boolean.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Session ] :session The session to use for the set of operations.
     # @option options [ Hash ] :let Mapping of variables to use in the command.
@@ -802,9 +907,9 @@ def parallel_scan(cursor_count, options = {})
     # @param [ Hash ] replacement The replacement document..
     # @param [ Hash ] options The options.
     #
-    # @option options [ true, false ] :upsert Whether to upsert if the
+    # @option options [ true | false ] :upsert Whether to upsert if the
     #   document doesn't exist.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :collation The collation to use.
     # @option options [ Session ] :session The session to use.
@@ -829,9 +934,9 @@ def replace_one(filter, replacement, options = {})
     # @param [ Hash | Array<Hash> ] update The update document or pipeline.
     # @param [ Hash ] options The options.
     #
-    # @option options [ true, false ] :upsert Whether to upsert if the
+    # @option options [ true | false ] :upsert Whether to upsert if the
     #   document doesn't exist.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :collation The collation to use.
     # @option options [ Array ] :array_filters A set of filters specifying to which array elements
@@ -858,9 +963,9 @@ def update_many(filter, update, options = {})
     # @param [ Hash | Array<Hash> ] update The update document or pipeline.
     # @param [ Hash ] options The options.
     #
-    # @option options [ true, false ] :upsert Whether to upsert if the
+    # @option options [ true | false ] :upsert Whether to upsert if the
     #   document doesn't exist.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :collation The collation to use.
     # @option options [ Array ] :array_filters A set of filters specifying to which array elements
@@ -927,8 +1032,8 @@ def find_one_and_delete(filter, options = {})
     # @option options [ Hash ] :sort The key and direction pairs by which the result set
     #   will be sorted.
     # @option options [ Symbol ] :return_document Either :before or :after.
-    # @option options [ true, false ] :upsert Whether to upsert if the document doesn't exist.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :upsert Whether to upsert if the document doesn't exist.
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :write_concern The write concern options.
     #   Defaults to the collection's write concern.
@@ -967,8 +1072,8 @@ def find_one_and_update(filter, update, options = {})
     # @option options [ Hash ] :sort The key and direction pairs by which the result set
     #   will be sorted.
     # @option options [ Symbol ] :return_document Either :before or :after.
-    # @option options [ true, false ] :upsert Whether to upsert if the document doesn't exist.
-    # @option options [ true, false ] :bypass_document_validation Whether or
+    # @option options [ true | false ] :upsert Whether to upsert if the document doesn't exist.
+    # @option options [ true | false ] :bypass_document_validation Whether or
     #   not to skip document level validation.
     # @option options [ Hash ] :write_concern The write concern options.
     #   Defaults to the collection's write concern.
diff --git a/lib/mongo/collection/view.rb b/lib/mongo/collection/view.rb
index dd58ef89ca..46e7eca2c7 100644
--- a/lib/mongo/collection/view.rb
+++ b/lib/mongo/collection/view.rb
@@ -127,6 +127,7 @@ def hash
       #   return in each response from MongoDB.
       # @option options [ Hash ] :collation The collation to use.
       # @option options [ String ] :comment Associate a comment with the query.
+      # @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use.
       # @option options [ Hash ] :explain Execute an explain with the provided
       #   explain options (known options are :verbose and :verbosity) rather
       #   than a find.
diff --git a/lib/mongo/collection/view/iterable.rb b/lib/mongo/collection/view/iterable.rb
index 943755aed5..d8ae3c3f2e 100644
--- a/lib/mongo/collection/view/iterable.rb
+++ b/lib/mongo/collection/view/iterable.rb
@@ -185,6 +185,8 @@ def initial_query_op(session)
             collection.client.log_warn("The :oplog_replay option is deprecated and ignored by MongoDB 4.4 and later")
           end
 
+          maybe_set_tailable_options(spec)
+
           if explained?
             spec[:explain] = options[:explain]
             Operation::Explain.new(spec)
@@ -200,6 +202,19 @@ def send_initial_query(server, session = nil)
         def use_query_cache?
           QueryCache.enabled? && !collection.system_collection?
         end
+
+        # Add tailable cusror options to the command specifiction if needed.
+        #
+        # @param [ Hash ] spec The command specification.
+        def maybe_set_tailable_options(spec)
+          case cursor_type
+          when :tailable
+            spec[:tailable] = true
+          when :tailable_await
+            spec[:tailable] = true
+            spec[:await_data] = true
+          end
+        end
       end
     end
   end
diff --git a/lib/mongo/collection/view/writable.rb b/lib/mongo/collection/view/writable.rb
index 22cf2d32f8..34b35d0478 100644
--- a/lib/mongo/collection/view/writable.rb
+++ b/lib/mongo/collection/view/writable.rb
@@ -98,8 +98,6 @@ def find_one_and_delete(opts = {})
           end.first&.fetch('value', nil)
         end
 
-        # db['users'].bulk_write([{insert_one: {x: 1}}, {insert_one: {x: 2}}])
-
         # Finds a single document and replaces it.
         #
         # @example Find a document and replace it, returning the original.
diff --git a/lib/mongo/crypt.rb b/lib/mongo/crypt.rb
index 202def248b..b363d8169b 100644
--- a/lib/mongo/crypt.rb
+++ b/lib/mongo/crypt.rb
@@ -35,5 +35,16 @@ module Crypt
     autoload(:ExplicitEncrypter, 'mongo/crypt/explicit_encrypter')
     autoload(:AutoEncrypter, 'mongo/crypt/auto_encrypter')
     autoload(:KMS, 'mongo/crypt/kms')
+
+    def validate_ffi!
+      return if defined?(FFI)
+
+      require 'ffi'
+    rescue LoadError => e
+      raise Error::UnmetDependency, 'Cannot enable encryption because the ffi gem ' \
+                                    "has not been installed. Add \"gem 'ffi'\" to your Gemfile and run " \
+                                    "\"bundle install\" to install the gem. (#{e.class}: #{e})"
+    end
+    module_function :validate_ffi!
   end
 end
diff --git a/lib/mongo/crypt/auto_encrypter.rb b/lib/mongo/crypt/auto_encrypter.rb
index 31bdd68094..74002dd0ab 100644
--- a/lib/mongo/crypt/auto_encrypter.rb
+++ b/lib/mongo/crypt/auto_encrypter.rb
@@ -91,6 +91,7 @@ class AutoEncrypter
       # @raise [ ArgumentError ] If required options are missing or incorrectly
       #   formatted.
       def initialize(options)
+        Crypt.validate_ffi!
         # Note that this call may eventually, via other method invocations,
         # create additional clients which have to be cleaned up.
         @options = set_default_options(options).freeze
diff --git a/lib/mongo/crypt/explicit_encrypter.rb b/lib/mongo/crypt/explicit_encrypter.rb
index e2256cb651..635c5b04f3 100644
--- a/lib/mongo/crypt/explicit_encrypter.rb
+++ b/lib/mongo/crypt/explicit_encrypter.rb
@@ -36,6 +36,7 @@ class ExplicitEncrypter
       #   should be hashes of TLS connection options. The options are equivalent
       #   to TLS connection options of Mongo::Client.
       def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options)
+        Crypt.validate_ffi!
         @crypt_handle = Handle.new(
           kms_providers,
           kms_tls_options,
diff --git a/lib/mongo/crypt/kms.rb b/lib/mongo/crypt/kms.rb
index f871aa145b..ef9f76c27d 100644
--- a/lib/mongo/crypt/kms.rb
+++ b/lib/mongo/crypt/kms.rb
@@ -89,7 +89,6 @@ def validate_tls_options(options)
             %i(
               ssl_verify_certificate
               ssl_verify_hostname
-              ssl_verify_ocsp_endpoint
             ).each do |opt|
               if provider_opts[opt] == false
                 raise ArgumentError.new(
diff --git a/lib/mongo/error/invalid_read_option.rb b/lib/mongo/error/invalid_read_option.rb
index 7115022f23..f1115d1b2a 100644
--- a/lib/mongo/error/invalid_read_option.rb
+++ b/lib/mongo/error/invalid_read_option.rb
@@ -31,7 +31,7 @@ class InvalidReadOption < Error
       #
       # @since 2.6.0
       def initialize(read_option, msg)
-        super("Invalid read option: #{read_option}: #{msg}")
+        super("Invalid read preference value: #{read_option.inspect}: #{msg}")
       end
     end
   end
diff --git a/lib/mongo/grid/file/chunk.rb b/lib/mongo/grid/file/chunk.rb
index 9ac8754f8b..c9a5d418e4 100644
--- a/lib/mongo/grid/file/chunk.rb
+++ b/lib/mongo/grid/file/chunk.rb
@@ -133,11 +133,12 @@ def initialize(document)
         #
         # @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
         # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ String ] The raw BSON data.
         #
         # @since 2.0.0
-        def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = BSON::Config.validating_keys?)
+        def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
           document.to_bson(buffer)
         end
 
diff --git a/lib/mongo/grid/file/info.rb b/lib/mongo/grid/file/info.rb
index 1ef41744b4..3a6890be77 100644
--- a/lib/mongo/grid/file/info.rb
+++ b/lib/mongo/grid/file/info.rb
@@ -228,11 +228,12 @@ def update_md5(bytes)
         #
         # @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
         # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ String ] The raw BSON data.
         #
         # @since 2.0.0
-        def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = BSON::Config.validating_keys?)
+        def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
           if @client_md5 && !document[:md5]
             document[:md5] = @client_md5.hexdigest
           end
diff --git a/lib/mongo/operation.rb b/lib/mongo/operation.rb
index 1d345ad54c..2c494bffba 100644
--- a/lib/mongo/operation.rb
+++ b/lib/mongo/operation.rb
@@ -9,7 +9,6 @@
 require 'mongo/operation/shared/executable_no_validate'
 require 'mongo/operation/shared/executable_transaction_label'
 require 'mongo/operation/shared/polymorphic_lookup'
-require 'mongo/operation/shared/polymorphic_operation'
 require 'mongo/operation/shared/polymorphic_result'
 require 'mongo/operation/shared/read_preference_supported'
 require 'mongo/operation/shared/bypass_document_validation'
@@ -22,8 +21,7 @@
 require 'mongo/operation/shared/specifiable'
 require 'mongo/operation/shared/validatable'
 require 'mongo/operation/shared/object_id_generator'
-require 'mongo/operation/shared/op_msg_or_command'
-require 'mongo/operation/shared/op_msg_or_find_command'
+require 'mongo/operation/shared/op_msg_executable'
 
 require 'mongo/operation/op_msg_base'
 require 'mongo/operation/command'
diff --git a/lib/mongo/operation/aggregate.rb b/lib/mongo/operation/aggregate.rb
index cef9575f3a..98084df678 100644
--- a/lib/mongo/operation/aggregate.rb
+++ b/lib/mongo/operation/aggregate.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/aggregate/command'
 require 'mongo/operation/aggregate/op_msg'
 require 'mongo/operation/aggregate/result'
 
@@ -33,7 +32,7 @@ module Operation
     # @since 2.0.0
     class Aggregate
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/aggregate/command.rb b/lib/mongo/operation/aggregate/command.rb
deleted file mode 100644
index 9239d2feaa..0000000000
--- a/lib/mongo/operation/aggregate/command.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Aggregate
-
-      # A MongoDB aggregate operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include PolymorphicResult
-        include ReadPreferenceSupported
-        include WriteConcernSupported
-        include Limited
-
-        private
-
-        def selector(connection)
-          super.tap do |selector|
-            if selector[:collation] && !connection.features.collation_enabled?
-              raise Error::UnsupportedCollation
-            end
-          end
-        end
-
-        def write_concern_supported?(connection)
-          connection.features.collation_enabled?
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/collections_info.rb b/lib/mongo/operation/collections_info.rb
index 371ace3177..370334f2a7 100644
--- a/lib/mongo/operation/collections_info.rb
+++ b/lib/mongo/operation/collections_info.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/collections_info/command'
 require 'mongo/operation/collections_info/result'
 
 module Mongo
@@ -28,23 +27,12 @@ module Operation
     # @since 2.0.0
     class CollectionsInfo
       include Specifiable
-      include PolymorphicOperation
-      include PolymorphicLookup
+      include OpMsgExecutable
 
       private
 
-      def final_operation(connection)
-         op_class = if connection.features.list_collections_enabled?
-          if connection.features.op_msg_enabled?
-            ListCollections::OpMsg
-          else
-            ListCollections::Command
-          end
-        else
-          CollectionsInfo::Command
-        end
-
-        op_class.new(spec)
+      def final_operation
+        ListCollections::OpMsg.new(spec)
       end
     end
   end
diff --git a/lib/mongo/operation/collections_info/command.rb b/lib/mongo/operation/collections_info/command.rb
deleted file mode 100644
index fba3b1b886..0000000000
--- a/lib/mongo/operation/collections_info/command.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class CollectionsInfo
-
-      # A MongoDB collectionInfo operation sent as a command message.
-      #
-      # @api private
-      class Command
-        include Specifiable
-        include Executable
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def get_result(connection, context, options = {})
-          # This is a Mongo::Operation::CollectionsInfo::Result
-          Result.new(*dispatch_message(connection, context), db_name)
-        end
-
-        def selector(connection)
-          {}
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::NAMESPACES, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/command.rb b/lib/mongo/operation/command.rb
index 2939247266..998f58899b 100644
--- a/lib/mongo/operation/command.rb
+++ b/lib/mongo/operation/command.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/command/command'
 require 'mongo/operation/command/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class Command
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/command/command.rb b/lib/mongo/operation/command/command.rb
deleted file mode 100644
index 88e0ea444b..0000000000
--- a/lib/mongo/operation/command/command.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Command
-
-      # A MongoDB command operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-
-        private
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/count.rb b/lib/mongo/operation/count.rb
index 2ebc6c0e12..92601c8086 100644
--- a/lib/mongo/operation/count.rb
+++ b/lib/mongo/operation/count.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/count/command'
 require 'mongo/operation/count/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class Count
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/count/command.rb b/lib/mongo/operation/count/command.rb
deleted file mode 100644
index a2d38016bd..0000000000
--- a/lib/mongo/operation/count/command.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Count
-
-      # A MongoDB count operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-
-        private
-
-        def selector(connection)
-          selector = spec[:selector]
-          selector = apply_collation(selector, connection, spec[:collation])
-          selector
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/create.rb b/lib/mongo/operation/create.rb
index e5371f940c..af266f268a 100644
--- a/lib/mongo/operation/create.rb
+++ b/lib/mongo/operation/create.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/create/command'
 require 'mongo/operation/create/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class Create
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/create/command.rb b/lib/mongo/operation/create/command.rb
deleted file mode 100644
index 2cef393c9d..0000000000
--- a/lib/mongo/operation/create/command.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Create
-
-      # A MongoDB create collection operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          selector = spec[:selector]
-          selector = apply_collation(selector, connection, spec[:collation])
-          selector
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/create_index.rb b/lib/mongo/operation/create_index.rb
index 0f4482118e..900f3025d1 100644
--- a/lib/mongo/operation/create_index.rb
+++ b/lib/mongo/operation/create_index.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/create_index/command'
 require 'mongo/operation/create_index/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class CreateIndex
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/create_index/command.rb b/lib/mongo/operation/create_index/command.rb
deleted file mode 100644
index 5bbf6a8c5b..0000000000
--- a/lib/mongo/operation/create_index/command.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class CreateIndex
-
-      # A MongoDB createindex operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          indexes.each do |index|
-            if index[:collation] && !connection.features.collation_enabled?
-              raise Error::UnsupportedCollation
-            end
-          end
-
-          {
-            createIndexes: coll_name,
-            indexes: indexes,
-          }.tap do |selector|
-            if commit_quorum = spec[:commit_quorum]
-              unless connection.features.commit_quorum_enabled?
-                raise Error::UnsupportedOption.commit_quorum_error
-              end
-              selector[:commitQuorum] = commit_quorum
-            end
-          end
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/create_user.rb b/lib/mongo/operation/create_user.rb
index d426dc7324..e27ec9d61e 100644
--- a/lib/mongo/operation/create_user.rb
+++ b/lib/mongo/operation/create_user.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/create_user/command'
 require 'mongo/operation/create_user/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class CreateUser
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/create_user/command.rb b/lib/mongo/operation/create_user/command.rb
deleted file mode 100644
index 032cd6c629..0000000000
--- a/lib/mongo/operation/create_user/command.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class CreateUser
-
-      # A MongoDB createuser operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          { :createUser => user.name }.merge(user.spec)
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/delete.rb b/lib/mongo/operation/delete.rb
index df18238836..dc2c6f9e02 100644
--- a/lib/mongo/operation/delete.rb
+++ b/lib/mongo/operation/delete.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/delete/command'
 require 'mongo/operation/delete/op_msg'
 require 'mongo/operation/delete/result'
 require 'mongo/operation/delete/bulk_result'
diff --git a/lib/mongo/operation/delete/command.rb b/lib/mongo/operation/delete/command.rb
deleted file mode 100644
index e1e3ea485d..0000000000
--- a/lib/mongo/operation/delete/command.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Delete
-
-      # A MongoDB delete operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-        include ExecutableNoValidate
-        include PolymorphicResult
-        include Validatable
-
-        private
-
-        def selector(connection)
-          {
-            delete: coll_name,
-            deletes: validate_updates(connection, send(IDENTIFIER)),
-            ordered: ordered?,
-          }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/distinct.rb b/lib/mongo/operation/distinct.rb
index 2653a74088..46995b4ef3 100644
--- a/lib/mongo/operation/distinct.rb
+++ b/lib/mongo/operation/distinct.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/distinct/command'
 require 'mongo/operation/distinct/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.5.0
     class Distinct
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/distinct/command.rb b/lib/mongo/operation/distinct/command.rb
deleted file mode 100644
index 091ae0586c..0000000000
--- a/lib/mongo/operation/distinct/command.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Distinct
-
-      # A MongoDB distinct operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-
-        private
-
-        def selector(connection)
-          selector = spec[:selector]
-          selector = apply_collation(selector, connection, spec[:collation])
-          selector
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/drop.rb b/lib/mongo/operation/drop.rb
index b7467a5ba3..b5b29a26b3 100644
--- a/lib/mongo/operation/drop.rb
+++ b/lib/mongo/operation/drop.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/drop/command'
 require 'mongo/operation/drop/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.4.0
     class Drop
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/drop/command.rb b/lib/mongo/operation/drop/command.rb
deleted file mode 100644
index 862bb2ff97..0000000000
--- a/lib/mongo/operation/drop/command.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Drop
-
-      # A MongoDB drop collection operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/drop_database.rb b/lib/mongo/operation/drop_database.rb
index be75a27fb5..db00a738c3 100644
--- a/lib/mongo/operation/drop_database.rb
+++ b/lib/mongo/operation/drop_database.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/drop_database/command'
 require 'mongo/operation/drop_database/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.4.0
     class DropDatabase
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/drop_database/command.rb b/lib/mongo/operation/drop_database/command.rb
deleted file mode 100644
index 983090af79..0000000000
--- a/lib/mongo/operation/drop_database/command.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class DropDatabase
-
-      # A MongoDB dropdatabase operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/drop_index.rb b/lib/mongo/operation/drop_index.rb
index ae70369525..493a911f38 100644
--- a/lib/mongo/operation/drop_index.rb
+++ b/lib/mongo/operation/drop_index.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/drop_index/command'
 require 'mongo/operation/drop_index/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class DropIndex
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/drop_index/command.rb b/lib/mongo/operation/drop_index/command.rb
deleted file mode 100644
index f78c634f8b..0000000000
--- a/lib/mongo/operation/drop_index/command.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class DropIndex
-
-      # A MongoDB dropindex operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          { :dropIndexes => coll_name, :index => index_name }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/explain.rb b/lib/mongo/operation/explain.rb
index e4757e19cc..50a61f2fcf 100644
--- a/lib/mongo/operation/explain.rb
+++ b/lib/mongo/operation/explain.rb
@@ -17,8 +17,6 @@
 
 require 'mongo/operation/explain/result'
 require 'mongo/operation/explain/op_msg'
-require 'mongo/operation/explain/command'
-require 'mongo/operation/explain/legacy'
 
 module Mongo
   module Operation
@@ -30,7 +28,7 @@ module Operation
     # @since 2.5.0
     class Explain
       include Specifiable
-      include OpMsgOrFindCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/explain/command.rb b/lib/mongo/operation/explain/command.rb
deleted file mode 100644
index 05c2783be8..0000000000
--- a/lib/mongo/operation/explain/command.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Explain
-
-      # A MongoDB explain command operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.0.0
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          # The mappings are BSON::Documents and as such store keys as
-          # strings, the spec here has symbol keys.
-          spec = BSON::Document.new(self.spec)
-
-          if spec[:collation] && !connection.features.collation_enabled?
-            raise Error::UnsupportedCollation
-          end
-
-          {
-            explain: {
-              find: coll_name,
-            }.update(Find::Builder::Command.selector(spec, connection)),
-          }.update(spec[:explain] || {})
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/explain/legacy.rb b/lib/mongo/operation/explain/legacy.rb
deleted file mode 100644
index cd54bc6b2f..0000000000
--- a/lib/mongo/operation/explain/legacy.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2015-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Explain
-
-      # A MongoDB explain command operation sent as a legacy wire protocol message.
-      #
-      # @api private
-      #
-      # @since 2.0.0
-      class Legacy
-        include Specifiable
-        include Executable
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def message(connection)
-          if spec[:collation] && !connection.features.collation_enabled?
-            raise Error::UnsupportedCollation
-          end
-
-          Protocol::Query.new(
-            db_name,
-            coll_name,
-            Find::Builder::Legacy.selector(spec, connection),
-            options(connection).update(
-              Find::Builder::Legacy.query_options(spec, connection),
-            ),
-          )
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/find.rb b/lib/mongo/operation/find.rb
index a4528c015a..533a6e00a6 100644
--- a/lib/mongo/operation/find.rb
+++ b/lib/mongo/operation/find.rb
@@ -15,9 +15,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/find/command'
 require 'mongo/operation/find/op_msg'
-require 'mongo/operation/find/legacy'
 require 'mongo/operation/find/result'
 require 'mongo/operation/find/builder'
 
@@ -31,7 +29,7 @@ module Operation
     # @since 2.0.0
     class Find
       include Specifiable
-      include OpMsgOrFindCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/find/builder.rb b/lib/mongo/operation/find/builder.rb
index c1d8b1fea6..552a93d35e 100644
--- a/lib/mongo/operation/find/builder.rb
+++ b/lib/mongo/operation/find/builder.rb
@@ -17,5 +17,4 @@
 
 require 'mongo/operation/find/builder/command'
 require 'mongo/operation/find/builder/flags'
-require 'mongo/operation/find/builder/legacy'
 require 'mongo/operation/find/builder/modifiers'
diff --git a/lib/mongo/operation/find/builder/legacy.rb b/lib/mongo/operation/find/builder/legacy.rb
deleted file mode 100644
index 47a2e59ad1..0000000000
--- a/lib/mongo/operation/find/builder/legacy.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2015-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Find
-      module Builder
-
-        # Builds a legacy OP_QUERY specification from options.
-        #
-        # @api private
-        module Legacy
-
-          # Mappings from driver options to legacy server values.
-          #
-          # @since 2.2.0
-          DRIVER_MAPPINGS = {
-            comment: '$comment',
-            explain: '$explain',
-            hint: '$hint',
-            max_scan: '$maxScan',
-            max_time_ms: '$maxTimeMS',
-            max_value: '$max',
-            min_value: '$min',
-            show_disk_loc: '$showDiskLoc',
-            snapshot: '$snapshot',
-            sort: '$orderby',
-            return_key: '$returnKey',
-          }.freeze
-
-          module_function def selector(spec, connection)
-            if Lint.enabled?
-              if spec.keys.any? { |k| String === k }
-                raise Error::LintError, "The spec must contain symbol keys only"
-              end
-            end
-
-            # Server versions that do not have the find command feature
-            # (versions older than 3.2) do not support the allow_disk_use option
-            # but perform no validation and will not raise an error if it is
-            # specified. If the allow_disk_use option is specified, raise an error
-            # to alert the user.
-            unless spec[:allow_disk_use].nil?
-              raise Error::UnsupportedOption.allow_disk_use_error
-            end
-
-            if spec[:collation] && !connection.features.collation_enabled?
-              raise Error::UnsupportedCollation
-            end
-
-            modifiers = {}
-            DRIVER_MAPPINGS.each do |k, server_k|
-              unless (value = spec[k]).nil?
-                modifiers[server_k] = value
-              end
-            end
-
-            selector = spec[:filter] || BSON::Document.new
-            # Write nil into rp if not talking to mongos, rather than false
-            rp = if connection.description.mongos?
-              read_pref_formatted(spec)
-            end
-            if modifiers.any? || rp
-              selector = {'$query' => selector}.update(modifiers)
-
-              if rp
-                selector['$readPreference'] = rp
-              end
-            end
-
-            selector
-          end
-
-          module_function def query_options(spec, connection)
-            query_options = {
-              project: spec[:projection],
-              skip: spec[:skip],
-              limit: spec[:limit],
-              # batch_size is converted to batchSize by Mongo::Protocol::Query.
-              batch_size: spec[:batch_size],
-            }
-
-            unless (flags = Builder::Flags.map_flags(spec)).empty?
-              query_options[:flags] = ((query_options[:flags] || []) + flags).uniq
-            end
-
-            query_options
-          end
-
-          private
-
-          module_function def read_pref_formatted(spec)
-            if spec[:read_preference]
-              raise ArgumentError, "Spec cannot include :read_preference here, use :read"
-            end
-
-            if read = spec[:read]
-              read_pref = ServerSelector.get(read).to_mongos
-              Mongo::Lint.validate_camel_case_read_preference(read_pref)
-              read_pref
-            else
-              nil
-            end
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/find/command.rb b/lib/mongo/operation/find/command.rb
deleted file mode 100644
index 181b557c10..0000000000
--- a/lib/mongo/operation/find/command.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Find
-
-      # A MongoDB find operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          # The mappings are BSON::Documents and as such store keys as
-          # strings, the spec here has symbol keys
-          spec = BSON::Document.new(self.spec)
-          {
-            find: coll_name,
-          }.update(Find::Builder::Command.selector(spec, connection))
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/find/legacy.rb b/lib/mongo/operation/find/legacy.rb
deleted file mode 100644
index 33989d1b8d..0000000000
--- a/lib/mongo/operation/find/legacy.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-require 'mongo/operation/find/legacy/result'
-
-module Mongo
-  module Operation
-    class Find
-
-      # A MongoDB find operation sent as a legacy wire protocol message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Legacy
-        include Specifiable
-        include Executable
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def message(connection)
-          selector = Find::Builder::Legacy.selector(spec, connection)
-          options = options(connection).update(
-            Find::Builder::Legacy.query_options(spec, connection),
-          )
-          Protocol::Query.new(
-            db_name,
-            coll_name,
-            selector,
-            options,
-          )
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/find/legacy/result.rb b/lib/mongo/operation/find/legacy/result.rb
deleted file mode 100644
index d4d0068661..0000000000
--- a/lib/mongo/operation/find/legacy/result.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2014-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Find
-      class Legacy
-
-        # Defines custom behavior of results for a query.
-        #
-        # @since 2.1.0
-        # @api semiprivate
-        class Result < Operation::Result
-          include Operation::Result::UseLegacyErrorParser
-
-          # Determine if the query was a success.
-          #
-          # @example Was the query successful?
-          #   result.successful?
-          #
-          # @return [ true, false ] If the query was successful.
-          #
-          # @since 2.0.0
-          # @api public
-          def successful?
-            !query_failure?
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/get_more.rb b/lib/mongo/operation/get_more.rb
index 455f8f6394..b7ae7ae5c7 100644
--- a/lib/mongo/operation/get_more.rb
+++ b/lib/mongo/operation/get_more.rb
@@ -16,9 +16,7 @@
 # limitations under the License.
 
 require 'mongo/operation/get_more/command_builder'
-require 'mongo/operation/get_more/command'
 require 'mongo/operation/get_more/op_msg'
-require 'mongo/operation/get_more/legacy'
 require 'mongo/operation/get_more/result'
 
 module Mongo
@@ -31,7 +29,7 @@ module Operation
     # @since 2.5.0
     class GetMore
       include Specifiable
-      include OpMsgOrFindCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/get_more/command.rb b/lib/mongo/operation/get_more/command.rb
deleted file mode 100644
index 9188d20e10..0000000000
--- a/lib/mongo/operation/get_more/command.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class GetMore
-
-      # A MongoDB getMore operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-        include CommandBuilder
-
-        private
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/get_more/legacy.rb b/lib/mongo/operation/get_more/legacy.rb
deleted file mode 100644
index e3ac61485f..0000000000
--- a/lib/mongo/operation/get_more/legacy.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class GetMore
-
-      # A MongoDB getMore operation sent as a legacy wire protocol message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Legacy
-        include Specifiable
-        include Executable
-
-        private
-
-        def message(connection)
-          Protocol::GetMore.new(db_name, coll_name, to_return, cursor_id)
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/indexes.rb b/lib/mongo/operation/indexes.rb
index e1ead670f1..779bee85aa 100644
--- a/lib/mongo/operation/indexes.rb
+++ b/lib/mongo/operation/indexes.rb
@@ -15,9 +15,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/indexes/command'
 require 'mongo/operation/indexes/op_msg'
-require 'mongo/operation/indexes/legacy'
 require 'mongo/operation/indexes/result'
 
 module Mongo
@@ -30,21 +28,7 @@ module Operation
     # @since 2.0.0
     class Indexes
       include Specifiable
-      include PolymorphicOperation
-      include PolymorphicLookup
-
-      private
-
-      def final_operation(connection)
-        cls = if connection.features.op_msg_enabled?
-          polymorphic_class(self.class.name, :OpMsg)
-        elsif connection.features.list_indexes_enabled?
-          polymorphic_class(self.class.name, :Command)
-        else
-          polymorphic_class(self.class.name, :Legacy)
-        end
-        cls.new(spec)
-      end
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/indexes/command.rb b/lib/mongo/operation/indexes/command.rb
deleted file mode 100644
index 0fdf19ee20..0000000000
--- a/lib/mongo/operation/indexes/command.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Indexes
-
-      # A MongoDB indexes operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/indexes/legacy.rb b/lib/mongo/operation/indexes/legacy.rb
deleted file mode 100644
index 27ef213f0f..0000000000
--- a/lib/mongo/operation/indexes/legacy.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2014-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Indexes
-
-      # A MongoDB indexes operation sent as a legacy wire protocol message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Legacy
-        include Specifiable
-        include Executable
-        include ReadPreferenceSupported
-
-        private
-
-        def selector(connection)
-          { ns: namespace }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Index::COLLECTION, command(connection), options(connection))
-        end
-
-        def result_class
-          Operation::Result
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/insert.rb b/lib/mongo/operation/insert.rb
index e9df182ccd..948351e5b0 100644
--- a/lib/mongo/operation/insert.rb
+++ b/lib/mongo/operation/insert.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/insert/command'
 require 'mongo/operation/insert/op_msg'
 require 'mongo/operation/insert/result'
 require 'mongo/operation/insert/bulk_result'
diff --git a/lib/mongo/operation/insert/command.rb b/lib/mongo/operation/insert/command.rb
deleted file mode 100644
index c403d7c80c..0000000000
--- a/lib/mongo/operation/insert/command.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Insert
-
-      # A MongoDB insert operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include ExecutableNoValidate
-        include Idable
-        include Limited
-        include WriteConcernSupported
-        include BypassDocumentValidation
-
-        private
-
-        def get_result(connection, context, options = {})
-          # This is a Mongo::Operation::Insert::Result
-          Result.new(*dispatch_message(connection, context), @ids)
-        end
-
-        def selector(connection)
-          { insert: coll_name,
-            documents: send(IDENTIFIER),
-            ordered: ordered? }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/kill_cursors.rb b/lib/mongo/operation/kill_cursors.rb
index fef0cb0597..17c5b1742c 100644
--- a/lib/mongo/operation/kill_cursors.rb
+++ b/lib/mongo/operation/kill_cursors.rb
@@ -16,7 +16,6 @@
 # limitations under the License.
 
 require 'mongo/operation/kill_cursors/command_builder'
-require 'mongo/operation/kill_cursors/command'
 require 'mongo/operation/kill_cursors/op_msg'
 
 module Mongo
@@ -29,7 +28,7 @@ module Operation
     # @since 2.0.0
     class KillCursors
       include Specifiable
-      include OpMsgOrFindCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/kill_cursors/command.rb b/lib/mongo/operation/kill_cursors/command.rb
deleted file mode 100644
index 2e12d3ef69..0000000000
--- a/lib/mongo/operation/kill_cursors/command.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class KillCursors
-
-      # A MongoDB killcursors operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include CommandBuilder
-
-        private
-
-        def selector(connection)
-          {
-            killCursors: coll_name,
-            cursors: int64_cursor_ids,
-          }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, selector(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/list_collections.rb b/lib/mongo/operation/list_collections.rb
index 8e50d8884d..9a2b81c440 100644
--- a/lib/mongo/operation/list_collections.rb
+++ b/lib/mongo/operation/list_collections.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/list_collections/command'
 require 'mongo/operation/list_collections/op_msg'
 require 'mongo/operation/list_collections/result'
 
@@ -29,7 +28,7 @@ module Operation
     # @since 2.0.0
     class ListCollections
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/list_collections/command.rb b/lib/mongo/operation/list_collections/command.rb
deleted file mode 100644
index de89b72617..0000000000
--- a/lib/mongo/operation/list_collections/command.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class ListCollections
-
-      # A MongoDB listcollections operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          (spec[SELECTOR] || {}).merge(listCollections: 1)
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/map_reduce.rb b/lib/mongo/operation/map_reduce.rb
index d77b37a076..90f2af74f6 100644
--- a/lib/mongo/operation/map_reduce.rb
+++ b/lib/mongo/operation/map_reduce.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/map_reduce/command'
 require 'mongo/operation/map_reduce/op_msg'
 require 'mongo/operation/map_reduce/result'
 
@@ -29,7 +28,7 @@ module Operation
     # @since 2.5.0
     class MapReduce
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/map_reduce/command.rb b/lib/mongo/operation/map_reduce/command.rb
deleted file mode 100644
index 116dc64601..0000000000
--- a/lib/mongo/operation/map_reduce/command.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class MapReduce
-
-      # A MongoDB mapreduce operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include WriteConcernSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          super.tap do |selector|
-            if selector[:collation] && !connection.features.collation_enabled?
-              raise Error::UnsupportedCollation
-            end
-          end
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/parallel_scan.rb b/lib/mongo/operation/parallel_scan.rb
index a435bdc922..3e9aea4106 100644
--- a/lib/mongo/operation/parallel_scan.rb
+++ b/lib/mongo/operation/parallel_scan.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/parallel_scan/command'
 require 'mongo/operation/parallel_scan/op_msg'
 require 'mongo/operation/parallel_scan/result'
 
@@ -29,7 +28,7 @@ module Operation
     # @since 2.0.0
     class ParallelScan
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/parallel_scan/command.rb b/lib/mongo/operation/parallel_scan/command.rb
deleted file mode 100644
index c6b36b8dc9..0000000000
--- a/lib/mongo/operation/parallel_scan/command.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class ParallelScan
-
-      # A MongoDB parallelscan operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          sel = { :parallelCollectionScan => coll_name, :numCursors => cursor_count }
-          if read_concern
-            sel[:readConcern] = Options::Mapper.transform_values_to_strings(
-              read_concern)
-          end
-          sel[:maxTimeMS] = max_time_ms if max_time_ms
-          add_read_preference_legacy(sel, connection)
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
-
-
-
-
-
diff --git a/lib/mongo/operation/remove_user.rb b/lib/mongo/operation/remove_user.rb
index f17358c918..e704a02ed8 100644
--- a/lib/mongo/operation/remove_user.rb
+++ b/lib/mongo/operation/remove_user.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/remove_user/command'
 require 'mongo/operation/remove_user/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class RemoveUser
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/remove_user/command.rb b/lib/mongo/operation/remove_user/command.rb
deleted file mode 100644
index 121078223e..0000000000
--- a/lib/mongo/operation/remove_user/command.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class RemoveUser
-
-      # A MongoDB removeuser operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          { :dropUser => user_name }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/shared/polymorphic_operation.rb b/lib/mongo/operation/shared/op_msg_executable.rb
similarity index 82%
rename from lib/mongo/operation/shared/polymorphic_operation.rb
rename to lib/mongo/operation/shared/op_msg_executable.rb
index 1786bd4c4d..345e905f13 100644
--- a/lib/mongo/operation/shared/polymorphic_operation.rb
+++ b/lib/mongo/operation/shared/op_msg_executable.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 # encoding: utf-8
 
-# Copyright (C) 2021 MongoDB Inc.
+# Copyright (C) 2018-2020 MongoDB Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,11 +18,11 @@
 module Mongo
   module Operation
 
-    # Shared behavior of implementing an operation differently based on
-    # the server that will be executing the operation.
+    # Shared behavior of executing the operation as an OpMsg.
     #
     # @api private
-    module PolymorphicOperation
+    module OpMsgExecutable
+      include PolymorphicLookup
 
       # Execute the operation.
       #
@@ -46,8 +46,13 @@ def execute(server, context:, options: {})
       #
       # @return [ Mongo::Operation::Result ] The operation result.
       def execute_with_connection(connection, context:, options: {})
-        operation = final_operation(connection)
-        operation.execute(connection, context: context, options: options)
+        final_operation.execute(connection, context: context, options: options)
+      end
+
+      private
+
+      def final_operation
+        polymorphic_class(self.class.name, :OpMsg).new(spec)
       end
     end
   end
diff --git a/lib/mongo/operation/shared/op_msg_or_command.rb b/lib/mongo/operation/shared/op_msg_or_command.rb
deleted file mode 100644
index 6716237e53..0000000000
--- a/lib/mongo/operation/shared/op_msg_or_command.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-
-    # Shared behavior of executing the operation as an OpMsg when supported
-    # or as a Command otherwise.
-    #
-    # @api private
-    module OpMsgOrCommand
-      include PolymorphicOperation
-      include PolymorphicLookup
-
-      private
-
-      def final_operation(connection)
-        cls = if connection.features.op_msg_enabled?
-          polymorphic_class(self.class.name, :OpMsg)
-        else
-          polymorphic_class(self.class.name, :Command)
-        end
-        cls.new(spec)
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/shared/op_msg_or_find_command.rb b/lib/mongo/operation/shared/op_msg_or_find_command.rb
deleted file mode 100644
index 6084d299ba..0000000000
--- a/lib/mongo/operation/shared/op_msg_or_find_command.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-
-    # Shared behavior of executing the operation as an OpMsg when supported,
-    # as a Command when find command is supported by the server, and as
-    # Legacy otherwise.
-    #
-    # @api private
-    module OpMsgOrFindCommand
-      include PolymorphicOperation
-      include PolymorphicLookup
-
-      private
-
-      def final_operation(connection)
-        cls = if connection.features.op_msg_enabled?
-          polymorphic_class(self.class.name, :OpMsg)
-        elsif connection.features.find_command_enabled?
-          polymorphic_class(self.class.name, :Command)
-        else
-          polymorphic_class(self.class.name, :Legacy)
-        end
-        cls.new(spec)
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/update.rb b/lib/mongo/operation/update.rb
index c97db17aa5..9b4aa12602 100644
--- a/lib/mongo/operation/update.rb
+++ b/lib/mongo/operation/update.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/update/command'
 require 'mongo/operation/update/op_msg'
 require 'mongo/operation/update/result'
 require 'mongo/operation/update/bulk_result'
diff --git a/lib/mongo/operation/update/command.rb b/lib/mongo/operation/update/command.rb
deleted file mode 100644
index 8e02bbbfd2..0000000000
--- a/lib/mongo/operation/update/command.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class Update
-
-      # A MongoDB update operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-        include BypassDocumentValidation
-        include ExecutableNoValidate
-        include PolymorphicResult
-        include Validatable
-
-        private
-
-        def selector(connection)
-          {
-            update: coll_name,
-            updates: validate_updates(connection, send(IDENTIFIER)),
-            ordered: ordered?,
-          }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/update_user.rb b/lib/mongo/operation/update_user.rb
index 498db1a9dc..598f1f1552 100644
--- a/lib/mongo/operation/update_user.rb
+++ b/lib/mongo/operation/update_user.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/update_user/command'
 require 'mongo/operation/update_user/op_msg'
 
 module Mongo
@@ -28,7 +27,7 @@ module Operation
     # @since 2.0.0
     class UpdateUser
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/update_user/command.rb b/lib/mongo/operation/update_user/command.rb
deleted file mode 100644
index 874c9837ca..0000000000
--- a/lib/mongo/operation/update_user/command.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class UpdateUser
-
-      # A MongoDB updateuser operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include WriteConcernSupported
-
-        private
-
-        def selector(connection)
-          { :updateUser => user.name }.merge(user.spec)
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/users_info.rb b/lib/mongo/operation/users_info.rb
index 71cae7f82a..3a1e63422c 100644
--- a/lib/mongo/operation/users_info.rb
+++ b/lib/mongo/operation/users_info.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/users_info/command'
 require 'mongo/operation/users_info/op_msg'
 require 'mongo/operation/users_info/result'
 
@@ -29,7 +28,7 @@ module Operation
     # @since 2.0.0
     class UsersInfo
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/users_info/command.rb b/lib/mongo/operation/users_info/command.rb
deleted file mode 100644
index 9da27cca65..0000000000
--- a/lib/mongo/operation/users_info/command.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2018-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class UsersInfo
-
-      # A MongoDB usersinfo operation sent as a command message.
-      #
-      # @api private
-      #
-      # @since 2.5.2
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include PolymorphicResult
-
-        private
-
-        def selector(connection)
-          { :usersInfo => user_name }
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/operation/write_command.rb b/lib/mongo/operation/write_command.rb
index 3d882f6157..6e322e4d94 100644
--- a/lib/mongo/operation/write_command.rb
+++ b/lib/mongo/operation/write_command.rb
@@ -15,7 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'mongo/operation/write_command/command'
 require 'mongo/operation/write_command/op_msg'
 
 module Mongo
@@ -26,7 +25,7 @@ module Operation
     # @api private
     class WriteCommand
       include Specifiable
-      include OpMsgOrCommand
+      include OpMsgExecutable
     end
   end
 end
diff --git a/lib/mongo/operation/write_command/command.rb b/lib/mongo/operation/write_command/command.rb
deleted file mode 100644
index b97e47d8f1..0000000000
--- a/lib/mongo/operation/write_command/command.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2021 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Operation
-    class WriteCommand
-
-      # A MongoDB write command operation sent as a command message.
-      #
-      # @api private
-      class Command
-        include Specifiable
-        include Executable
-        include Limited
-        include ReadPreferenceSupported
-        include Validatable
-
-        private
-
-        def selector(connection)
-          super.tap do |selector|
-            if selector.key?(:findAndModify)
-              validate_find_options(connection, selector)
-            end
-            if wc = spec[:write_concern]
-              selector[:writeConcern] = wc.options
-            end
-          end
-        end
-
-        def message(connection)
-          Protocol::Query.new(db_name, Database::COMMAND, command(connection), options(connection))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/mongo/protocol.rb b/lib/mongo/protocol.rb
index b09097f3cd..4289a38835 100644
--- a/lib/mongo/protocol.rb
+++ b/lib/mongo/protocol.rb
@@ -10,12 +10,9 @@
 
 # Client Requests
 require 'mongo/protocol/compressed'
-require 'mongo/protocol/delete'
 require 'mongo/protocol/get_more'
-require 'mongo/protocol/insert'
 require 'mongo/protocol/kill_cursors'
 require 'mongo/protocol/query'
-require 'mongo/protocol/update'
 require 'mongo/protocol/msg'
 
 # Server Responses
diff --git a/lib/mongo/protocol/bit_vector.rb b/lib/mongo/protocol/bit_vector.rb
index 3f32fb10ff..998d3d1e42 100644
--- a/lib/mongo/protocol/bit_vector.rb
+++ b/lib/mongo/protocol/bit_vector.rb
@@ -39,9 +39,11 @@ def initialize(layout)
         #
         # @param buffer [ String ] Buffer to receive the serialized vector
         # @param value [ Array<Symbol> ] Array of flags to encode
+        # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ String ] Buffer that received the serialized vector
-        def serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def serialize(buffer, value, validating_keys = nil)
           bits = 0
           value.each { |flag| bits |= (@masks[flag] || 0) }
           buffer.put_int32(bits)
diff --git a/lib/mongo/protocol/caching_hash.rb b/lib/mongo/protocol/caching_hash.rb
index 93694a2875..2087d2df5e 100644
--- a/lib/mongo/protocol/caching_hash.rb
+++ b/lib/mongo/protocol/caching_hash.rb
@@ -38,32 +38,15 @@ def bson_type
       #
       # @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
       # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
+      #   This option is deprecated and will not be used. It will removed in version 3.0.
       #
       # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
-      def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = BSON::Config.validating_keys?)
+      def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
         if !@bytes
-          @bytes = @hash.to_bson(BSON::ByteBuffer.new, validating_keys).to_s
-        elsif needs_validation?(validating_keys)
-          @validated = true
-          return @hash.to_bson(buffer, validating_keys)
+          @bytes = @hash.to_bson(BSON::ByteBuffer.new).to_s
         end
-        @validated ||= validating_keys
         buffer.put_bytes(@bytes)
       end
-
-      private
-
-      # Checks the current value for validating keys, and whether or not this
-      # bson has been validated in the past, and decides if we need to recalculate
-      # the to_bson to check the validations.
-      #
-      # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
-      #
-      # @return [ true, false ] Whether or not the bson needs to be recalculated
-      #   with validation.
-      def needs_validation?(validating_keys)
-        !@validated && validating_keys
-      end
     end
   end
 end
diff --git a/lib/mongo/protocol/delete.rb b/lib/mongo/protocol/delete.rb
deleted file mode 100644
index 67478395cb..0000000000
--- a/lib/mongo/protocol/delete.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2014-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Protocol
-
-    # MongoDB Wire protocol Delete message.
-    #
-    # This is a client request message that is sent to the server in order
-    # to delete selected documents in the specified namespace.
-    #
-    # The operation, by default, operates on many documents. Setting
-    # the +:single_remove+ flag allows for a single matching document
-    # to be removed.
-    #
-    # @api semipublic
-    class Delete < Message
-
-      # Creates a new Delete message
-      #
-      # @example Remove all users named Tyler.
-      #   Query.new('xgen', 'users', {:name => 'Tyler'})
-      #
-      # @param database [String, Symbol] The database to remove from.
-      # @param collection [String, Symbol] The collection to remove from.
-      # @param selector [Hash] The query used to select doc(s) to remove.
-      # @param options [Hash] The additional delete options.
-      #
-      # @option options :flags [Array] The flags for the delete message.
-      #
-      #   Supported flags: +:single_remove+
-      def initialize(database, collection, selector, options = {})
-        @database = database
-        @namespace = "#{database}.#{collection}"
-        @selector = selector
-        @flags = options[:flags] || []
-        @upconverter = Upconverter.new(collection, selector, options)
-        super
-      end
-
-      # Return the event payload for monitoring.
-      #
-      # @example Return the event payload.
-      #   message.payload
-      #
-      # @return [ BSON::Document ] The event payload.
-      #
-      # @since 2.1.0
-      def payload
-        BSON::Document.new(
-          command_name: 'delete',
-          database_name: @database,
-          command: upconverter.command,
-          request_id: request_id
-        )
-      end
-
-      protected
-
-      attr_reader :upconverter
-
-      private
-
-      # The operation code required to specify a Delete message.
-      # @return [Fixnum] the operation code.
-      #
-      # @since 2.5.0
-      OP_CODE = 2006
-
-      # Available flags for a Delete message.
-      FLAGS = [:single_remove]
-
-      # Field representing Zero encoded as an Int32.
-      field :zero, Zero
-
-      # @!attribute
-      # @return [String] The namespace for this Delete message.
-      field :namespace, CString
-
-      # @!attribute
-      # @return [Array<Symbol>] The flags for this Delete message.
-      field :flags, BitVector.new(FLAGS)
-
-      # @!attribute
-      # @return [Hash] The selector for this Delete message.
-      field :selector, Document
-
-      # Converts legacy delete messages to the appropriare OP_COMMAND style
-      # message.
-      #
-      # @since 2.1.0
-      class Upconverter
-
-        # The delete command constant.
-        #
-        # @since 2.2.0
-        DELETE = 'delete'.freeze
-
-        # The deletes command constant.
-        #
-        # @since 2.2.0
-        DELETES = 'deletes'.freeze
-
-        # @return [ String ] collection The name of the collection.
-        attr_reader :collection
-
-        # @return [ BSON::Document, Hash ] filter The query filter or command.
-        attr_reader :filter
-
-        # @return [ Hash ] options The options.
-        attr_reader :options
-
-        # Instantiate the upconverter.
-        #
-        # @example Instantiate the upconverter.
-        #   Upconverter.new('users', { name: 'test' })
-        #
-        # @param [ String ] collection The name of the collection.
-        # @param [ BSON::Document, Hash ] filter The filter or command.
-        #
-        # @since 2.1.0
-        def initialize(collection, filter, options)
-          @collection = collection
-          @filter = filter
-          @options = options
-        end
-
-        # Get the upconverted command.
-        #
-        # @example Get the command.
-        #   upconverter.command
-        #
-        # @return [ BSON::Document ] The upconverted command.
-        #
-        # @since 2.1.0
-        def command
-          document = BSON::Document.new
-          document.store(DELETE, collection)
-          document.store(DELETES, [ BSON::Document.new(Message::Q => filter, Message::LIMIT => limit) ])
-          document.store(Message::ORDERED, true)
-          document
-        end
-
-        private
-
-        def limit
-          if options.key?(:flags)
-            options[:flags].include?(:single_remove) ? 1 : 0
-          else
-            0
-          end
-        end
-      end
-
-      Registry.register(OP_CODE, self)
-    end
-  end
-end
diff --git a/lib/mongo/protocol/insert.rb b/lib/mongo/protocol/insert.rb
deleted file mode 100644
index 6b02daf84b..0000000000
--- a/lib/mongo/protocol/insert.rb
+++ /dev/null
@@ -1,181 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2014-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Protocol
-
-    # MongoDB Wire protocol Insert message.
-    #
-    # This is a client request message that is sent to the server in order
-    # to insert documents within a namespace.
-    #
-    # The operation only has one flag +:continue_on_error+ which the user
-    # can use to instruct the database server to continue processing a bulk
-    # insertion if one happens to fail (e.g. due to duplicate IDs). This makes
-    # builk insert behave similarly to a seires of single inserts, except
-    # lastError will be set if any insert fails, not just the last one.
-    #
-    # If multiple errors occur, only the most recent will be reported by the
-    # getLastError mechanism.
-    #
-    # @api semipublic
-    class Insert < Message
-
-      # Creates a new Insert message
-      #
-      # @example Insert a user document
-      #   Insert.new('xgen', 'users', [{:name => 'Tyler'}])
-      #
-      # @example Insert serveral user documents and continue on errors
-      #   Insert.new('xgen', 'users', users, :flags => [:continue_on_error])
-      #
-      # @param database [String, Symbol]  The database to insert into.
-      # @param collection [String, Symbol] The collection to insert into.
-      # @param documents [Array<Hash>] The documents to insert.
-      # @param options [Hash] Additional options for the insertion.
-      #
-      # @option options :flags [Array] The flags for the insertion message.
-      #   Supported flags: +:continue_on_error+
-      # @option options [ true, false ] validating_keys Whether keys should be
-      #   validated for being valid document keys (i.e. not begin with $ and
-      #   not contain dots).
-      def initialize(database, collection, documents, options = {})
-        @database = database
-        @namespace = "#{database}.#{collection}"
-        @documents = documents
-        @flags = options[:flags] || []
-        @upconverter = Upconverter.new(collection, documents, options)
-        @options = options
-        super
-      end
-
-      # Return the event payload for monitoring.
-      #
-      # @example Return the event payload.
-      #   message.payload
-      #
-      # @return [ BSON::Document ] The event payload.
-      #
-      # @since 2.1.0
-      def payload
-        BSON::Document.new(
-          command_name: 'insert',
-          database_name: @database,
-          command: upconverter.command,
-          request_id: request_id
-        )
-      end
-
-      protected
-
-      attr_reader :upconverter
-
-      private
-
-      def validating_keys?
-        @options.fetch(:validating_keys, true)
-      end
-
-      # The operation code required to specify an Insert message.
-      # @return [Fixnum] the operation code.
-      #
-      # @since 2.5.0
-      OP_CODE = 2002
-
-      # Available flags for an Insert message.
-      FLAGS = [:continue_on_error]
-
-      # @!attribute
-      # @return [Array<Symbol>] The flags for this Insert message.
-      field :flags, BitVector.new(FLAGS)
-
-      # @!attribute
-      # @return [String] The namespace for this Insert message.
-      field :namespace, CString
-
-      # @!attribute
-      # @return [Array<Hash>] The documents to insert.
-      field :documents, Document, true
-
-      # Converts legacy insert messages to the appropriare OP_COMMAND style
-      # message.
-      #
-      # @since 2.1.0
-      class Upconverter
-
-        # Insert field constant.
-        #
-        # @since 2.1.0
-        INSERT = 'insert'.freeze
-
-        # Documents field constant.
-        #
-        # @since 2.1.0
-        DOCUMENTS = 'documents'.freeze
-
-        # Write concern field constant.
-        #
-        # @since 2.1.0
-        WRITE_CONCERN = 'writeConcern'.freeze
-
-        # @return [ String ] collection The name of the collection.
-        attr_reader :collection
-
-        # @return [ Array<BSON::Document> ] documents The documents to insert.
-        attr_reader :documents
-
-        # @return [ Hash ] options The options.
-        attr_reader :options
-
-        # Instantiate the upconverter.
-        #
-        # @example Instantiate the upconverter.
-        #   Upconverter.new('users', documents)
-        #
-        # @param [ String ] collection The name of the collection.
-        # @param [ Array<BSON::Document> ] documents The documents.
-        # @param [ Hash ] options The options.
-        #
-        # @since 2.1.0
-        def initialize(collection, documents, options)
-          @collection = collection
-          @documents = documents
-          @options = options
-        end
-
-        # Get the upconverted command.
-        #
-        # @example Get the command.
-        #   upconverter.command
-        #
-        # @return [ BSON::Document ] The upconverted command.
-        #
-        # @since 2.1.0
-        def command
-          document = BSON::Document.new
-          document.store(INSERT, collection)
-          document.store(DOCUMENTS, documents)
-          document.store(Message::ORDERED, options.fetch(:ordered, true))
-          document.merge!(WRITE_CONCERN => options[:write_concern].options) if options[:write_concern]
-          document
-        end
-      end
-
-      Registry.register(OP_CODE, self)
-    end
-  end
-end
diff --git a/lib/mongo/protocol/message.rb b/lib/mongo/protocol/message.rb
index ed5674e0d9..438f740454 100644
--- a/lib/mongo/protocol/message.rb
+++ b/lib/mongo/protocol/message.rb
@@ -354,16 +354,16 @@ def serialize_fields(buffer, max_bson_size = nil)
           if field[:multi]
             value.each do |item|
               if field[:type].respond_to?(:size_limited?)
-                field[:type].serialize(buffer, item, max_bson_size, validating_keys?)
+                field[:type].serialize(buffer, item, max_bson_size)
               else
-                field[:type].serialize(buffer, item, validating_keys?)
+                field[:type].serialize(buffer, item)
               end
             end
           else
             if field[:type].respond_to?(:size_limited?)
-              field[:type].serialize(buffer, value, max_bson_size, validating_keys?)
+              field[:type].serialize(buffer, value, max_bson_size)
             else
-              field[:type].serialize(buffer, value, validating_keys?)
+              field[:type].serialize(buffer, value)
             end
           end
         end
@@ -456,10 +456,6 @@ def self.deserialize_field(message, io, field, options = {})
           field[:type].deserialize(io, options)
         )
       end
-
-      def validating_keys?
-        @options[:validating_keys] if @options
-      end
     end
   end
 end
diff --git a/lib/mongo/protocol/msg.rb b/lib/mongo/protocol/msg.rb
index 876884b500..194de8ea6f 100644
--- a/lib/mongo/protocol/msg.rb
+++ b/lib/mongo/protocol/msg.rb
@@ -58,6 +58,7 @@ class Msg < Message
       # @option options [ true, false ] validating_keys Whether keys should be
       #   validated for being valid document keys (i.e. not begin with $ and
       #   not contain dots).
+      #   This option is deprecated and will not be used. It will removed in version 3.0.
       #
       # @api private
       #
diff --git a/lib/mongo/protocol/serializers.rb b/lib/mongo/protocol/serializers.rb
index e021b9a0c7..cc1c40bd1b 100644
--- a/lib/mongo/protocol/serializers.rb
+++ b/lib/mongo/protocol/serializers.rb
@@ -51,9 +51,11 @@ module Header
         #
         # @param buffer [ String ] Buffer to receive the serialized value.
         # @param value [ String ] Header value to be serialized.
+        # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ String ] Buffer with serialized value.
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           buffer.put_bytes(value.pack(HEADER_PACK))
         end
 
@@ -80,7 +82,7 @@ module CString
         # @param value [ String ] The string to be serialized.
         #
         # @return [ String ] Buffer with serialized value.
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           buffer.put_cstring(value)
         end
       end
@@ -96,7 +98,7 @@ module Zero
         # @param value [ Fixnum ] Ignored value.
         #
         # @return [ String ] Buffer with serialized value.
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           buffer.put_int32(ZERO)
         end
       end
@@ -112,7 +114,7 @@ module Int32
         # @param value [ Integer | BSON::Int32 ] 32-bit integer to be serialized.
         #
         # @return [String] Buffer with serialized value.
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           if value.is_a?(BSON::Int32)
             if value.respond_to?(:value)
               # bson-ruby >= 4.6.0
@@ -146,7 +148,7 @@ module Int64
         # @param value [ Integer | BSON::Int64 ] 64-bit integer to be serialized.
         #
         # @return [ String ] Buffer with serialized value.
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           if value.is_a?(BSON::Int64)
             if value.respond_to?(:value)
               # bson-ruby >= 4.6.0
@@ -182,19 +184,20 @@ module Sections
         # @param [ Array<Hash, BSON::Document> ] value The sections to be serialized.
         # @param [ Fixnum ] max_bson_size The max bson size of documents in the sections.
         # @param [ true, false ] validating_keys Whether to validate document keys.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ BSON::ByteBuffer ] Buffer with serialized value.
         #
         # @since 2.5.0
-        def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
           value.each do |section|
             case section[:type]
             when PayloadZero::TYPE
-              PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
+              PayloadZero.serialize(buffer, section[:payload], max_bson_size)
             when nil
-              PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
+              PayloadZero.serialize(buffer, section[:payload], max_bson_size)
             when PayloadOne::TYPE
-              PayloadOne.serialize(buffer, section[:payload], max_bson_size, validating_keys)
+              PayloadOne.serialize(buffer, section[:payload], max_bson_size)
             else
               raise Error::UnknownPayloadType.new(section[:type])
             end
@@ -259,13 +262,14 @@ module PayloadZero
           # @param [ BSON::Document, Hash ] value The object to serialize.
           # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
           # @param [ true, false ] validating_keys Whether to validate document keys.
+          #   This option is deprecated and will not be used. It will removed in version 3.0.
           #
           # @return [ BSON::ByteBuffer ] Buffer with serialized value.
           #
           # @since 2.5.0
-          def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
+          def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
             buffer.put_byte(TYPE_BYTE)
-            Serializers::Document.serialize(buffer, value, max_bson_size, validating_keys)
+            Serializers::Document.serialize(buffer, value, max_bson_size)
           end
 
           # Deserializes a section of payload type 0 of an OP_MSG from the IO stream.
@@ -307,17 +311,18 @@ module PayloadOne
           # @param [ BSON::Document, Hash ] value The object to serialize.
           # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
           # @param [ true, false ] validating_keys Whether to validate document keys.
+          #   This option is deprecated and will not be used. It will removed in version 3.0.
           #
           # @return [ BSON::ByteBuffer ] Buffer with serialized value.
           #
           # @since 2.5.0
-          def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
+          def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
             buffer.put_byte(TYPE_BYTE)
             start = buffer.length
             buffer.put_int32(0) # hold for size
             buffer.put_cstring(value[:identifier])
             value[:sequence].each do |document|
-              Document.serialize(buffer, document, max_bson_size, validating_keys)
+              Document.serialize(buffer, document, max_bson_size)
             end
             buffer.replace_int32(start, buffer.length - start)
           end
@@ -356,9 +361,9 @@ module Document
         # @param value [ Hash ] Document to serialize as BSON.
         #
         # @return [ String ] Buffer with serialized value.
-        def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
           start_size = buffer.length
-          value.to_bson(buffer, validating_keys)
+          value.to_bson(buffer)
           serialized_size = buffer.length - start_size
           if max_bson_size && serialized_size > max_bson_size
             raise Error::MaxBSONSize,
@@ -401,11 +406,12 @@ module Byte
         # @param [ BSON::ByteBuffer ] buffer Buffer to receive the single byte.
         # @param [ String ] value The byte to write to the buffer.
         # @param [ true, false ] validating_keys Whether to validate keys.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ BSON::ByteBuffer ] Buffer with serialized value.
         #
         # @since 2.5.0
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           buffer.put_byte(value)
         end
 
@@ -432,11 +438,12 @@ module Bytes
         # @param [ BSON::ByteBuffer ] buffer Buffer to receive the bytes.
         # @param [ String ] value The bytes to write to the buffer.
         # @param [ true, false ] validating_keys Whether to validate keys.
+        #   This option is deprecated and will not be used. It will removed in version 3.0.
         #
         # @return [ BSON::ByteBuffer ] Buffer with serialized value.
         #
         # @since 2.5.0
-        def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
+        def self.serialize(buffer, value, validating_keys = nil)
           buffer.put_bytes(value)
         end
 
diff --git a/lib/mongo/protocol/update.rb b/lib/mongo/protocol/update.rb
deleted file mode 100644
index f74c0208af..0000000000
--- a/lib/mongo/protocol/update.rb
+++ /dev/null
@@ -1,214 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-# Copyright (C) 2014-2020 MongoDB Inc.
-#
-# 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.
-
-module Mongo
-  module Protocol
-
-    # MongoDB Wire protocol Update message.
-    #
-    # This is a client request message that is sent to the server in order
-    # to update documents matching the provided query.
-    #
-    # The default is to update a single document. In order to update many at
-    # a time users should set the +:multi_update+ flag for the update.
-    #
-    # If an upsert (update or insert) is desired, users can set the +:upsert+
-    # flag in order to indicate they would like to insert the merged selector
-    # and update if no document matching the update query currently exists.
-    #
-    # @api semipublic
-    class Update < Message
-
-      # Creates a new Update message
-      #
-      # @example Update single document
-      #   Update.new('xgen', 'users', {:name => 'Tyler'}, {:name => 'Bob'})
-      #
-      # @example Perform a multi update
-      #   Update.new('xgen', 'users',
-      #     {:age => 20}, {:age => 21}, :flags => [:multi_update])
-      #
-      # @example Perform an upsert
-      #   Update.new('xgen', 'users', {:name => 'Tyler'}, :flags => [:upsert])
-      #
-      # @param database [String, Symbol]  The database to update.
-      # @param collection [String, Symbol] The collection to update.
-      # @param selector [Hash] The update selector.
-      # @param update [Hash] The update to perform.
-      # @param options [Hash] The additional query options.
-      #
-      # @option options :flags [Array] The flags for the update message.
-      #
-      #   Supported flags: +:upsert+, +:multi_update+
-      def initialize(database, collection, selector, update, options = {})
-        @database    = database
-        @collection  = collection
-        @namespace   = "#{database}.#{collection}"
-        @selector    = selector
-        @update      = update
-        @flags       = options[:flags] || []
-        @upconverter = Upconverter.new(collection, selector, update, flags)
-        super
-      end
-
-      # Return the event payload for monitoring.
-      #
-      # @example Return the event payload.
-      #   message.payload
-      #
-      # @return [ BSON::Document ] The event payload.
-      #
-      # @since 2.1.0
-      def payload
-        BSON::Document.new(
-          command_name: 'update',
-          database_name: @database,
-          command: upconverter.command,
-          request_id: request_id
-        )
-      end
-
-      protected
-
-      attr_reader :upconverter
-
-      private
-
-      # The operation code required to specify an Update message.
-      # @return [Fixnum] the operation code.
-      #
-      # @since 2.5.0
-      OP_CODE = 2001
-
-      # Available flags for an Update message.
-      FLAGS = [:upsert, :multi_update]
-
-      # Field representing Zero encoded as an Int32.
-      field :zero, Zero
-
-      # @!attribute
-      # @return [String] The namespace for this Update message.
-      field :namespace, CString
-
-      # @!attribute
-      # @return [Array<Symbol>] The flags for this Update message.
-      field :flags, BitVector.new(FLAGS)
-
-      # @!attribute
-      # @return [Hash] The selector for this Update message.
-      field :selector, Document
-
-      # @!attribute
-      # @return [Hash] The update for this Delete message.
-      field :update, Document
-
-      # Converts legacy update messages to the appropriare OP_COMMAND style
-      # message.
-      #
-      # @since 2.1.0
-      class Upconverter
-
-        # The multi constant.
-        #
-        # @since 2.2.0
-        MULTI = 'multi'.freeze
-
-        # The u constant.
-        #
-        # @since 2.2.0
-        U = 'u'.freeze
-
-        # The update constant.
-        #
-        # @since 2.2.0
-        UPDATE = 'update'.freeze
-
-        # The updates constant.
-        #
-        # @since 2.2.0
-        UPDATES = 'updates'.freeze
-
-        # The upsert constant.
-        #
-        # @since 2.2.0
-        UPSERT = 'upsert'.freeze
-
-        # @return [ String ] collection The name of the collection.
-        attr_reader :collection
-
-        # @return [ Hash ] filter The filter.
-        attr_reader :filter
-
-        # @return [ Hash ] update The update.
-        attr_reader :update
-
-        # @return [ Array<Symbol> ] flags The flags.
-        attr_reader :flags
-
-        # Instantiate the upconverter.
-        #
-        # @example Instantiate the upconverter.
-        #   Upconverter.new(
-        #     'users',
-        #     { name: 'test' },
-        #     { '$set' => { 'name' => 't' }},
-        #     []
-        #   )
-        #
-        # @param [ String ] collection The name of the collection.
-        # @param [ Hash ] filter The filter.
-        # @param [ Hash ] update The update.
-        # @param [ Array<Symbol> ] flags The flags.
-        #
-        # @since 2.1.0
-        def initialize(collection, filter, update, flags)
-          @collection = collection
-          @filter = filter
-          @update = update
-          @flags = flags
-        end
-
-        # Get the upconverted command.
-        #
-        # @example Get the command.
-        #   upconverter.command
-        #
-        # @return [ BSON::Document ] The upconverted command.
-        #
-        # @since 2.1.0
-        def command
-          document = BSON::Document.new
-          updates = BSON::Document.new
-          updates.store(Message::Q, filter)
-          updates.store(U, update)
-          if flags.include?(:multi_update)
-            updates.store(MULTI, true)
-          end
-          if flags.include?(:upsert)
-            updates.store(UPSERT, true)
-          end
-          document.store(UPDATE, collection)
-          document.store(Message::ORDERED, true)
-          document.store(UPDATES, [ updates ])
-          document
-        end
-      end
-
-      Registry.register(OP_CODE, self)
-    end
-  end
-end
diff --git a/lib/mongo/query_cache.rb b/lib/mongo/query_cache.rb
index e50f12fe6c..07eea1055d 100644
--- a/lib/mongo/query_cache.rb
+++ b/lib/mongo/query_cache.rb
@@ -114,24 +114,24 @@ def clear_namespace(namespace)
       #
       # @param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.
       #
-      # @option opts [ String | nil ] namespace The namespace of the query,
+      # @option opts [ String | nil ] :namespace The namespace of the query,
       #   in the format "database_name.collection_name".
-      # @option opts [ Array, Hash ] selector The selector passed to the query.
+      # @option opts [ Array, Hash ] :selector The selector passed to the query.
       #   For most queries, this will be a Hash, but for aggregations, this
       #   will be an Array representing the aggregation pipeline. May not be nil.
-      # @option opts [ Integer | nil ] skip The skip value of the query.
-      # @option opts [ Hash | nil ] sort The order of the query results
+      # @option opts [ Integer | nil ] :skip The skip value of the query.
+      # @option opts [ Hash | nil ] :sort The order of the query results
       #   (e.g. { name: -1 }).
-      # @option opts [ Integer | nil ] limit The limit value of the query.
-      # @option opts [ Hash | nil ] projection The projection of the query
+      # @option opts [ Integer | nil ] :limit The limit value of the query.
+      # @option opts [ Hash | nil ] :projection The projection of the query
       #   results (e.g. { name: 1 }).
-      # @option opts [ Hash | nil ] collation The collation of the query
+      # @option opts [ Hash | nil ] :collation The collation of the query
       #   (e.g. { "locale" => "fr_CA" }).
-      # @option opts [ Hash | nil ] read_concern The read concern of the query
+      # @option opts [ Hash | nil ] :read_concern The read concern of the query
       #   (e.g. { level: :majority }).
-      # @option opts [ Hash | nil ] read_preference The read preference of
+      # @option opts [ Hash | nil ] :read_preference The read preference of
       #   the query (e.g. { mode: :secondary }).
-      # @option opts [ Boolean | nil ] multi_collection Whether the query
+      # @option opts [ Boolean | nil ] :multi_collection Whether the query
       #   results could potentially come from multiple collections. When true,
       #   these results will be stored under the nil namespace key and cleared
       #   on every write command.
@@ -152,24 +152,24 @@ def set(cursor, **opts)
       # For the given query options, retrieve a cached cursor that can be used
       # to obtain the correct query results, if one exists in the cache.
       #
-      # @option opts [ String | nil ] namespace The namespace of the query,
+      # @option opts [ String | nil ] :namespace The namespace of the query,
       #   in the format "database_name.collection_name".
-      # @option opts [ Array, Hash ] selector The selector passed to the query.
+      # @option opts [ Array, Hash ] :selector The selector passed to the query.
       #   For most queries, this will be a Hash, but for aggregations, this
       #   will be an Array representing the aggregation pipeline. May not be nil.
-      # @option opts [ Integer | nil ] skip The skip value of the query.
-      # @option opts [ Hash | nil ] sort The order of the query results
+      # @option opts [ Integer | nil ] :skip The skip value of the query.
+      # @option opts [ Hash | nil ] :sort The order of the query results
       #   (e.g. { name: -1 }).
-      # @option opts [ Integer | nil ] limit The limit value of the query.
-      # @option opts [ Hash | nil ] projection The projection of the query
+      # @option opts [ Integer | nil ] :limit The limit value of the query.
+      # @option opts [ Hash | nil ] :projection The projection of the query
       #   results (e.g. { name: 1 }).
-      # @option opts [ Hash | nil ] collation The collation of the query
+      # @option opts [ Hash | nil ] :collation The collation of the query
       #   (e.g. { "locale" => "fr_CA" }).
-      # @option opts [ Hash | nil ] read_concern The read concern of the query
+      # @option opts [ Hash | nil ] :read_concern The read concern of the query
       #   (e.g. { level: :majority }).
-      # @option opts [ Hash | nil ] read_preference The read preference of
+      # @option opts [ Hash | nil ] :read_preference The read preference of
       #   the query (e.g. { mode: :secondary }).
-      # @option opts [ Boolean | nil ] multi_collection Whether the query
+      # @option opts [ Boolean | nil ] :multi_collection Whether the query
       #   results could potentially come from multiple collections. When true,
       #   these results will be stored under the nil namespace key and cleared
       #   on every write command.
diff --git a/lib/mongo/server/app_metadata.rb b/lib/mongo/server/app_metadata.rb
index 8d81dd1f2d..7d4bf00961 100644
--- a/lib/mongo/server/app_metadata.rb
+++ b/lib/mongo/server/app_metadata.rb
@@ -15,6 +15,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+require 'mongo/server/app_metadata/environment'
+require 'mongo/server/app_metadata/truncator'
+
 module Mongo
   class Server
     # Application metadata that is sent to the server during a handshake,
@@ -26,11 +29,6 @@ class Server
     class AppMetadata
       extend Forwardable
 
-      # The max application metadata document byte size.
-      #
-      # @since 2.4.0
-      MAX_DOCUMENT_SIZE = 512.freeze
-
       # The max application name byte size.
       #
       # @since 2.4.0
@@ -139,6 +137,23 @@ def validated_document
         document
       end
 
+      # Get BSON::Document to be used as value for `client` key in
+      # handshake document.
+      #
+      # @return [BSON::Document] Document describing client for handshake.
+      #
+      # @api private
+      def client_document
+        @client_document ||=
+          BSON::Document.new.tap do |doc|
+            doc[:application] = { name: @app_name } if @app_name
+            doc[:driver] = driver_doc
+            doc[:os] = os_doc
+            doc[:platform] = platform
+            env_doc.tap { |env| doc[:env] = env if env }
+          end
+      end
+
       private
 
       # Check whether it is possible to build a valid app metadata document
@@ -152,20 +167,6 @@ def validate!
         true
       end
 
-      # Get BSON::Document to be used as value for `client` key in
-      # handshake document.
-      #
-      # @return [BSON::Document] Document describing client for handshake.
-      def full_client_document
-        BSON::Document.new.tap do |doc|
-          doc[:application] = { name: @app_name } if @app_name
-          doc[:driver] = driver_doc
-          doc[:os] = os_doc
-          doc[:platform] = platform
-        end
-      end
-
-
       # Get the metadata as BSON::Document to be sent to
       # as part of the handshake. The document should
       # be appended to a suitable handshake command.
@@ -173,30 +174,11 @@ def full_client_document
       # @return [BSON::Document] Document for connection's handshake.
       def document
         @document ||= begin
-          client_document = full_client_document
-          while client_document.to_bson.to_s.size > MAX_DOCUMENT_SIZE do
-            if client_document[:os][:name] || client_document[:os][:architecture]
-              client_document[:os].delete(:name)
-              client_document[:os].delete(:architecture)
-            elsif client_document[:platform]
-              client_document.delete(:platform)
-            else
-              client_document = nil
-            end
+          client = Truncator.new(client_document).document
+          BSON::Document.new(compression: @compressors, client: client).tap do |doc|
+            doc[:saslSupportedMechs] = @request_auth_mech if @request_auth_mech
+            doc.update(Utils.transform_server_api(@server_api)) if @server_api
           end
-          document = BSON::Document.new(
-            {
-              compression: @compressors,
-              client: client_document,
-            }
-          )
-          document[:saslSupportedMechs] = @request_auth_mech if @request_auth_mech
-          if @server_api
-            document.update(
-              Utils.transform_server_api(@server_api)
-            )
-          end
-          document
         end
       end
 
@@ -223,6 +205,11 @@ def os_doc
         }
       end
 
+      def env_doc
+        env = Environment.new
+        env.faas? ? env.to_h : nil
+      end
+
       def type
         (RbConfig::CONFIG && RbConfig::CONFIG['host_os']) ?
           RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase : 'unknown'
diff --git a/lib/mongo/server/app_metadata/environment.rb b/lib/mongo/server/app_metadata/environment.rb
new file mode 100644
index 0000000000..5e9c556f7b
--- /dev/null
+++ b/lib/mongo/server/app_metadata/environment.rb
@@ -0,0 +1,255 @@
+# frozen_string_literal: true
+
+# Copyright (C) 2016-2023 MongoDB Inc.
+#
+# 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.
+
+module Mongo
+  class Server
+    class AppMetadata
+      # Implements the logic from the handshake spec, for deducing and
+      # reporting the current FaaS environment in which the program is
+      # executing.
+      #
+      # @api private
+      class Environment
+        # Error class for reporting that too many discriminators were found
+        # in the environment. (E.g. if the environment reports that it is
+        # running under both AWS and Azure.)
+        class TooManyEnvironments < Mongo::Error; end
+
+        # Error class for reporting that a required environment variable is
+        # missing.
+        class MissingVariable < Mongo::Error; end
+
+        # Error class for reporting that the wrong type was given for a
+        # field.
+        class TypeMismatch < Mongo::Error; end
+
+        # Error class for reporting that the value for a field is too long.
+        class ValueTooLong < Mongo::Error; end
+
+        # This value is not explicitly specified in the spec, only implied to be
+        # less than 512.
+        MAXIMUM_VALUE_LENGTH = 500
+
+        # The mapping that determines which FaaS environment is active, based
+        # on which environment variable(s) are present.
+        DISCRIMINATORS = {
+          'AWS_EXECUTION_ENV' => { pattern: /^AWS_Lambda_/, name: 'aws.lambda' },
+          'AWS_LAMBDA_RUNTIME_API' => { name: 'aws.lambda' },
+          'FUNCTIONS_WORKER_RUNTIME' => { name: 'azure.func' },
+          'K_SERVICE' => { name: 'gcp.func' },
+          'FUNCTION_NAME' => { name: 'gcp.func' },
+          'VERCEL' => { name: 'vercel' },
+        }.freeze
+
+        # Describes how to coerce values of the specified type.
+        COERCIONS = {
+          string: ->(v) { String(v) },
+          integer: ->(v) { Integer(v) }
+        }.freeze
+
+        # Describes which fields are required for each FaaS environment,
+        # along with their expected types, and how they should be named in
+        # the handshake document.
+        FIELDS = {
+          'aws.lambda' => {
+            'AWS_REGION' => { field: :region, type: :string },
+            'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => { field: :memory_mb, type: :integer },
+          },
+
+          'azure.func' => {},
+
+          'gcp.func' => {
+            'FUNCTION_MEMORY_MB' => { field: :memory_mb, type: :integer },
+            'FUNCTION_TIMEOUT_SEC' => { field: :timeout_sec, type: :integer },
+            'FUNCTION_REGION' => { field: :region, type: :string },
+          },
+
+          'vercel' => {
+            'VERCEL_URL' => { field: :url, type: :string },
+            'VERCEL_REGION' => { field: :region, type: :string },
+          },
+        }.freeze
+
+        # @return [ String | nil ] the name of the FaaS environment that was
+        #   detected, or nil if no valid FaaS environment was detected.
+        attr_reader :name
+
+        # @return [ Hash | nil ] the fields describing the detected FaaS
+        #   environment.
+        attr_reader :fields
+
+        # @return [ String | nil ] the error message explaining why a valid
+        #   FaaS environment was not detected, or nil if no error occurred.
+        #
+        # @note These error messagess are not to be propogated to the
+        #   user; they are intended only for troubleshooting and debugging.)
+        attr_reader :error
+
+        # Create a new AppMetadata::Environment object, initializing it from
+        # the current ENV variables. If no FaaS environment is detected, or
+        # if the environment contains invalid or contradictory state, it will
+        # be initialized with {{name}} set to {{nil}}.
+        def initialize
+          @error = nil
+          @name = detect_environment
+          populate_fields
+        rescue TooManyEnvironments => e
+          self.error = "too many environments detected: #{e.message}"
+        rescue MissingVariable => e
+          self.error = "missing environment variable: #{e.message}"
+        rescue TypeMismatch => e
+          self.error = e.message
+        rescue ValueTooLong => e
+          self.error = "value for #{e.message} is too long"
+        end
+
+        # Queries whether the current environment is a valid FaaS environment.
+        #
+        # @return [ true | false ] whether the environment is a FaaS
+        #   environment or not.
+        def faas?
+          @name != nil
+        end
+
+        # Queries whether the current environment is a valid AWS Lambda
+        # environment.
+        #
+        # @return [ true | false ] whether the environment is a AWS Lambda
+        #   environment or not.
+        def aws?
+          @name == 'aws.lambda'
+        end
+
+        # Queries whether the current environment is a valid Azure
+        # environment.
+        #
+        # @return [ true | false ] whether the environment is a Azure
+        #   environment or not.
+        def azure?
+          @name == 'azure.func'
+        end
+
+        # Queries whether the current environment is a valid GCP
+        # environment.
+        #
+        # @return [ true | false ] whether the environment is a GCP
+        #   environment or not.
+        def gcp?
+          @name == 'gcp.func'
+        end
+
+        # Queries whether the current environment is a valid Vercel
+        # environment.
+        #
+        # @return [ true | false ] whether the environment is a Vercel
+        #   environment or not.
+        def vercel?
+          @name == 'vercel'
+        end
+
+        # Compiles the detected environment information into a Hash. It will
+        # always include a {{name}} key, but may include other keys as well,
+        # depending on the detected FaaS environment. (See the handshake
+        # spec for details.)
+        #
+        # @return [ Hash ] the detected environment information.
+        def to_h
+          fields.merge(name: name)
+        end
+
+        private
+
+        # Searches the DESCRIMINATORS list to see which (if any) apply to
+        # the current environment.
+        #
+        # @return [ String | nil ] the name of the detected FaaS provider.
+        #
+        # @raise [ TooManyEnvironments ] if the environment contains
+        #   discriminating variables for more than one FaaS provider.
+        def detect_environment
+          matches = DISCRIMINATORS.keys.select { |k| discriminator_matches?(k) }
+          names = matches.map { |m| DISCRIMINATORS[m][:name] }.uniq
+
+          raise TooManyEnvironments, names.join(', ') if names.length > 1
+
+          names.first
+        end
+
+        # Determines whether the named environment variable exists, and (if
+        # a pattern has been declared for that descriminator) whether the
+        # pattern matches the value of the variable.
+        #
+        # @param [ String ] var the name of the environment variable
+        #
+        # @return [ true | false ] if the variable describes the current
+        #   environment or not.
+        def discriminator_matches?(var)
+          return false unless ENV[var]
+
+          disc = DISCRIMINATORS[var]
+          return true unless disc[:pattern]
+
+          disc[:pattern].match?(ENV[var])
+        end
+
+        # Extracts environment information from the current environment
+        # variables, based on the detected FaaS environment. Populates the
+        # {{@fields}} instance variable.
+        def populate_fields
+          return unless name
+
+          @fields = FIELDS[name].each_with_object({}) do |(var, defn), fields|
+            fields[defn[:field]] = extract_field(var, defn)
+          end
+        end
+
+        # Extracts the named variable from the environment and validates it
+        # against its declared definition.
+        #
+        # @param [ String ] var The name of the environment variable to look
+        #   for.
+        # @param [ Hash ] definition The definition of the field that applies
+        #   to the named variable.
+        #
+        # @return [ Integer | String ] the validated and coerced value of the
+        #   given environment variable.
+        #
+        # @raise [ MissingVariable ] if the environment does not include a
+        #   variable required by the current FaaS provider.
+        # @raise [ ValueTooLong ] if a required variable is too long.
+        # @raise [ TypeMismatch ] if a required variable cannot be coerced to
+        #   the expected type.
+        def extract_field(var, definition)
+          raise MissingVariable, var unless ENV[var]
+          raise ValueTooLong, var if ENV[var].length > MAXIMUM_VALUE_LENGTH
+
+          COERCIONS[definition[:type]].call(ENV[var])
+        rescue ArgumentError
+          raise TypeMismatch,
+                "#{var} must be #{definition[:type]} (got #{ENV[var].inspect})"
+        end
+
+        # Sets the error message to the given value and sets the name to nil.
+        #
+        # @param [ String ] msg The error message to store.
+        def error=(msg)
+          @name = nil
+          @error = msg
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mongo/server/app_metadata/truncator.rb b/lib/mongo/server/app_metadata/truncator.rb
new file mode 100644
index 0000000000..125b8f1e2e
--- /dev/null
+++ b/lib/mongo/server/app_metadata/truncator.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+# Copyright (C) 2016-2023 MongoDB Inc.
+#
+# 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.
+
+module Mongo
+  class Server
+    class AppMetadata
+      # Implements the metadata truncation logic described in the handshake
+      # spec.
+      #
+      # @api private
+      class Truncator
+        # @return [ BSON::Document ] the document being truncated.
+        attr_reader :document
+
+        # The max application metadata document byte size.
+        MAX_DOCUMENT_SIZE = 512
+
+        # Creates a new Truncator instance and tries enforcing the maximum
+        # document size on the given document.
+        #
+        # @param [ BSON::Document] document The document to (potentially)
+        #   truncate.
+        #
+        # @note The document is modified in-place; if you wish to keep the
+        #   original unchanged, you must deep-clone it prior to sending it to
+        #   a truncator.
+        def initialize(document)
+          @document = document
+          try_truncate!
+        end
+
+        # The current size of the document, in bytes, as a serialized BSON
+        # document.
+        #
+        # @return [ Integer ] the size of the document
+        def size
+          @document.to_bson.to_s.length
+        end
+
+        # Whether the document fits within the required maximum document size.
+        #
+        # @return [ true | false ] if the document is okay or not.
+        def ok?
+          size <= MAX_DOCUMENT_SIZE
+        end
+
+        private
+
+        # How many extra bytes must be trimmed before the document may be
+        # considered #ok?.
+        #
+        # @return [ Integer ] how many bytes larger the document is than the
+        #   maximum document size.
+        def excess
+          size - MAX_DOCUMENT_SIZE
+        end
+
+        # Attempt to truncate the document using the documented metadata
+        # priorities (see the handshake specification).
+        def try_truncate!
+          %i[ env_fields os_fields env platform ].each do |target|
+            break if ok?
+
+            send(:"try_truncate_#{target}!")
+          end
+        end
+
+        # Attempt to truncate or remove the {{:platform}} key from the
+        # document.
+        def try_truncate_platform!
+          @document.delete(:platform) unless try_truncate_string(@document[:platform])
+        end
+
+        # Attempt to truncate the keys in the {{:env}} subdocument.
+        def try_truncate_env_fields!
+          try_truncate_hash(@document[:env], reserved: %w[ name ])
+        end
+
+        # Attempt to truncate the keys in the {{:os}} subdocument.
+        def try_truncate_os_fields!
+          try_truncate_hash(@document[:os], reserved: %w[ type ])
+        end
+
+        # Remove the {{:env}} key from the document.
+        def try_truncate_env!
+          @document.delete(:env)
+        end
+
+        # A helper method for truncating a string (in-place) by whatever
+        # {{#excess}} is required.
+        #
+        # @param [ String ] string the string value to truncate.
+        #
+        # @note the parameter is modified in-place.
+        def try_truncate_string(string)
+          length = string&.length || 0
+
+          return false if excess > length
+
+          string[(length - excess)..-1] = ''
+        end
+
+        # A helper method for removing the keys of a Hash (in-place) until
+        # the document is the necessary size. The keys are considered in order
+        # (using the Hash's native key ordering), and each will be removed from
+        # the hash in turn, until the document is the necessary size.
+        #
+        # Any keys in the {{reserved}} list will be ignored.
+        #
+        # @param [ Hash | nil ] hash the Hash instance to consider.
+        # @param [ Array ] reserved the list of keys to ignore in the hash.
+        #
+        # @note the hash parameter is modified in-place.
+        def try_truncate_hash(hash, reserved: [])
+          return false unless hash
+
+          keys = hash.keys - reserved
+          keys.each do |key|
+            hash.delete(key)
+
+            return true if ok?
+          end
+
+          false
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mongo/session.rb b/lib/mongo/session.rb
index 6c58c27547..8b6d31ec35 100644
--- a/lib/mongo/session.rb
+++ b/lib/mongo/session.rb
@@ -522,7 +522,7 @@ def with_transaction(options=nil)
     #
     # @option options [ Integer ] :max_commit_time_ms The maximum amount of
     #   time to allow a single commitTransaction command to run, in milliseconds.
-    # @option options [ Hash ] read_concern The read concern options hash,
+    # @option options [ Hash ] :read_concern The read concern options hash,
     #   with the following optional keys:
     #   - *:level* -- the read preference level as a symbol; valid values
     #      are *:local*, *:majority*, and *:snapshot*
diff --git a/lib/mongo/version.rb b/lib/mongo/version.rb
index c32b2a09b3..4226eb5c5b 100644
--- a/lib/mongo/version.rb
+++ b/lib/mongo/version.rb
@@ -20,5 +20,5 @@ module Mongo
   # The current version of the driver.
   #
   # @since 2.0.0
-  VERSION = '2.18.1'.freeze
+  VERSION = '2.18.3'.freeze
 end
diff --git a/spec/integration/command_spec.rb b/spec/integration/command_spec.rb
index 9b3237df72..8f78137823 100644
--- a/spec/integration/command_spec.rb
+++ b/spec/integration/command_spec.rb
@@ -12,7 +12,7 @@
 
     let(:payload) do
       server.with_connection do |connection|
-        command.send(:final_operation, connection).send(:message, connection).payload.dup.tap do |payload|
+        command.send(:final_operation).send(:message, connection).payload.dup.tap do |payload|
           if payload['request_id'].is_a?(Integer)
             payload['request_id'] = 42
           end
@@ -150,28 +150,6 @@
           expect(payload).to eq(expected_payload)
         end
       end
-
-      # Servers using legacy wire protocol message do not have $db in payload.
-      # $db is added to the payload later when the command monitoring event is
-      # published.
-      context 'pre-OP_MSG servers' do
-        max_server_version '3.4'
-
-        let(:expected_payload) do
-          {
-            'command' => {
-              'find' => 'collection_name',
-            },
-            'command_name' => 'find',
-            'database_name' => 'foo',
-            'request_id' => 42,
-          }
-        end
-
-        it 'returns expected payload' do
-          expect(payload).to eq(expected_payload)
-        end
-      end
     end
 
   end
diff --git a/spec/integration/connection/faas_env_spec.rb b/spec/integration/connection/faas_env_spec.rb
new file mode 100644
index 0000000000..08f2b55867
--- /dev/null
+++ b/spec/integration/connection/faas_env_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# Test Plan scenarios from the handshake spec
+SCENARIOS = {
+  'Valid AWS' => {
+    'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+    'AWS_REGION' => 'us-east-2',
+    'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024',
+  },
+
+  'Valid Azure' => {
+    'FUNCTIONS_WORKER_RUNTIME' => 'ruby',
+  },
+
+  'Valid GCP' => {
+    'K_SERVICE' => 'servicename',
+    'FUNCTION_MEMORY_MB' => '1024',
+    'FUNCTION_TIMEOUT_SEC' => '60',
+    'FUNCTION_REGION' => 'us-central1',
+  },
+
+  'Valid Vercel' => {
+    'VERCEL' => '1',
+    'VERCEL_URL' => '*.vercel.app',
+    'VERCEL_REGION' => 'cdg1',
+  },
+
+  'Invalid - multiple providers' => {
+    'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+    'AWS_REGION' => 'us-east-2',
+    'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024',
+    'FUNCTIONS_WORKER_RUNTIME' => 'ruby',
+  },
+
+  'Invalid - long string' => {
+    'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+    'AWS_REGION' => 'a' * 512,
+    'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024',
+  },
+
+  'Invalid - wrong types' => {
+    'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+    'AWS_REGION' => 'us-east-2',
+    'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => 'big',
+  },
+}.freeze
+
+describe 'Connect under FaaS Env' do
+  clean_slate
+
+  SCENARIOS.each do |name, env|
+    context "when given #{name}" do
+      local_env(env)
+
+      it 'connects successfully' do
+        resp = authorized_client.database.command(ping: 1)
+        expect(resp).to be_a(Mongo::Operation::Result)
+      end
+    end
+  end
+end
diff --git a/spec/integration/find_options_spec.rb b/spec/integration/find_options_spec.rb
new file mode 100644
index 0000000000..519a164b70
--- /dev/null
+++ b/spec/integration/find_options_spec.rb
@@ -0,0 +1,227 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Find operation options' do
+  require_mri
+  require_no_auth
+  min_server_fcv '4.4'
+
+  let(:subscriber) { Mrss::EventSubscriber.new }
+
+  let(:seeds) do
+    [ SpecConfig.instance.addresses.first ]
+  end
+
+  let(:client_options) do
+    {}
+  end
+
+  let(:collection_options) do
+    {}
+  end
+
+  let(:client) do
+    ClientRegistry.instance.new_local_client(
+      seeds,
+      SpecConfig.instance.test_options
+        .merge(database: SpecConfig.instance.test_db)
+        .merge(client_options)
+    ).tap do |client|
+      client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
+    end
+  end
+
+  let(:collection) do
+    client['find_options', collection_options]
+  end
+
+  let(:find_command) do
+    subscriber.started_events.find { |cmd| cmd.command_name == 'find' }
+  end
+
+  let(:should_create_collection) { true }
+
+  before do
+    client['find_options'].drop
+    collection.create if should_create_collection
+    collection.insert_many([ { a: 1 }, { a: 2 }, { a: 3 } ])
+  end
+
+  describe 'collation' do
+    let(:client_options) do
+      {}
+    end
+
+    let(:collation) do
+      { 'locale' => 'en_US' }
+    end
+
+    context 'when defined on the collection' do
+      let(:collection_options) do
+        { collation: collation }
+      end
+
+      it 'uses the collation defined on the collection' do
+        collection.find.to_a
+        expect(find_command.command['collation']).to be_nil
+      end
+    end
+
+    context 'when defined on the operation' do
+      let(:collection_options) do
+        {}
+      end
+
+      it 'uses the collation defined on the collection' do
+        collection.find({}, collation: collation).to_a
+        expect(find_command.command['collation']).to eq(collation)
+      end
+    end
+
+    context 'when defined on both collection and operation' do
+      let(:collection_options) do
+        { 'locale' => 'de_AT' }
+      end
+
+      let(:should_create_collection) { false }
+
+      it 'uses the collation defined on the collection' do
+        collection.find({}, collation: collation).to_a
+        expect(find_command.command['collation']).to eq(collation)
+      end
+    end
+  end
+
+  describe 'read concern' do
+    context 'when defined on the client' do
+      let(:client_options) do
+        { read_concern: { level: :local } }
+      end
+
+      let(:collection_options) do
+        {}
+      end
+
+      it 'uses the read concern defined on the client' do
+        collection.find.to_a
+        expect(find_command.command['readConcern']).to eq('level' => 'local')
+      end
+
+      context 'when defined on the collection' do
+        let(:collection_options) do
+          { read_concern: { level: :majority } }
+        end
+
+        it 'uses the read concern defined on the collection' do
+          collection.find.to_a
+          expect(find_command.command['readConcern']).to eq('level' => 'majority')
+        end
+
+        context 'when defined on the operation' do
+          let(:operation_read_concern) do
+            { level: :available }
+          end
+
+          it 'uses the read concern defined on the operation' do
+            collection.find({}, read_concern: operation_read_concern).to_a
+            expect(find_command.command['readConcern']).to eq('level' => 'available')
+          end
+        end
+      end
+
+      context 'when defined on the operation' do
+        let(:collection_options) do
+          {}
+        end
+
+        let(:operation_read_concern) do
+          { level: :available }
+        end
+
+        it 'uses the read concern defined on the operation' do
+          collection.find({}, read_concern: operation_read_concern).to_a
+          expect(find_command.command['readConcern']).to eq('level' => 'available')
+        end
+      end
+    end
+
+    context 'when defined on the collection' do
+      let(:client_options) do
+        {}
+      end
+
+      let(:collection_options) do
+        { read_concern: { level: :majority } }
+      end
+
+      it 'uses the read concern defined on the collection' do
+        collection.find.to_a
+        expect(find_command.command['readConcern']).to eq('level' => 'majority')
+      end
+
+      context 'when defined on the operation' do
+        let(:operation_read_concern) do
+          { level: :available }
+        end
+
+        it 'uses the read concern defined on the operation' do
+          collection.find({}, read_concern: operation_read_concern).to_a
+          expect(find_command.command['readConcern']).to eq('level' => 'available')
+        end
+      end
+    end
+  end
+
+  describe 'read preference' do
+    require_topology :replica_set
+
+    context 'when defined on the client' do
+      let(:client_options) do
+        { read: { mode: :secondary } }
+      end
+
+      let(:collection_options) do
+        {}
+      end
+
+      it 'uses the read preference defined on the client' do
+        collection.find.to_a
+        expect(find_command.command['$readPreference']).to eq('mode' => 'secondary')
+      end
+
+      context 'when defined on the collection' do
+        let(:collection_options) do
+          { read: { mode: :secondary_preferred } }
+        end
+
+        it 'uses the read concern defined on the collection' do
+          collection.find.to_a
+          expect(find_command.command['$readPreference']).to eq('mode' => 'secondaryPreferred')
+        end
+      end
+    end
+  end
+
+  describe 'cursor type' do
+    let(:collection_options) do
+      { capped: true, size: 1000 }
+    end
+
+    context 'when cursor type is :tailable' do
+      it 'sets the cursor type to tailable' do
+        collection.find({}, cursor_type: :tailable).first
+        expect(find_command.command['tailable']).to be true
+        expect(find_command.command['awaitData']).to be_falsey
+      end
+    end
+
+    context 'when cursor type is :tailable_await' do
+      it 'sets the cursor type to tailable' do
+        collection.find({}, cursor_type: :tailable_await).first
+        expect(find_command.command['tailable']).to be true
+        expect(find_command.command['awaitData']).to be true
+      end
+    end
+  end
+end
diff --git a/spec/integration/ocsp_verifier_spec.rb b/spec/integration/ocsp_verifier_spec.rb
index d70b702fee..4f5ce23175 100644
--- a/spec/integration/ocsp_verifier_spec.rb
+++ b/spec/integration/ocsp_verifier_spec.rb
@@ -352,7 +352,7 @@
 
     it 'verifies' do
       # TODO This test will fail if the certificate expires
-      verifier.verify.should be true
+      expect(verifier.verify).to be(true), "If atlas-ocsp certificates have expired, run spec/support/certificates/retrieve-atlas-cert to get a new ones"
     end
   end
 end
diff --git a/spec/lite_spec_helper.rb b/spec/lite_spec_helper.rb
index e6a63ecfbe..696938759e 100644
--- a/spec/lite_spec_helper.rb
+++ b/spec/lite_spec_helper.rb
@@ -110,6 +110,15 @@ class ExampleTimeout < StandardError; end
   config.include(MongosMacros)
   config.extend(Mongo::Macros)
 
+  # Used for spec/solo/*
+  def require_solo
+    before(:all) do
+      unless %w(1 true yes).include?(ENV['SOLO'])
+        skip 'Set SOLO=1 in environment to run solo tests'
+      end
+    end
+  end
+
   if SpecConfig.instance.ci?
     SdamFormatterIntegration.subscribe
     config.add_formatter(JsonExtFormatter, File.join(File.dirname(__FILE__), '../tmp/rspec.json'))
diff --git a/spec/mongo/address_spec.rb b/spec/mongo/address_spec.rb
index 826dacb869..db67bd1340 100644
--- a/spec/mongo/address_spec.rb
+++ b/spec/mongo/address_spec.rb
@@ -305,7 +305,7 @@
         RSpec::Mocks.with_temporary_scope do
           resolved_address = double('address')
           # This test's expectation
-          expect(resolved_address).to receive(:socket).with(0, connect_timeout: 10)
+          expect(resolved_address).to receive(:socket).with(0, { connect_timeout: 10 })
 
           expect(Mongo::Address::IPv4).to receive(:new).and_return(resolved_address)
 
diff --git a/spec/mongo/client_construction_spec.rb b/spec/mongo/client_construction_spec.rb
index 5bc278beb4..b5008da416 100644
--- a/spec/mongo/client_construction_spec.rb
+++ b/spec/mongo/client_construction_spec.rb
@@ -953,7 +953,7 @@
         end
 
         it 'includes the platform info in the app metadata' do
-          expect(app_metadata.send(:full_client_document)[:platform]).to match(/mongoid-6\.0\.2/)
+          expect(app_metadata.client_document[:platform]).to match(/mongoid-6\.0\.2/)
         end
       end
 
@@ -980,7 +980,7 @@
           end
 
           it 'does not include the platform info in the app metadata' do
-            expect(app_metadata.send(:full_client_document)[:platform]).to eq(platform_string)
+            expect(app_metadata.client_document[:platform]).to eq(platform_string)
           end
         end
 
@@ -999,7 +999,7 @@
           end
 
           it 'does not include the platform info in the app metadata' do
-            expect(app_metadata.send(:full_client_document)[:platform]).to eq(platform_string)
+            expect(app_metadata.client_document[:platform]).to eq(platform_string)
           end
         end
       end
@@ -1773,28 +1773,28 @@
             expect do
               client = new_local_client_nmio(['127.0.0.1:27017'],
                 :read => {:mode => :bogus})
-            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read option: {"mode"=>:bogus}: mode bogus is not one of recognized modes')
+            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read preference value: {"mode"=>:bogus}: mode bogus is not one of recognized modes')
           end
 
           it 'rejects bogus read preference as string' do
             expect do
               client = new_local_client_nmio(['127.0.0.1:27017'],
                 :read => {:mode => 'bogus'})
-            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read option: {"mode"=>"bogus"}: mode bogus is not one of recognized modes')
+            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read preference value: {"mode"=>"bogus"}: mode bogus is not one of recognized modes')
           end
 
           it 'rejects read option specified as a string' do
             expect do
               client = new_local_client_nmio(['127.0.0.1:27017'],
                 :read => 'primary')
-            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read option: primary: must be a hash')
+            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read preference value: "primary": the read preference must be specified as a hash: { mode: "primary" }')
           end
 
           it 'rejects read option specified as a symbol' do
             expect do
               client = new_local_client_nmio(['127.0.0.1:27017'],
                 :read => :primary)
-            end.to raise_error(Mongo::Error::InvalidReadOption, 'Invalid read option: primary: must be a hash')
+            end.to raise_error(Mongo::Error::InvalidReadOption, "Invalid read preference value: :primary: the read preference must be specified as a hash: { mode: :primary }")
           end
         end
       end
diff --git a/spec/mongo/client_spec.rb b/spec/mongo/client_spec.rb
index 0544820176..af847cdb20 100644
--- a/spec/mongo/client_spec.rb
+++ b/spec/mongo/client_spec.rb
@@ -947,7 +947,7 @@
           before do
             sessions_checked_out = 0
 
-            allow_any_instance_of(Mongo::Server).to receive(:with_connection).and_wrap_original do |m, *args, &block|
+            allow_any_instance_of(Mongo::Server).to receive(:with_connection).and_wrap_original do |m, *args, **kwargs, &block|
               m.call(*args) do |connection|
                 sessions_checked_out = 0
                 res = block.call(connection)
@@ -955,14 +955,6 @@
                 res
               end
             end
-
-            allow_any_instance_of(Mongo::Session).to receive(:materialize).and_wrap_original do |m, *args|
-              sessions_checked_out += 1
-              m.call(*args).tap do
-                checked_out_connections = args[0].connection_pool.instance_variable_get("@checked_out_connections")
-                expect(checked_out_connections.length).to eq 1
-              end
-            end
           end
 
           it 'doesn\'t have any live sessions' do
diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb
index c241fe95fa..adca88d52a 100644
--- a/spec/mongo/cluster_spec.rb
+++ b/spec/mongo/cluster_spec.rb
@@ -480,7 +480,7 @@
       end
 
       it 'constructs an AppMetadata object with the app_name' do
-        expect(cluster.app_metadata.send(:full_client_document)[:application]).to eq('name' => 'cluster_test')
+        expect(cluster.app_metadata.client_document[:application]).to eq('name' => 'cluster_test')
       end
     end
 
@@ -491,7 +491,7 @@
       end
 
       it 'constructs an AppMetadata object with no app_name' do
-        expect(cluster.app_metadata.send(:full_client_document)[:application]).to be_nil
+        expect(cluster.app_metadata.client_document[:application]).to be_nil
       end
     end
   end
diff --git a/spec/mongo/collection_crud_spec.rb b/spec/mongo/collection_crud_spec.rb
index 829e6f0452..411b6bd4a0 100644
--- a/spec/mongo/collection_crud_spec.rb
+++ b/spec/mongo/collection_crud_spec.rb
@@ -362,6 +362,42 @@
       expect(result.inserted_ids.size).to eq(2)
     end
 
+    context 'when an enumerable is used instead of an array' do
+
+      context 'when the enumerable is not empty' do
+
+        let(:source_data) do
+          [{ name: 'test1' }, { name: 'test2' }]
+        end
+
+        let(:result) do
+          authorized_collection.insert_many(source_data.lazy)
+        end
+
+        it 'should accepts them without raising an error' do
+          expect { result }.to_not raise_error
+          expect(result.inserted_count).to eq(source_data.size)
+        end
+      end
+
+      context 'when the enumerable is empty' do
+
+        let(:source_data) do
+          []
+        end
+
+        let(:result) do
+          authorized_collection.insert_many(source_data.lazy)
+        end
+
+        it 'should raise ArgumentError' do
+          expect do
+            result
+          end.to raise_error(ArgumentError, /Bulk write requests cannot be empty/)
+        end
+      end
+    end
+
     context 'when a session is provided' do
 
       let(:session) do
@@ -4412,4 +4448,24 @@ def generate
       expect(result).to be_nil
     end
   end
+
+  context "when creating collection with view_on and pipeline" do
+    before do
+      authorized_client["my_view"].drop
+      authorized_collection.insert_one({ bar: "here!" })
+      authorized_client["my_view",
+        view_on: authorized_collection.name,
+        pipeline: [ { :'$project' => { "baz": "$bar" } } ]
+      ].create
+    end
+
+    it "the view has a document" do
+      expect(authorized_client["my_view"].find.to_a.length).to eq(1)
+    end
+
+    it "applies the pipeline" do
+      expect(authorized_client["my_view"].find.first).to have_key("baz")
+      expect(authorized_client["my_view"].find.first["baz"]).to eq("here!")
+    end
+  end
 end
diff --git a/spec/mongo/collection_spec.rb b/spec/mongo/collection_spec.rb
index a4c1b774df..184347b245 100644
--- a/spec/mongo/collection_spec.rb
+++ b/spec/mongo/collection_spec.rb
@@ -647,7 +647,11 @@
     context 'when the collection is capped' do
 
       let(:collection) do
-        described_class.new(database, :specs, :capped => true, :size => 1024)
+        described_class.new(database, :specs, :capped => true, :size => 4096, :max => 512)
+      end
+
+      let(:collstats) do
+        database.read_command(:collstats => :specs).documents.first
       end
 
       before do
@@ -658,6 +662,12 @@
       it 'returns true' do
         expect(collection).to be_capped
       end
+
+      it "applies the options" do
+        expect(collstats["capped"]).to be true
+        expect(collstats["max"]).to eq(512)
+        expect(collstats["maxSize"]).to eq(4096)
+      end
     end
 
     context 'when the collection is not capped' do
diff --git a/spec/mongo/crypt/kms_spec.rb b/spec/mongo/crypt/kms_spec.rb
index 89f749af1d..f84ebadeb2 100644
--- a/spec/mongo/crypt/kms_spec.rb
+++ b/spec/mongo/crypt/kms_spec.rb
@@ -33,7 +33,6 @@
         %i(
           ssl_verify_certificate
           ssl_verify_hostname
-          ssl_verify_ocsp_endpoint
         ).each do |insecure_opt|
           expect {
             Mongo::Crypt::KMS::Validations.validate_tls_options(
@@ -46,14 +45,18 @@
       end
 
       it 'allows valid options' do
-        options = {
-          aws: {
-            ssl: true,
-            ssl_cert_string: 'Content is not validated',
-
-          }
-        }
+        expect do
+          Mongo::Crypt::KMS::Validations.validate_tls_options(
+            {
+              aws: {
+                ssl: true,
+                ssl_cert_string: 'Content is not validated',
+                ssl_verify_ocsp_endpoint: false
+              }
+            }
+          )
+        end.not_to raise_error
       end
     end
   end
-end
\ No newline at end of file
+end
diff --git a/spec/mongo/crypt_spec.rb b/spec/mongo/crypt_spec.rb
new file mode 100644
index 0000000000..c341c8ed5c
--- /dev/null
+++ b/spec/mongo/crypt_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mongo::Crypt do
+  describe '.validate_ffi!' do
+    context 'when ffi is available' do
+      context 'when ffi is loaded' do
+        it 'does not raise' do
+          expect do
+            described_class.validate_ffi!
+          end.not_to raise_error
+        end
+      end
+    end
+    # There is no reasonably simple way to test the path where ffi is not
+    # available. The ffi gem is a part of our standard test dependencies, so
+    # it's always available. So, we would need a dedicated configuration
+    # just to test this feature; it seems to be an overhead.
+  end
+end
diff --git a/spec/mongo/index/view_spec.rb b/spec/mongo/index/view_spec.rb
index 01627c61ce..d1c874e37d 100644
--- a/spec/mongo/index/view_spec.rb
+++ b/spec/mongo/index/view_spec.rb
@@ -960,6 +960,7 @@
         min_server_fcv '5.0'
 
         it 'passes wildcardProjection correctly' do
+          skip 'https://jira.mongodb.org/browse/RUBY-3216'
           expect(indexes[:wildcardProjection]).to eq({ '_id' => false, 'rating' => true })
         end
       end
diff --git a/spec/mongo/operation/delete/command_spec.rb b/spec/mongo/operation/delete/command_spec.rb
deleted file mode 100644
index c189306d8c..0000000000
--- a/spec/mongo/operation/delete/command_spec.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe Mongo::Operation::Delete::Command do
-
-  let(:write_concern) do
-    Mongo::WriteConcern.get(w: :majority)
-  end
-
-  let(:session) { nil }
-  let(:deletes) { [{:q => { :foo => 1 }, :limit => 1}] }
-  let(:spec) do
-    { :deletes       => deletes,
-      :db_name       => authorized_collection.database.name,
-      :coll_name     => authorized_collection.name,
-      :write_concern => write_concern,
-      :ordered       => true,
-      :session       => session
-    }
-  end
-
-  let(:op) { described_class.new(spec) }
-
-  describe '#initialize' do
-
-    context 'spec' do
-
-      it 'sets the spec' do
-        expect(op.spec).to eq(spec)
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'spec' do
-
-      context 'when two ops have the same specs' do
-        let(:other) { described_class.new(spec) }
-
-        it 'returns true' do
-          expect(op).to eq(other)
-        end
-      end
-
-      context 'when two ops have different specs' do
-        let(:other_deletes) { [{:q => { :bar => 1 }, :limit => 1}] }
-        let(:other_spec) do
-          { :deletes       => other_deletes,
-            :db_name       => authorized_collection.database.name,
-            :coll_name     => authorized_collection.name,
-            :write_concern => write_concern,
-            :ordered       => true
-          }
-        end
-        let(:other) { described_class.new(other_spec) }
-
-        it 'returns false' do
-          expect(op).not_to eq(other)
-        end
-      end
-    end
-  end
-
-  describe 'write concern' do
-
-    context 'when write concern is not specified' do
-
-      let(:spec) do
-        { :deletes       => deletes,
-          :db_name       => authorized_collection.database.name,
-          :coll_name     => authorized_collection.name,
-          :ordered       => true
-        }
-      end
-
-      it 'does not include write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to be_nil
-      end
-    end
-
-    context 'when write concern is specified' do
-
-      it 'includes write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to eq(write_concern.options)
-      end
-    end
-  end
-
-  describe '#message' do
-
-    context 'when the server does not support OP_MSG' do
-      max_server_version '3.4'
-
-      let(:expected_selector) do
-        {
-            :delete        => authorized_collection.name,
-            :deletes       => deletes,
-            :writeConcern => write_concern.options,
-            :ordered       => true
-        }
-      end
-
-      it 'creates the correct query wire protocol message' do
-        expect(Mongo::Protocol::Query).to receive(:new).with(authorized_collection.database.name,
-                                                             '$cmd',
-                                                             expected_selector,
-                                                             { limit: -1 } )
-        op.send(:message, authorized_primary)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/operation/find/legacy_spec.rb b/spec/mongo/operation/find/legacy_spec.rb
deleted file mode 100644
index f9cf973f3b..0000000000
--- a/spec/mongo/operation/find/legacy_spec.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe Mongo::Operation::Find::Legacy do
-  max_server_version '3.4'
-
-  let(:context) { Mongo::Operation::Context.new }
-
-  let(:selector) { { foo: 1 } }
-  let(:query_options) { {} }
-  let(:spec) do
-    { :selector  => selector,
-      :options      => query_options,
-      :db_name   => authorized_collection.database.name,
-      :coll_name => authorized_collection.name,
-      :read => Mongo::ServerSelector.get
-    }
-  end
-  let(:op) { described_class.new(spec) }
-
-  describe '#initialize' do
-
-    context 'query spec' do
-      it 'sets the query spec' do
-        expect(op.spec).to be(spec)
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'when two ops have different specs' do
-      let(:other_spec) do
-        { :selector  => { :a => 1 },
-          :options      => query_options,
-          :db_name   => authorized_collection.database.name,
-          :coll_name => authorized_collection.name
-        }
-      end
-      let(:other) { described_class.new(other_spec) }
-
-      it 'returns false' do
-        expect(op).not_to eq(other)
-      end
-    end
-  end
-
-  describe '#message' do
-
-    let(:query_options) do
-      { :flags => [ :no_cursor_timeout ]}
-    end
-
-    let(:query) do
-      described_class.new(spec)
-    end
-
-    let(:cluster_single) do
-      double('cluster').tap do |c|
-        allow(c).to receive(:single?).and_return(true)
-      end
-    end
-
-    let(:message) do
-      authorized_primary.with_connection do |connection|
-        query.send(:message, connection)
-      end
-    end
-
-    it 'applies the correct flags' do
-      expect(message.flags).to eq(query_options[:flags])
-    end
-
-    context 'when the server is a secondary' do
-      let(:description) do
-        double('description').tap do |description|
-          expect(description).to receive(:mongos?) { false }
-          expect(description).to receive(:standalone?) { false }
-          #expect(description).to receive(:load_balancer?) { false }
-        end
-      end
-
-      let(:connection) do
-        double('connection').tap do |conn|
-          expect(conn).to receive(:description).at_least(:once).and_return(description)
-        end
-      end
-
-      let(:secondary_server_single) do
-        double('secondary_server').tap do |server|
-          expect(server).to receive(:with_connection).and_yield(connection)
-          expect(server).to receive(:cluster) { cluster_single }
-        end
-      end
-
-      let(:message) do
-        secondary_server_single.with_connection do |connection|
-          query.send(:message, connection)
-        end
-      end
-
-      before do
-        allow(connection).to receive(:server) { secondary_server_single }
-      end
-
-      it 'applies the correct flags' do
-        expect(message.flags).to eq([ :no_cursor_timeout, :secondary_ok ])
-      end
-    end
-
-    context "when the document contains an 'ok' field" do
-
-      before do
-        authorized_collection.insert_one(ok: false)
-      end
-
-      after do
-        authorized_collection.delete_many
-      end
-
-      it 'does not raise an exception' do
-        authorized_primary.with_connection do |connection|
-          expect(op.execute(connection, context: context)).to be_a(Mongo::Operation::Find::Legacy::Result)
-        end
-      end
-    end
-  end
-end
-
diff --git a/spec/mongo/operation/get_more_spec.rb b/spec/mongo/operation/get_more_spec.rb
deleted file mode 100644
index 7c5bd9bb19..0000000000
--- a/spec/mongo/operation/get_more_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe Mongo::Operation::GetMore::Legacy do
-
-  let(:to_return) do
-    50
-  end
-
-  let(:cursor_id) do
-    1
-  end
-
-  let(:spec) do
-    { :db_name   => SpecConfig.instance.test_db,
-      :coll_name => TEST_COLL,
-      :to_return => to_return,
-      :cursor_id => cursor_id }
-  end
-
-  let(:op) { described_class.new(spec) }
-
-  let(:context) { Mongo::Operation::Context.new }
-
-  describe '#initialize' do
-
-    it 'sets the spec' do
-      expect(op.spec).to be(spec)
-    end
-  end
-
-  describe '#==' do
-
-    context ' when two ops have different specs' do
-      let(:other_spec) do
-        { :db_name   => 'test_db',
-          :coll_name => 'test_coll',
-          :to_return => 50,
-          :cursor_id => 2 }
-      end
-      let(:other) { described_class.new(other_spec) }
-
-      it 'returns false' do
-        expect(op).not_to eq(other)
-      end
-    end
-  end
-
-  describe '#message' do
-
-    it 'creates a get more wire protocol message with correct specs' do
-      expect(Mongo::Protocol::GetMore).to receive(:new).with(SpecConfig.instance.test_db, TEST_COLL, to_return, cursor_id).and_call_original
-      begin
-        authorized_primary.with_connection do |connection|
-          op.execute(connection, context: context)
-        end
-      rescue
-      end
-    end
-  end
-end
diff --git a/spec/mongo/operation/insert/command_spec.rb b/spec/mongo/operation/insert/command_spec.rb
deleted file mode 100644
index 930c8fdfa5..0000000000
--- a/spec/mongo/operation/insert/command_spec.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe Mongo::Operation::Insert::Command do
-
-  let(:documents) { [{ :_id => 1, :foo => 1 }] }
-  let(:session) { nil }
-  let(:spec) do
-    { :documents     => documents,
-      :db_name       => authorized_collection.database.name,
-      :coll_name     => authorized_collection.name,
-      :write_concern => write_concern,
-      :ordered       => true,
-      :session       => session
-    }
-  end
-
-  let(:write_concern) do
-    Mongo::WriteConcern.get(w: :majority)
-  end
-
-  let(:op) { described_class.new(spec) }
-
-  describe '#initialize' do
-
-    context 'spec' do
-
-      it 'sets the spec' do
-        expect(op.spec).to eq(spec)
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'spec' do
-
-      context 'when two ops have the same specs' do
-        let(:other) { described_class.new(spec) }
-
-        it 'returns true' do
-          expect(op).to eq(other)
-        end
-      end
-
-      context 'when two ops have different specs' do
-        let(:other_documents) { [{ :bar => 1 }] }
-        let(:other_spec) do
-          { :documents     => other_documents,
-            :db_name       => authorized_collection.database.name,
-            :insert        => authorized_collection.name,
-            :write_concern => write_concern.options,
-            :ordered       => true
-          }
-        end
-        let(:other) { described_class.new(other_spec) }
-
-        it 'returns false' do
-          expect(op).not_to eq(other)
-        end
-      end
-    end
-  end
-
-  describe 'write concern' do
-    # https://jira.mongodb.org/browse/RUBY-2224
-    require_no_linting
-
-    context 'when write concern is not specified' do
-
-      let(:spec) do
-        { :documents     => documents,
-          :db_name       => authorized_collection.database.name,
-          :coll_name     => authorized_collection.name,
-          :ordered       => true
-        }
-      end
-
-      it 'does not include write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to be_nil
-      end
-    end
-
-    context 'when write concern is specified' do
-
-      it 'includes write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to eq(write_concern.options)
-      end
-    end
-  end
-
-  describe '#message' do
-    # https://jira.mongodb.org/browse/RUBY-2224
-    require_no_linting
-
-    context 'when the server does not support OP_MSG' do
-      max_server_version '3.4'
-
-      let(:expected_selector) do
-        { :documents     => documents,
-          :insert        => authorized_collection.name,
-          :writeConcern => write_concern.options,
-          :ordered       => true
-        }
-      end
-
-      it 'creates a query wire protocol message with correct specs' do
-        expect(Mongo::Protocol::Query).to receive(:new).with(authorized_collection.database.name,
-                                                             '$cmd',
-                                                             expected_selector,
-                                                             { limit: -1, validating_keys: true })
-        op.send(:message, authorized_primary)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/operation/update/command_spec.rb b/spec/mongo/operation/update/command_spec.rb
deleted file mode 100644
index f2b0c6de46..0000000000
--- a/spec/mongo/operation/update/command_spec.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe Mongo::Operation::Update::Command do
-
-  let(:updates) { [{:q => { :foo => 1 },
-                    :u => { :$set => { :bar => 1 } },
-                    :multi => true,
-                    :upsert => false }] }
-
-  let(:write_concern) do
-    Mongo::WriteConcern.get(w: :majority)
-  end
-  let(:session) { nil }
-  let(:spec) do
-    { :updates       => updates,
-      :db_name       => SpecConfig.instance.test_db,
-      :coll_name     => TEST_COLL,
-      :write_concern => write_concern,
-      :ordered       => true,
-      :session       => session
-    }
-  end
-
-  let(:op) { described_class.new(spec) }
-
-  describe '#initialize' do
-
-    context 'spec' do
-
-      it 'sets the spec' do
-        expect(op.spec).to eq(spec)
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'spec' do
-
-      context 'when two ops have the same specs' do
-        let(:other) { described_class.new(spec) }
-
-        it 'returns true' do
-          expect(op).to eq(other)
-        end
-      end
-
-      context 'when two ops have different specs' do
-        let(:other_updates) { [{:q => { :bar => 1 },
-                                :u => { :$set => { :bar => 2 } },
-                                :multi => true,
-                                :upsert => false }] }
-        let(:other_spec) do
-          { :updates       => other_updates,
-            :db_name       => SpecConfig.instance.test_db,
-            :coll_name     => TEST_COLL,
-            :write_concern => Mongo::WriteConcern.get(w: :majority),
-            :ordered       => true
-          }
-        end
-        let(:other) { described_class.new(other_spec) }
-
-        it 'returns false' do
-          expect(op).not_to eq(other)
-        end
-      end
-    end
-  end
-
-  describe 'write concern' do
-    # https://jira.mongodb.org/browse/RUBY-2224
-    require_no_linting
-
-    context 'when write concern is not specified' do
-
-      let(:spec) do
-        { :updates       => updates,
-          :db_name       => SpecConfig.instance.test_db,
-          :coll_name     => TEST_COLL,
-          :ordered       => true
-        }
-      end
-
-      it 'does not include write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to be_nil
-      end
-    end
-
-    context 'when write concern is specified' do
-
-      it 'includes write concern in the selector' do
-        expect(op.send(:command, double('server'))[:writeConcern]).to eq(write_concern.options)
-      end
-    end
-  end
-
-  describe '#message' do
-    # https://jira.mongodb.org/browse/RUBY-2224
-    require_no_linting
-
-    context 'when the server does not support OP_MSG' do
-      max_server_version '3.4'
-
-      let(:expected_selector) do
-        {
-            :update        => TEST_COLL,
-            :updates       => updates,
-            :ordered       => true,
-            :writeConcern   => write_concern.options
-        }
-      end
-
-      it 'creates the correct Command message' do
-        expect(Mongo::Protocol::Query).to receive(:new).with(SpecConfig.instance.test_db, '$cmd', expected_selector, { limit: -1 })
-        op.send(:message, authorized_primary)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/protocol/caching_hash_spec.rb b/spec/mongo/protocol/caching_hash_spec.rb
index cbf717faca..e92c756679 100644
--- a/spec/mongo/protocol/caching_hash_spec.rb
+++ b/spec/mongo/protocol/caching_hash_spec.rb
@@ -34,49 +34,4 @@
       end
     end
   end
-
-  describe "#needs_validation?" do
-
-    before do
-      hash.to_bson(BSON::ByteBuffer.new, validation)
-    end
-
-    let(:needs_validation?) do
-      hash.send(:needs_validation?, validating_keys)
-    end
-
-    context "when calling to_bson without validation" do
-      let(:validation) { false }
-
-      context "validating_keys is true" do
-        let(:validating_keys) { true }
-
-        it "is true" do
-          expect(needs_validation?).to be true
-        end
-      end
-
-      context "validating_keys is false" do
-        let(:validating_keys) { false }
-
-        it "is false" do
-          expect(needs_validation?).to be false
-        end
-      end
-    end
-
-    context "when calling to_bson with validation" do
-      let(:validation) { true }
-
-      [true, false].each do |b|
-        context "validating_keys is #{b}" do
-          let(:validating_keys) { b }
-
-          it "is false" do
-            expect(needs_validation?).to be false
-          end
-        end
-      end
-    end
-  end
 end
diff --git a/spec/mongo/protocol/delete_spec.rb b/spec/mongo/protocol/delete_spec.rb
deleted file mode 100644
index d3bb0566c6..0000000000
--- a/spec/mongo/protocol/delete_spec.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'lite_spec_helper'
-require 'support/shared/protocol'
-
-describe Mongo::Protocol::Delete do
-
-  let(:opcode)   { 2006 }
-  let(:db)       { SpecConfig.instance.test_db }
-  let(:collection_name) { 'protocol-test' }
-  let(:ns)       { "#{db}.#{collection_name}" }
-  let(:selector) { { :name => 'Tyler' } }
-  let(:options)     { Hash.new }
-
-  let(:message) do
-    described_class.new(db, collection_name, selector, options)
-  end
-
-  describe '#initialize' do
-
-    it 'sets the namepsace' do
-      expect(message.namespace).to eq(ns)
-    end
-
-    it 'sets the selector' do
-      expect(message.selector).to eq(selector)
-    end
-
-    context 'when options are provided' do
-
-      context 'when flags are provided' do
-        let(:options) { { :flags => [:single_remove] } }
-
-        it 'sets the flags' do
-          expect(message.flags).to eq(options[:flags])
-        end
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'when the other is a delete' do
-
-      context 'when the fields are equal' do
-        let(:other) do
-          described_class.new(db, collection_name, selector, options)
-        end
-
-        it 'returns true' do
-          expect(message).to eq(other)
-        end
-      end
-
-      context 'when the database is not equal' do
-        let(:other) do
-          described_class.new('tyler', collection_name, selector, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the collection is not equal' do
-        let(:other) do
-          described_class.new(db, 'tyler', selector, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the selector is not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, { :a => 1 }, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the options are not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, selector, :flags => [:single_remove])
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-    end
-
-    context 'when the other is not a delete' do
-      let(:other) do
-        expect(message).not_to eq('test')
-      end
-    end
-  end
-
-  describe '#hash' do
-    let(:values) do
-      message.send(:fields).map do |field|
-        message.instance_variable_get(field[:name])
-      end
-    end
-
-    it 'returns a hash of the field values' do
-      expect(message.hash).to eq(values.hash)
-    end
-  end
-
-  describe '#replyable?' do
-
-    it 'returns false' do
-      expect(message).to_not be_replyable
-    end
-  end
-
-  describe '#serialize' do
-    let(:bytes) { message.serialize }
-
-    include_examples 'message with a header'
-
-    describe 'zero' do
-      let(:field) { bytes.to_s[16..19] }
-
-      it 'serializes a zero' do
-        expect(field).to be_int32(0)
-      end
-    end
-
-    describe 'namespace' do
-      let(:field) { bytes.to_s[20..36] }
-      it 'serializes the namespace' do
-        expect(field).to be_cstring(ns)
-      end
-    end
-
-    describe 'flags' do
-      let(:field) { bytes.to_s[37..40] }
-
-      context 'when no flags are provided' do
-        it 'does not set any bits' do
-          expect(field).to be_int32(0)
-        end
-      end
-
-      context 'when flags are provided' do
-        let(:options) { { :flags => flags } }
-
-        context 'single remove flag' do
-          let(:flags) { [:single_remove] }
-          it 'sets the first bit' do
-            expect(field).to be_int32(1)
-          end
-        end
-      end
-    end
-
-    describe 'selector' do
-      let(:field) { bytes.to_s[41..-1] }
-      it 'serializes the selector' do
-        expect(field).to be_bson(selector)
-      end
-    end
-  end
-
-  describe '#registry' do
-
-    context 'when the class is loaded' do
-
-      it 'registers the op code in the Protocol Registry' do
-        expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
-      end
-
-      it 'creates an #op_code instance method' do
-        expect(message.op_code).to eq(described_class::OP_CODE)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/protocol/insert_spec.rb b/spec/mongo/protocol/insert_spec.rb
deleted file mode 100644
index 804963a17a..0000000000
--- a/spec/mongo/protocol/insert_spec.rb
+++ /dev/null
@@ -1,179 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'lite_spec_helper'
-require 'support/shared/protocol'
-
-describe Mongo::Protocol::Insert do
-
-  let(:opcode) { 2002 }
-  let(:db)     { SpecConfig.instance.test_db }
-  let(:collection_name) { 'protocol-test' }
-  let(:ns)     { "#{db}.#{collection_name}" }
-  let(:doc1)   { { :name => 'Tyler' } }
-  let(:doc2)   { { :name => 'Brandon' } }
-  let(:docs)   { [doc1, doc2] }
-  let(:options)   { Hash.new }
-
-  let(:message) do
-    described_class.new(db, collection_name, docs, options)
-  end
-
-  describe '#initialize' do
-
-    it 'sets the namepsace' do
-      expect(message.namespace).to eq(ns)
-    end
-
-    it 'sets the documents' do
-      expect(message.documents).to eq(docs)
-    end
-
-    context 'when options are provided' do
-
-      context 'when flags are provided' do
-        let(:options) { { :flags => [:continue_on_error] } }
-
-        it 'sets the flags' do
-          expect(message.flags).to eq(options[:flags])
-        end
-      end
-    end
-  end
-
-  describe '#==' do
-
-    context 'when the other is an insert' do
-
-      context 'when the fields are equal' do
-        let(:other) do
-          described_class.new(db, collection_name, docs, options)
-        end
-
-        it 'returns true' do
-          expect(message).to eq(other)
-        end
-      end
-
-      context 'when the database is not equal' do
-        let(:other) do
-          described_class.new('tyler', collection_name, docs, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the collection is not equal' do
-        let(:other) do
-          described_class.new(db, 'tyler', docs, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the documents are not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, docs[1..1], options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the options are not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, docs, :flags => [:continue_on_error])
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-    end
-
-    context 'when the other is not an insert' do
-      let(:other) do
-        expect(message).not_to eq('test')
-      end
-    end
-  end
-
-  describe '#hash' do
-    let(:values) do
-      message.send(:fields).map do |field|
-        message.instance_variable_get(field[:name])
-      end
-    end
-
-    it 'returns a hash of the field values' do
-      expect(message.hash).to eq(values.hash)
-    end
-  end
-
-  describe '#replyable?' do
-
-    it 'returns false' do
-      expect(message).to_not be_replyable
-    end
-  end
-
-  describe '#serialize' do
-    let(:bytes) { message.serialize }
-
-    include_examples 'message with a header'
-
-    describe 'flags' do
-      let(:field) { bytes.to_s[16..19] }
-
-      context 'when no flags are provided' do
-        it 'does not set any bits' do
-          expect(field).to be_int32(0)
-        end
-      end
-
-      context 'when flags are provided' do
-        let(:options) { { :flags => flags } }
-
-        context 'continue on error flag' do
-          let(:flags) { [:continue_on_error] }
-          it 'sets the first bit' do
-            expect(field).to be_int32(1)
-          end
-        end
-      end
-    end
-
-    describe 'namespace' do
-      let(:field) { bytes.to_s[20..36] }
-      it 'serializes the namespace' do
-        expect(field).to be_cstring(ns)
-      end
-    end
-
-    describe 'documents' do
-      let(:field) { bytes.to_s[37..-1] }
-      it 'serializes the documents' do
-        expect(field).to be_bson_sequence(docs)
-      end
-    end
-  end
-
-  describe '#registry' do
-
-    context 'when the class is loaded' do
-
-      it 'registers the op code in the Protocol Registry' do
-        expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
-      end
-
-      it 'creates an #op_code instance method' do
-        expect(message.op_code).to eq(described_class::OP_CODE)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/protocol/msg_spec.rb b/spec/mongo/protocol/msg_spec.rb
index 09391685ce..39ebc72bf5 100644
--- a/spec/mongo/protocol/msg_spec.rb
+++ b/spec/mongo/protocol/msg_spec.rb
@@ -403,10 +403,8 @@
         { validating_keys: true }
       end
 
-      it 'checks the sequence document keys' do
-        expect {
-          message.serialize
-        }.to raise_exception(BSON::String::IllegalKey)
+      it 'does not check the sequence document keys' do
+        expect(message.serialize).to be_a(BSON::ByteBuffer)
       end
     end
 
diff --git a/spec/mongo/protocol/update_spec.rb b/spec/mongo/protocol/update_spec.rb
deleted file mode 100644
index 98412f1db8..0000000000
--- a/spec/mongo/protocol/update_spec.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-# frozen_string_literal: true
-# encoding: utf-8
-
-require 'lite_spec_helper'
-require 'support/shared/protocol'
-
-describe Mongo::Protocol::Update do
-
-  let(:opcode)   { 2001 }
-  let(:db)       { SpecConfig.instance.test_db }
-  let(:collection_name) { 'protocol-test' }
-  let(:ns)       { "#{db}.#{collection_name}" }
-  let(:selector) { { :name => 'Tyler' } }
-  let(:update_doc) { { :name => 'Bob' } }
-  let(:options)       { Hash.new }
-
-  let(:message) do
-    described_class.new(db, collection_name, selector, update_doc, options)
-  end
-
-  describe '#initialize' do
-
-    it 'sets the namespace' do
-      expect(message.namespace).to eq(ns)
-    end
-
-    it 'sets the selector' do
-      expect(message.selector).to eq(selector)
-    end
-
-    it 'sets the update document' do
-      expect(message.update).to eq(update_doc)
-    end
-  end
-
-  describe '#==' do
-
-    context 'when the other is an update' do
-
-      context 'when the fields are equal' do
-        let(:other) do
-          described_class.new(db, collection_name, selector, update_doc, options)
-        end
-
-        it 'returns true' do
-          expect(message).to eq(other)
-        end
-      end
-
-      context 'when the database is not equal' do
-        let(:other) do
-          described_class.new('tyler', collection_name, selector, update_doc, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the collection is not equal' do
-        let(:other) do
-          described_class.new(db, 'tyler', selector, update_doc, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the selector is not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, { :a => 1 }, update_doc, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the update document is not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, selector, { :a => 1 }, options)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-
-      context 'when the options are not equal' do
-        let(:other) do
-          described_class.new(db, collection_name, selector, update_doc,
-                              :flags => :upsert)
-        end
-
-        it 'returns false' do
-          expect(message).not_to eq(other)
-        end
-      end
-    end
-
-    context 'when the other is not a query' do
-      let(:other) do
-        expect(message).not_to eq('test')
-      end
-    end
-  end
-
-  describe '#hash' do
-    let(:values) do
-      message.send(:fields).map do |field|
-        message.instance_variable_get(field[:name])
-      end
-    end
-
-    it 'returns a hash of the field values' do
-      expect(message.hash).to eq(values.hash)
-    end
-  end
-
-  describe '#replyable?' do
-
-    it 'returns false' do
-      expect(message).to_not be_replyable
-    end
-  end
-
-  describe '#serialize' do
-    let(:bytes) { message.serialize }
-
-    include_examples 'message with a header'
-
-    describe 'zero' do
-      let(:field) { bytes.to_s[16..19] }
-
-      it 'serializes a zero' do
-        expect(field).to be_int32(0)
-      end
-    end
-
-    describe 'namespace' do
-      let(:field) { bytes.to_s[20..36] }
-      it 'serializes the namespace' do
-        expect(field).to be_cstring(ns)
-      end
-    end
-
-    describe 'flags' do
-      let(:field) { bytes.to_s[37..40] }
-
-      context 'when no flags are provided' do
-        it 'does not set any bits' do
-          expect(field).to be_int32(0)
-        end
-      end
-
-      context 'when flags are provided' do
-        let(:options) { { :flags => flags } }
-
-        context 'upsert flag' do
-          let(:flags) { [:upsert] }
-          it 'sets the first bit' do
-            expect(field).to be_int32(1)
-          end
-        end
-
-        context 'multi update' do
-          let(:flags) { [:multi_update] }
-          it 'sets the second bit' do
-            expect(field).to be_int32(2)
-          end
-        end
-      end
-    end
-
-    describe 'selector' do
-      let(:field) { bytes.to_s[41..61] }
-      it 'serializes the selector' do
-        expect(field).to be_bson(selector)
-      end
-    end
-
-    describe 'update' do
-      let(:field) { bytes.to_s[62..80] }
-      it 'serializes the update' do
-        expect(field).to be_bson(update_doc)
-      end
-    end
-  end
-
-  describe '#registry' do
-
-    context 'when the class is loaded' do
-
-      it 'registers the op code in the Protocol Registry' do
-        expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
-      end
-
-      it 'creates an #op_code instance method' do
-        expect(message.op_code).to eq(described_class::OP_CODE)
-      end
-    end
-  end
-end
diff --git a/spec/mongo/server/app_metadata/environment_spec.rb b/spec/mongo/server/app_metadata/environment_spec.rb
new file mode 100644
index 0000000000..3bf88acca9
--- /dev/null
+++ b/spec/mongo/server/app_metadata/environment_spec.rb
@@ -0,0 +1,193 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mongo::Server::AppMetadata::Environment do
+  let(:env) { described_class.new }
+
+  shared_examples_for 'running in a FaaS environment' do
+    it 'reports that a FaaS environment is detected' do
+      expect(env.faas?).to be true
+    end
+  end
+
+  shared_examples_for 'running outside a FaaS environment' do
+    it 'reports that no FaaS environment is detected' do
+      expect(env.faas?).to be false
+    end
+  end
+
+  context 'when run outside of a FaaS environment' do
+    it_behaves_like 'running outside a FaaS environment'
+  end
+
+  context 'when run in a FaaS environment' do
+    context 'when environment is invalid due to type mismatch' do
+      local_env(
+        'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+        'AWS_REGION' => 'us-east-2',
+        'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => 'big'
+      )
+
+      it_behaves_like 'running outside a FaaS environment'
+
+      it 'fails due to type mismatch' do
+        expect(env.error).to match(/AWS_LAMBDA_FUNCTION_MEMORY_SIZE must be integer/)
+      end
+    end
+
+    context 'when environment is invalid due to long string' do
+      local_env(
+        'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+        'AWS_REGION' => 'a' * 512,
+        'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024'
+      )
+
+      it_behaves_like 'running outside a FaaS environment'
+
+      it 'fails due to long string' do
+        expect(env.error).to match(/too long/)
+      end
+    end
+
+    context 'when environment is invalid due to multiple providers' do
+      local_env(
+        'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+        'AWS_REGION' => 'us-east-2',
+        'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024',
+        'FUNCTIONS_WORKER_RUNTIME' => 'ruby'
+      )
+
+      it_behaves_like 'running outside a FaaS environment'
+
+      it 'fails due to multiple providers' do
+        expect(env.error).to match(/too many environments/)
+      end
+    end
+
+    context 'when environment is invalid due to missing variable' do
+      local_env(
+        'AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7',
+        'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024'
+      )
+
+      it_behaves_like 'running outside a FaaS environment'
+
+      it 'fails due to missing variable' do
+        expect(env.error).to match(/missing environment variable/)
+      end
+    end
+
+    context 'when FaaS environment is AWS' do
+      shared_examples_for 'running in an AWS environment' do
+        context 'when environment is valid' do
+          local_env(
+            'AWS_REGION' => 'us-east-2',
+            'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024'
+          )
+
+          it_behaves_like 'running in a FaaS environment'
+
+          it 'recognizes AWS' do
+            expect(env.name).to be == 'aws.lambda'
+            expect(env.fields[:region]).to be == 'us-east-2'
+            expect(env.fields[:memory_mb]).to be == 1024
+          end
+        end
+      end
+
+      # per DRIVERS-2623, AWS_EXECUTION_ENV must be prefixed
+      # with 'AWS_Lambda_'.
+      context 'when AWS_EXECUTION_ENV is invalid' do
+        local_env(
+          'AWS_EXECUTION_ENV' => 'EC2',
+          'AWS_REGION' => 'us-east-2',
+          'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => '1024'
+        )
+
+        it_behaves_like 'running outside a FaaS environment'
+      end
+
+      context 'when AWS_EXECUTION_ENV is detected' do
+        local_env('AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7')
+        it_behaves_like 'running in an AWS environment'
+      end
+
+      context 'when AWS_LAMBDA_RUNTIME_API is detected' do
+        local_env('AWS_LAMBDA_RUNTIME_API' => 'lambda.aws.amazon.com/api')
+        it_behaves_like 'running in an AWS environment'
+      end
+    end
+
+    context 'when FaaS environment is Azure' do
+      local_env('FUNCTIONS_WORKER_RUNTIME' => 'ruby')
+
+      it_behaves_like 'running in a FaaS environment'
+
+      it 'recognizes Azure' do
+        expect(env.name).to be == 'azure.func'
+      end
+    end
+
+    context 'when FaaS environment is GCP' do
+      local_env(
+        'FUNCTION_MEMORY_MB' => '1024',
+        'FUNCTION_TIMEOUT_SEC' => '60',
+        'FUNCTION_REGION' => 'us-central1'
+      )
+
+      shared_examples_for 'running in a GCP environment' do
+        it_behaves_like 'running in a FaaS environment'
+
+        it 'recognizes GCP' do
+          expect(env.name).to be == 'gcp.func'
+          expect(env.fields[:region]).to be == 'us-central1'
+          expect(env.fields[:memory_mb]).to be == 1024
+          expect(env.fields[:timeout_sec]).to be == 60
+        end
+      end
+
+      context 'when K_SERVICE is present' do
+        local_env('K_SERVICE' => 'servicename')
+        it_behaves_like 'running in a GCP environment'
+      end
+
+      context 'when FUNCTION_NAME is present' do
+        local_env('FUNCTION_NAME' => 'functionName')
+        it_behaves_like 'running in a GCP environment'
+      end
+    end
+
+    context 'when FaaS environment is Vercel' do
+      local_env(
+        'VERCEL' => '1',
+        'VERCEL_URL' => '*.vercel.app',
+        'VERCEL_REGION' => 'cdg1'
+      )
+
+      it_behaves_like 'running in a FaaS environment'
+
+      it 'recognizes Vercel' do
+        expect(env.name).to be == 'vercel'
+        expect(env.fields[:url]).to be == '*.vercel.app'
+        expect(env.fields[:region]).to be == 'cdg1'
+      end
+    end
+
+    context 'when converting environment to a hash' do
+      local_env(
+        'K_SERVICE' => 'servicename',
+        'FUNCTION_MEMORY_MB' => '1024',
+        'FUNCTION_TIMEOUT_SEC' => '60',
+        'FUNCTION_REGION' => 'us-central1'
+      )
+
+      it 'includes name and all fields' do
+        expect(env.to_h).to be == {
+          name: 'gcp.func', memory_mb: 1024,
+          timeout_sec: 60, region: 'us-central1',
+        }
+      end
+    end
+  end
+end
diff --git a/spec/mongo/server/app_metadata/truncator_spec.rb b/spec/mongo/server/app_metadata/truncator_spec.rb
new file mode 100644
index 0000000000..52fcad3240
--- /dev/null
+++ b/spec/mongo/server/app_metadata/truncator_spec.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# Quoted from specifications/source/mongodb-handshake/handshake.rst:
+#
+# Implementors SHOULD cumulatively update fields in the following order
+# until the document is under the size limit:
+#
+# 1. Omit fields from env except env.name.
+# 2. Omit fields from os except os.type.
+# 3. Omit the env document entirely.
+# 4. Truncate platform.
+
+describe Mongo::Server::AppMetadata::Truncator do
+  let(:truncator) { described_class.new(Marshal.load(Marshal.dump(metadata))) }
+
+  let(:app_name) { 'application' }
+  let(:driver) { { name: 'driver', version: '1.2.3' } }
+  let(:os) { { type: 'Darwin', name: 'macOS', architecture: 'arm64', version: '13.4' } }
+  let(:platform) { { platform: 'platform' } }
+  let(:env) { { name: 'aws.lambda', region: 'region', memory_mb: 1024 } }
+
+  let(:metadata) do
+    BSON::Document.new.tap do |doc|
+      doc[:application] = { name: app_name }
+      doc[:driver] = driver
+      doc[:os] = os
+      doc[:platform] = platform
+      doc[:env] = env
+    end
+  end
+
+  let(:untruncated_length) { metadata.to_bson.to_s.length }
+  let(:truncated_length) { truncator.document.to_bson.to_s.length }
+
+  shared_examples_for 'a truncated document' do
+    it 'is shorter' do
+      expect(truncated_length).to be < untruncated_length
+    end
+
+    it 'is not be longer than the maximum document size' do
+      expect(truncated_length).to be <= described_class::MAX_DOCUMENT_SIZE
+    end
+  end
+
+  describe 'MAX_DOCUMENT_SIZE' do
+    it 'is 512 bytes' do
+      # This test is an additional check that MAX_DOCUMENT_SIZE
+      # has not been accidentially changed.
+      expect(described_class::MAX_DOCUMENT_SIZE).to be == 512
+    end
+  end
+
+  context 'when document does not need truncating' do
+    it 'does not truncate anything' do
+      expect(truncated_length).to be == untruncated_length
+    end
+  end
+
+  context 'when modifying env is sufficient' do
+    context 'when a single value is too long' do
+      let(:env) { { name: 'name', a: 'a' * 1000, b: 'b' } }
+
+      it 'preserves name' do
+        expect(truncator.document[:env][:name]).to be == 'name'
+      end
+
+      it 'removes the too-long entry and keeps name' do
+        expect(truncator.document[:env].keys).to be == %w[ name b ]
+      end
+
+      it_behaves_like 'a truncated document'
+    end
+
+    context 'when multiple values are too long' do
+      let(:env) { { name: 'name', a: 'a' * 1000, b: 'b', c: 'c' * 1000, d: 'd' } }
+
+      it 'preserves name' do
+        expect(truncator.document[:env][:name]).to be == 'name'
+      end
+
+      it 'removes all other entries until size is satisifed' do
+        expect(truncator.document[:env].keys).to be == %w[ name d ]
+      end
+
+      it_behaves_like 'a truncated document'
+    end
+  end
+
+  context 'when modifying os is sufficient' do
+    context 'when a single value is too long' do
+      let(:os) { { type: 'type', a: 'a' * 1000, b: 'b' } }
+
+      it 'truncates env' do
+        expect(truncator.document[:env].keys).to be == %w[ name ]
+      end
+
+      it 'preserves type' do
+        expect(truncator.document[:os][:type]).to be == 'type'
+      end
+
+      it 'removes the too-long entry and keeps type' do
+        expect(truncator.document[:os].keys).to be == %w[ type b ]
+      end
+
+      it_behaves_like 'a truncated document'
+    end
+
+    context 'when multiple values are too long' do
+      let(:os) { { type: 'type', a: 'a' * 1000, b: 'b', c: 'c' * 1000, d: 'd' } }
+
+      it 'truncates env' do
+        expect(truncator.document[:env].keys).to be == %w[ name ]
+      end
+
+      it 'preserves type' do
+        expect(truncator.document[:os][:type]).to be == 'type'
+      end
+
+      it 'removes all other entries until size is satisifed' do
+        expect(truncator.document[:os].keys).to be == %w[ type d ]
+      end
+
+      it_behaves_like 'a truncated document'
+    end
+  end
+
+  context 'when truncating os is insufficient' do
+    let(:env) { { name: 'n' * 1000 } }
+
+    it 'truncates os' do
+      expect(truncator.document[:os].keys).to be == %w[ type ]
+    end
+
+    it 'removes env' do
+      expect(truncator.document.key?(:env)).to be false
+    end
+
+    it_behaves_like 'a truncated document'
+  end
+
+  context 'when platform is too long' do
+    let(:platform) { 'n' * 1000 }
+
+    it 'truncates os' do
+      expect(truncator.document[:os].keys).to be == %w[ type ]
+    end
+
+    it 'removes env' do
+      expect(truncator.document.key?(:env)).to be false
+    end
+
+    it 'truncates platform' do
+      expect(truncator.document[:platform].length).to be < 1000
+    end
+  end
+end
diff --git a/spec/mongo/server/app_metadata_spec.rb b/spec/mongo/server/app_metadata_spec.rb
index 72ea2a989b..8422a4f472 100644
--- a/spec/mongo/server/app_metadata_spec.rb
+++ b/spec/mongo/server/app_metadata_spec.rb
@@ -4,6 +4,7 @@
 require 'spec_helper'
 
 describe Mongo::Server::AppMetadata do
+  let(:max_size) { described_class::Truncator::MAX_DOCUMENT_SIZE }
 
   let(:app_metadata) do
     described_class.new(cluster.options)
@@ -13,14 +14,6 @@
     authorized_client.cluster
   end
 
-  describe 'MAX_DOCUMENT_SIZE' do
-    it 'should be 512 bytes' do
-      # This test is an additional check that MAX_DOCUMENT_SIZE
-      # has not been accidentially changed.
-      expect(described_class::MAX_DOCUMENT_SIZE).to eq(512)
-    end
-  end
-
   describe '#initialize' do
 
     context 'when the cluster has an app name option set' do
@@ -34,7 +27,7 @@
       end
 
       it 'sets the app name' do
-        expect(app_metadata.send(:full_client_document)[:application][:name]).to eq('app_metadata_test')
+        expect(app_metadata.client_document[:application][:name]).to eq('app_metadata_test')
       end
 
       context 'when the app name exceeds the max length of 128' do
@@ -58,77 +51,70 @@
     context 'when the cluster does not have an app name option set' do
 
       it 'does not set the app name' do
-        expect(app_metadata.send(:full_client_document)[:application]).to be(nil)
+        expect(app_metadata.client_document[:application]).to be(nil)
       end
     end
 
     context 'when the client document exceeds the max of 512 bytes' do
-      # Server api parameters change metadata length
-      require_no_required_api_version
-
-      context 'when the os.type length is too long' do
-
-        before do
-          allow(app_metadata).to receive(:type).and_return('x'*500)
+      shared_examples_for 'a truncated document' do
+        it 'is too long before validation' do
+          expect(app_metadata.client_document.to_bson.to_s.size).to be > max_size
         end
 
-        it 'truncates the document' do
-          expect(
-            app_metadata.validated_document.to_bson.to_s.size
-          ).to be < described_class::MAX_DOCUMENT_SIZE
+        it 'is acceptable after validation' do
+          app_metadata.validated_document # force validation
+          expect(app_metadata.client_document.to_bson.to_s.size).to be <= max_size
         end
       end
 
       context 'when the os.name length is too long' do
-
         before do
           allow(app_metadata).to receive(:name).and_return('x'*500)
         end
 
-        it 'truncates the document' do
-          expect(
-            app_metadata.validated_document.to_bson.to_s.size
-          ).to be < described_class::MAX_DOCUMENT_SIZE
-        end
+        it_behaves_like 'a truncated document'
       end
 
       context 'when the os.architecture length is too long' do
-
         before do
           allow(app_metadata).to receive(:architecture).and_return('x'*500)
         end
 
-        it 'truncates the document' do
-          expect(
-            app_metadata.validated_document.to_bson.to_s.size
-          ).to be < described_class::MAX_DOCUMENT_SIZE
-        end
+        it_behaves_like 'a truncated document'
       end
 
       context 'when the platform length is too long' do
-
         before do
           allow(app_metadata).to receive(:platform).and_return('x'*500)
         end
 
-        it 'truncates the document' do
-          expect(
-            app_metadata.validated_document.to_bson.to_s.size
-          ).to be < described_class::MAX_DOCUMENT_SIZE
-        end
+        it_behaves_like 'a truncated document'
       end
+    end
 
-      context 'when the driver info is too long' do
-        require_no_compression
+    context 'when run outside of a FaaS environment' do
+      it 'should exclude the :env key from the client document' do
+        expect(app_metadata.client_document.key?(:env)).to be false
+      end
+    end
 
-        before do
-          allow(app_metadata).to receive(:driver_doc).and_return('x'*500)
+    context 'when run inside of a FaaS environment' do
+      context 'when the environment is invalid' do
+        # invalid, because it is missing the other required fields
+        local_env('AWS_EXECUTION_ENV' => 'AWS_Lambda_ruby2.7')
+
+        it 'should exclude the :env key from the client document' do
+          expect(app_metadata.client_document.key?(:env)).to be false
         end
+      end
+
+      context 'when the environment is valid' do
+        # valid, because Azure requires only the one field
+        local_env('FUNCTIONS_WORKER_RUNTIME' => 'ruby')
 
-        it 'truncates the document' do
-          expect(
-            app_metadata.validated_document.to_bson.to_s.size
-          ).to be < described_class::MAX_DOCUMENT_SIZE
+        it 'should include the :env key in the client document' do
+          expect(app_metadata.client_document.key?(:env)).to be true
+          expect(app_metadata.client_document[:env][:name]).to be == "azure.func"
         end
       end
     end
diff --git a/spec/mongo/socket/ssl_spec.rb b/spec/mongo/socket/ssl_spec.rb
index aa02a428d3..c9aabb65b4 100644
--- a/spec/mongo/socket/ssl_spec.rb
+++ b/spec/mongo/socket/ssl_spec.rb
@@ -592,14 +592,8 @@
         )
       end
 
-      around do |example|
-        saved = ENV['SSL_CERT_FILE']
-        ENV['SSL_CERT_FILE'] = SpecConfig.instance.local_ca_cert_path
-        begin
-          example.run
-        ensure
-          ENV['SSL_CERT_FILE'] = saved
-        end
+      local_env do
+        { 'SSL_CERT_FILE' => SpecConfig.instance.local_ca_cert_path }
       end
 
       it 'uses the default cert store' do
diff --git a/spec/runners/crud/requirement.rb b/spec/runners/crud/requirement.rb
index d8e98169b2..88b35d0c95 100644
--- a/spec/runners/crud/requirement.rb
+++ b/spec/runners/crud/requirement.rb
@@ -109,9 +109,9 @@ def satisfied?
           end
         end
         if @auth == true
-          ok &&= cc.auth_enabled?
+          ok &&= SpecConfig.instance.auth?
         elsif @auth == false
-          ok &&= !cc.auth_enabled?
+          ok &&= !SpecConfig.instance.auth?
         end
         if @csfle
           ok &&= !!(ENV['LIBMONGOCRYPT_PATH'] || ENV['FLE'])
diff --git a/spec/shared b/spec/shared
index a8eadfb319..81298ee8ee 160000
--- a/spec/shared
+++ b/spec/shared
@@ -1 +1 @@
-Subproject commit a8eadfb31900d8fa5d4fa03fc4869ed5b119914b
+Subproject commit 81298ee8ee4bac7f446abcbd13045332396d05fe
diff --git a/spec/solo/clean_exit_spec.rb b/spec/solo/clean_exit_spec.rb
index 12d5d66886..003dd38057 100644
--- a/spec/solo/clean_exit_spec.rb
+++ b/spec/solo/clean_exit_spec.rb
@@ -2,18 +2,11 @@
 # encoding: utf-8
 
 require 'mongo'
+require 'lite_spec_helper'
 
 describe 'Clean exit' do
-
-  before(:all) do
-    unless %w(1 true yes).include?(ENV['SOLO'])
-      skip 'Set SOLO=1 in environment to run solo tests'
-    end
-
-    if %w(1 true yes).include?(ENV['EXTERNAL_DISABLED'])
-      skip "Test requires external connectivity"
-    end
-  end
+  require_external_connectivity
+  require_solo
 
   context 'with SRV URI' do
 
diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml b/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml
index fa4360c074..e6289047bf 100644
--- a/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml
+++ b/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml
@@ -2,7 +2,8 @@ description: "change-streams-showExpandedEvents"
 schemaVersion: "1.7"
 runOnRequirements:
   - minServerVersion: "6.0.0"
-    topologies: [ replicaset, sharded-replicaset, sharded ]
+    topologies: [ replicaset, sharded ]
+    serverless: forbid
 createEntities:
   - client:
       id: &client0 client0
@@ -160,7 +161,15 @@ tests:
       - name: createChangeStream
         object: *collection0
         arguments:
-          pipeline: []
+          pipeline:
+            # On sharded clusters, the create command run when loading initial
+            # data sometimes is still reported in the change stream. To avoid
+            # this, we exclude the create command when creating the change
+            # stream, but specifically don't exclude other events to still catch
+            # driver errors.
+            - $match:
+                operationType:
+                  $ne: create
           showExpandedEvents: true
         saveResultAsEntity: &changeStream0 changeStream0
       - name: createIndex
@@ -259,9 +268,7 @@ tests:
         arguments:
           command:
             collMod: *collection0
-          # Added here to fix our tests as we require a commandName but don't
-          # do anything with it.
-          commandName: modify_collection
+          commandName: collMod
       - name: iterateUntilDocumentOrError
         object: *changeStream0
         expectResult:
@@ -269,7 +276,8 @@ tests:
 
   - description: "when showExpandedEvents is true, shardCollection events are reported"
     runOnRequirements:
-      - topologies: [ sharded-replicaset, sharded ]
+      # Note: minServerVersion is specified in top-level runOnRequirements
+      - topologies: [ sharded ]
     operations:
       - name: dropCollection
         object: *shardedDb
@@ -292,6 +300,7 @@ tests:
             shardCollection: shardedDb.shardedCollection
             key:
               _id: 1
+          commandName: shardCollection
       - name: iterateUntilDocumentOrError
         object: *changeStream0
         expectResult:
diff --git a/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml b/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml
index 570fb5a547..adeab99b18 100644
--- a/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml
+++ b/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml
@@ -93,6 +93,8 @@ tests:
                 payload: { $$exists: false }
 
   - description: "getnonce"
+    runOnRequirements:
+    - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007
     operations:
       - name: runCommand
         object: *database
@@ -159,6 +161,8 @@ tests:
                 roles: { $$exists: false }
 
   - description: "copydbgetnonce"
+    runOnRequirements:
+    - maxServerVersion: 3.6.99 # copydbgetnonce was removed as of 4.0 via SERVER-32276
     operations:
       - name: runCommand
         object: *database
@@ -176,6 +180,8 @@ tests:
               command: { copydbgetnonce: { $$exists: false } }
 
   - description: "copydbsaslstart"
+    runOnRequirements:
+    - maxServerVersion: 4.0.99 # copydbsaslstart was removed as of 4.2 via SERVER-36211
     operations:
       - name: runCommand
         object: *database
@@ -193,6 +199,8 @@ tests:
               command: { copydbsaslstart: { $$exists: false } }
 
   - description: "copydb"
+    runOnRequirements:
+    - maxServerVersion: 4.0.99 # copydb was removed as of 4.2 via SERVER-36257
     operations:
       - name: runCommand
         object: *database
diff --git a/spec/support/aws_utils.rb b/spec/support/aws_utils.rb
index 9dc2ec410c..e66e3ed353 100644
--- a/spec/support/aws_utils.rb
+++ b/spec/support/aws_utils.rb
@@ -1,16 +1,16 @@
 # frozen_string_literal: true
-# encoding: utf-8
 
 autoload :Byebug, 'byebug'
 autoload :Paint, 'paint'
 
+require 'aws-sdk-core'
+
 module Aws
   autoload :CloudWatchLogs, 'aws-sdk-cloudwatchlogs'
-  autoload :Credentials, 'aws-sdk-core'
   autoload :EC2, 'aws-sdk-ec2'
   autoload :ECS, 'aws-sdk-ecs'
   autoload :IAM, 'aws-sdk-iam'
-  autoload :STS, 'aws-sdk-core'
+  autoload :STS, 'aws-sdk-sts'
 end
 
 module AwsUtils
diff --git a/spec/support/certificates/atlas-ocsp-ca.crt b/spec/support/certificates/atlas-ocsp-ca.crt
index 06fc1a64a3..1712595dec 100644
--- a/spec/support/certificates/atlas-ocsp-ca.crt
+++ b/spec/support/certificates/atlas-ocsp-ca.crt
@@ -2,53 +2,53 @@ Certificate:
     Data:
         Version: 3 (0x2)
         Serial Number:
-            06:d8:d9:04:d5:58:43:46:f6:8a:2f:a7:54:22:7e:c4
+            0c:f5:bd:06:2b:56:02:f4:7a:b8:50:2c:23:cc:f0:66
         Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
+        Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G2
         Validity
-            Not Before: Apr 14 00:00:00 2021 GMT
-            Not After : Apr 13 23:59:59 2031 GMT
-        Subject: C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
+            Not Before: Mar 30 00:00:00 2021 GMT
+            Not After : Mar 29 23:59:59 2031 GMT
+        Subject: C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 Public-Key: (2048 bit)
                 Modulus:
-                    00:c1:4b:b3:65:47:70:bc:dd:4f:58:db:ec:9c:ed:
-                    c3:66:e5:1f:31:13:54:ad:4a:66:46:1f:2c:0a:ec:
-                    64:07:e5:2e:dc:dc:b9:0a:20:ed:df:e3:c4:d0:9e:
-                    9a:a9:7a:1d:82:88:e5:11:56:db:1e:9f:58:c2:51:
-                    e7:2c:34:0d:2e:d2:92:e1:56:cb:f1:79:5f:b3:bb:
-                    87:ca:25:03:7b:9a:52:41:66:10:60:4f:57:13:49:
-                    f0:e8:37:67:83:df:e7:d3:4b:67:4c:22:51:a6:df:
-                    0e:99:10:ed:57:51:74:26:e2:7d:c7:ca:62:2e:13:
-                    1b:7f:23:88:25:53:6f:c1:34:58:00:8b:84:ff:f8:
-                    be:a7:58:49:22:7b:96:ad:a2:88:9b:15:bc:a0:7c:
-                    df:e9:51:a8:d5:b0:ed:37:e2:36:b4:82:4b:62:b5:
-                    49:9a:ec:c7:67:d6:e3:3e:f5:e3:d6:12:5e:44:f1:
-                    bf:71:42:7d:58:84:03:80:b1:81:01:fa:f9:ca:32:
-                    bb:b4:8e:27:87:27:c5:2b:74:d4:a8:d6:97:de:c3:
-                    64:f9:ca:ce:53:a2:56:bc:78:17:8e:49:03:29:ae:
-                    fb:49:4f:a4:15:b9:ce:f2:5c:19:57:6d:6b:79:a7:
-                    2b:a2:27:20:13:b5:d0:3d:40:d3:21:30:07:93:ea:
-                    99:f5
+                    00:cc:f7:10:62:4f:a6:bb:63:6f:ed:90:52:56:c5:
+                    6d:27:7b:7a:12:56:8a:f1:f4:f9:d6:e7:e1:8f:bd:
+                    95:ab:f2:60:41:15:70:db:12:00:fa:27:0a:b5:57:
+                    38:5b:7d:b2:51:93:71:95:0e:6a:41:94:5b:35:1b:
+                    fa:7b:fa:bb:c5:be:24:30:fe:56:ef:c4:f3:7d:97:
+                    e3:14:f5:14:4d:cb:a7:10:f2:16:ea:ab:22:f0:31:
+                    22:11:61:69:90:26:ba:78:d9:97:1f:e3:7d:66:ab:
+                    75:44:95:73:c8:ac:ff:ef:5d:0a:8a:59:43:e1:ac:
+                    b2:3a:0f:f3:48:fc:d7:6b:37:c1:63:dc:de:46:d6:
+                    db:45:fe:7d:23:fd:90:e8:51:07:1e:51:a3:5f:ed:
+                    49:46:54:7f:2c:88:c5:f4:13:9c:97:15:3c:03:e8:
+                    a1:39:dc:69:0c:32:c1:af:16:57:4c:94:47:42:7c:
+                    a2:c8:9c:7d:e6:d4:4d:54:af:42:99:a8:c1:04:c2:
+                    77:9c:d6:48:e4:ce:11:e0:2a:80:99:f0:43:70:cf:
+                    3f:76:6b:d1:4c:49:ab:24:5e:c2:0d:82:fd:46:a8:
+                    ab:6c:93:cc:62:52:42:75:92:f8:9a:fa:5e:5e:b2:
+                    b0:61:e5:1f:1f:b9:7f:09:98:e8:3d:fa:83:7f:47:
+                    69:a1
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: critical
                 CA:TRUE, pathlen:0
             X509v3 Subject Key Identifier: 
-                B7:6B:A2:EA:A8:AA:84:8C:79:EA:B4:DA:0F:98:B2:C5:95:76:B9:F4
+                74:85:80:C0:66:C7:DF:37:DE:CF:BD:29:37:AA:03:1D:BE:ED:CD:17
             X509v3 Authority Key Identifier: 
-                03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55
+                4E:22:54:20:18:95:E6:E3:6E:E6:0F:FA:FA:B9:12:ED:06:17:8F:39
             X509v3 Key Usage: critical
                 Digital Signature, Certificate Sign, CRL Sign
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication, TLS Web Client Authentication
             Authority Information Access: 
                 OCSP - URI:http://ocsp.digicert.com
-                CA Issuers - URI:http://cacerts.digicert.com/DigiCertGlobalRootCA.crt
+                CA Issuers - URI:http://cacerts.digicert.com/DigiCertGlobalRootG2.crt
             X509v3 CRL Distribution Points: 
                 Full Name:
-                  URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl
+                  URI:http://crl3.digicert.com/DigiCertGlobalRootG2.crl
             X509v3 Certificate Policies: 
                 Policy: 2.16.840.1.114412.2.1
                 Policy: 2.23.140.1.1
@@ -57,47 +57,47 @@ Certificate:
                 Policy: 2.23.140.1.2.3
     Signature Algorithm: sha256WithRSAEncryption
     Signature Value:
-        80:32:ce:5e:0b:dd:6e:5a:0d:0a:af:e1:d6:84:cb:c0:8e:fa:
-        85:70:ed:da:5d:b3:0c:f7:2b:75:40:fe:85:0a:fa:f3:31:78:
-        b7:70:4b:1a:89:58:ba:80:bd:f3:6b:1d:e9:7e:cf:0b:ba:58:
-        9c:59:d4:90:d3:fd:6c:fd:d0:98:6d:b7:71:82:5b:cf:6d:0b:
-        5a:09:d0:7b:de:c4:43:d8:2a:a4:de:9e:41:26:5f:bb:8f:99:
-        cb:dd:ae:e1:a8:6f:9f:87:fe:74:b7:1f:1b:20:ab:b1:4f:c6:
-        f5:67:5d:5d:9b:3c:e9:ff:69:f7:61:6c:d6:d9:f3:fd:36:c6:
-        ab:03:88:76:d2:4b:2e:75:86:e3:fc:d8:55:7d:26:c2:11:77:
-        df:3e:02:b6:7c:f3:ab:7b:7a:86:36:6f:b8:f7:d8:93:71:cf:
-        86:df:73:30:fa:7b:ab:ed:2a:59:c8:42:84:3b:11:17:1a:52:
-        f3:c9:0e:14:7d:a2:5b:72:67:ba:71:ed:57:47:66:c5:b8:02:
-        4a:65:34:5e:8b:d0:2a:3c:20:9c:51:99:4c:e7:52:9e:f7:6b:
-        11:2b:0d:92:7e:1d:e8:8a:eb:36:16:43:87:ea:2a:63:bf:75:
-        3f:eb:de:c4:03:bb:0a:3c:f7:30:ef:eb:af:4c:fc:8b:36:10:
-        73:3e:f3:a4
+        90:f1:70:cb:28:97:69:97:7c:74:fd:c0:fa:26:7b:53:ab:ad:
+        cd:65:fd:ba:9c:06:9c:8a:d7:5a:43:87:ed:4d:4c:56:5f:ad:
+        c1:c5:b5:05:20:2e:59:d1:ff:4a:f5:a0:2a:d8:b0:95:ad:c9:
+        2e:4a:3b:d7:a7:f6:6f:88:29:fc:30:3f:24:84:bb:c3:b7:7b:
+        93:07:2c:af:87:6b:76:33:ed:00:55:52:b2:59:9e:e4:b9:d0:
+        f3:df:e7:0f:fe:dd:f8:c4:b9:10:72:81:09:04:5f:cf:97:9e:
+        2e:32:75:8e:cf:9a:58:d2:57:31:7e:37:01:81:b2:66:6d:29:
+        1a:b1:66:09:6d:d1:6e:90:f4:b9:fa:2f:01:14:c5:5c:56:64:
+        01:d9:7d:87:a8:38:53:9f:8b:5d:46:6d:5c:c6:27:84:81:d4:
+        7e:8c:8c:a3:9b:52:e7:c6:88:ec:37:7c:2a:fb:f0:55:5a:38:
+        72:10:d8:00:13:cf:4c:73:db:aa:37:35:a8:29:81:69:9c:76:
+        bc:de:18:7b:90:d4:ca:cf:ef:67:03:fd:04:5a:21:16:b1:ff:
+        ea:3f:df:dc:82:f5:eb:f4:59:92:23:0d:24:2a:95:25:4c:ca:
+        a1:91:e6:d4:b7:ac:87:74:b3:f1:6d:a3:99:db:f9:d5:bd:84:
+        40:9f:07:98
 
 -----BEGIN CERTIFICATE-----
-MIIEvjCCA6agAwIBAgIQBtjZBNVYQ0b2ii+nVCJ+xDANBgkqhkiG9w0BAQsFADBh
+MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh
 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaME8xCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBS
-U0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6a
-qXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddn
-g9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuW
-raKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB
-Afr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21r
-eacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBgjCCAX4wEgYDVR0TAQH/BAgwBgEB
-/wIBADAdBgNVHQ4EFgQUt2ui6qiqhIx56rTaD5iyxZV2ufQwHwYDVR0jBBgwFoAU
-A95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
-CCsGAQUFBwMBBggrBgEFBQcDAjB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGG
-GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2Nh
-Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDBCBgNV
-HR8EOzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRH
-bG9iYWxSb290Q0EuY3JsMD0GA1UdIAQ2MDQwCwYJYIZIAYb9bAIBMAcGBWeBDAEB
-MAgGBmeBDAECATAIBgZngQwBAgIwCAYGZ4EMAQIDMA0GCSqGSIb3DQEBCwUAA4IB
-AQCAMs5eC91uWg0Kr+HWhMvAjvqFcO3aXbMM9yt1QP6FCvrzMXi3cEsaiVi6gL3z
-ax3pfs8LulicWdSQ0/1s/dCYbbdxglvPbQtaCdB73sRD2Cqk3p5BJl+7j5nL3a7h
-qG+fh/50tx8bIKuxT8b1Z11dmzzp/2n3YWzW2fP9NsarA4h20ksudYbj/NhVfSbC
-EXffPgK2fPOre3qGNm+499iTcc+G33Mw+nur7SpZyEKEOxEXGlLzyQ4UfaJbcme6
-ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697E
-A7sKPPcw7+uvTPyLNhBzPvOk
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0yMTAzMzAwMDAwMDBaFw0zMTAzMjkyMzU5NTlaMFkxCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2Jh
+bCBHMiBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMz3EGJPprtjb+2QUlbFbSd7ehJWivH0+dbn4Y+9lavyYEEV
+cNsSAPonCrVXOFt9slGTcZUOakGUWzUb+nv6u8W+JDD+Vu/E832X4xT1FE3LpxDy
+FuqrIvAxIhFhaZAmunjZlx/jfWardUSVc8is/+9dCopZQ+GssjoP80j812s3wWPc
+3kbW20X+fSP9kOhRBx5Ro1/tSUZUfyyIxfQTnJcVPAPooTncaQwywa8WV0yUR0J8
+osicfebUTVSvQpmowQTCd5zWSOTOEeAqgJnwQ3DPP3Zr0UxJqyRewg2C/Uaoq2yT
+zGJSQnWS+Jr6Xl6ysGHlHx+5fwmY6D36g39HaaECAwEAAaOCAYIwggF+MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHSFgMBmx9833s+9KTeqAx2+7c0XMB8G
+A1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA4GA1UdDwEB/wQEAwIBhjAd
+BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYIKwYBBQUHAQEEajBoMCQG
+CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKG
+NGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RH
+Mi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
+L0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDA9BgNVHSAENjA0MAsGCWCGSAGG/WwC
+ATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgGBmeBDAECAzANBgkqhkiG
+9w0BAQsFAAOCAQEAkPFwyyiXaZd8dP3A+iZ7U6utzWX9upwGnIrXWkOH7U1MVl+t
+wcW1BSAuWdH/SvWgKtiwla3JLko716f2b4gp/DA/JIS7w7d7kwcsr4drdjPtAFVS
+slme5LnQ89/nD/7d+MS5EHKBCQRfz5eeLjJ1js+aWNJXMX43AYGyZm0pGrFmCW3R
+bpD0ufovARTFXFZkAdl9h6g4U5+LXUZtXMYnhIHUfoyMo5tS58aI7Dd8KvvwVVo4
+chDYABPPTHPbqjc1qCmBaZx2vN4Ye5DUys/vZwP9BFohFrH/6j/f3IL16/RZkiMN
+JCqVJUzKoZHm1Lesh3Sz8W2jmdv51b2EQJ8HmA==
 -----END CERTIFICATE-----
\ No newline at end of file
diff --git a/spec/support/certificates/atlas-ocsp.crt b/spec/support/certificates/atlas-ocsp.crt
index 44ca65c775..de51c17cef 100644
--- a/spec/support/certificates/atlas-ocsp.crt
+++ b/spec/support/certificates/atlas-ocsp.crt
@@ -2,41 +2,41 @@ Certificate:
     Data:
         Version: 3 (0x2)
         Serial Number:
-            0e:fb:8b:f4:73:3e:98:df:1d:48:6d:da:d2:08:ca:0d
+            03:a8:39:03:e8:84:cd:24:1c:9c:b1:27:8c:0a:7a:9b
         Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
+        Issuer: C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
         Validity
-            Not Before: May  5 00:00:00 2022 GMT
-            Not After : Jun  2 23:59:59 2023 GMT
+            Not Before: Jun  2 00:00:00 2023 GMT
+            Not After : Jun  4 23:59:59 2024 GMT
         Subject: C = US, ST = New York, L = New York, O = "MongoDB, Inc.", CN = *.mongodb-dev.net
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 Public-Key: (2048 bit)
                 Modulus:
-                    00:b3:40:eb:4a:26:af:07:a3:ea:58:c7:3b:50:5c:
-                    94:47:5a:0a:9d:86:b7:bb:03:9c:5a:4a:23:5e:7c:
-                    b1:cd:fc:4f:56:d5:05:4e:ce:f8:f2:b1:55:09:13:
-                    ff:cc:e6:a4:fb:41:2e:0c:8f:1a:a3:ea:4b:a3:51:
-                    c6:f7:91:e6:47:49:ae:3b:94:df:68:80:68:2a:62:
-                    a2:d1:42:5b:89:c1:70:2b:1c:a7:84:df:98:86:1a:
-                    69:eb:88:c4:51:4d:c9:7f:10:90:a0:0a:c5:7c:07:
-                    12:9e:c2:94:e1:88:2e:2d:21:94:a4:8a:27:1f:20:
-                    1b:6f:14:27:21:0c:4b:6f:72:5b:18:e7:75:7a:90:
-                    4c:a8:18:e7:4d:0f:ed:e4:03:f4:10:b7:65:23:9f:
-                    b2:5f:0d:eb:85:3c:b0:34:d3:9a:2c:80:85:2e:53:
-                    35:19:44:03:13:b3:df:fb:0d:db:80:6f:12:60:1a:
-                    b8:d6:25:0d:6d:23:fd:a6:d8:aa:dc:82:73:cc:d2:
-                    26:9b:71:50:9a:09:cf:ef:60:ba:d1:22:cb:fb:2f:
-                    69:a2:a6:c6:a9:30:77:1c:d7:bc:c2:6e:6d:e6:69:
-                    65:f3:91:3d:ee:04:f9:a0:41:5f:50:dc:75:15:4d:
-                    15:43:76:35:e9:97:65:7a:d8:3f:00:4d:44:29:7c:
-                    5d:25
+                    00:df:91:d8:18:9b:41:9c:83:33:58:e9:c2:42:00:
+                    5a:a5:c0:61:e8:df:39:20:46:33:ae:87:34:c4:13:
+                    89:d2:c9:4c:eb:6f:a5:cd:dc:31:07:b4:cf:53:74:
+                    9b:3c:32:e8:df:4b:64:88:a2:03:87:e9:27:f9:7b:
+                    57:ca:be:7b:51:0f:f2:b1:78:45:0e:5f:06:e9:0a:
+                    c9:fe:67:d0:19:3c:8c:41:01:5f:b7:66:a0:a4:3b:
+                    54:18:c2:5b:5e:b1:d7:9e:6a:78:f3:bd:24:1d:fc:
+                    ce:43:d2:74:60:83:f4:f3:55:a4:dd:82:f0:e9:f1:
+                    3d:ff:d1:b6:32:0f:c8:e4:aa:95:bf:52:33:8f:2f:
+                    be:28:ea:ea:cf:a7:13:65:7e:3c:61:2c:38:a0:95:
+                    45:b8:2d:7e:20:33:38:36:fd:6f:c8:a8:e2:ad:5b:
+                    3c:91:95:d9:ec:0b:08:7d:81:25:0c:b9:40:6b:97:
+                    8a:d1:c4:f9:4d:9b:45:57:03:b9:5c:03:90:d0:68:
+                    29:50:12:87:59:48:f0:19:c3:4d:42:3e:80:c3:4e:
+                    7e:30:e1:bb:fe:f8:30:55:83:15:ca:af:12:13:3a:
+                    da:e9:91:a3:10:6d:fc:b6:64:5d:89:a3:b4:18:9a:
+                    7e:ab:90:dd:11:fa:db:af:98:12:58:3c:8f:47:22:
+                    f2:53
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Authority Key Identifier: 
-                B7:6B:A2:EA:A8:AA:84:8C:79:EA:B4:DA:0F:98:B2:C5:95:76:B9:F4
+                74:85:80:C0:66:C7:DF:37:DE:CF:BD:29:37:AA:03:1D:BE:ED:CD:17
             X509v3 Subject Key Identifier: 
-                22:7C:2F:C7:F4:D6:75:0E:A1:02:32:21:1A:A6:29:25:46:48:B8:6A
+                97:FB:A6:29:30:AE:9D:F0:D9:85:05:12:AA:D4:C1:D9:EC:0F:5B:CE
             X509v3 Subject Alternative Name: 
                 DNS:*.mongodb-dev.net, DNS:mongodb-dev.net
             X509v3 Key Usage: critical
@@ -45,108 +45,108 @@ Certificate:
                 TLS Web Server Authentication, TLS Web Client Authentication
             X509v3 CRL Distribution Points: 
                 Full Name:
-                  URI:http://crl3.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl
+                  URI:http://crl3.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl
                 Full Name:
-                  URI:http://crl4.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl
+                  URI:http://crl4.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl
             X509v3 Certificate Policies: 
                 Policy: 2.23.140.1.2.2
                   CPS: http://www.digicert.com/CPS
             Authority Information Access: 
                 OCSP - URI:http://ocsp.digicert.com
-                CA Issuers - URI:http://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1-1.crt
+                CA Issuers - URI:http://cacerts.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt
             X509v3 Basic Constraints: 
                 CA:FALSE
             CT Precertificate SCTs: 
                 Signed Certificate Timestamp:
                     Version   : v1 (0x0)
-                    Log ID    : E8:3E:D0:DA:3E:F5:06:35:32:E7:57:28:BC:89:6B:C9:
-                                03:D3:CB:D1:11:6B:EC:EB:69:E1:77:7D:6D:06:BD:6E
-                    Timestamp : May  5 16:12:54.726 2022 GMT
+                    Log ID    : EE:CD:D0:64:D5:DB:1A:CE:C5:5C:B7:9D:B4:CD:13:A2:
+                                32:87:46:7C:BC:EC:DE:C3:51:48:59:46:71:1F:B5:9B
+                    Timestamp : Jun  2 08:44:16.090 2023 GMT
                     Extensions: none
                     Signature : ecdsa-with-SHA256
-                                30:45:02:21:00:C4:D8:1A:00:3C:BD:E5:E1:13:B2:5D:
-                                DA:7F:69:A9:4C:83:AB:CC:C2:00:9F:FB:98:09:44:5F:
-                                93:0C:92:CD:F7:02:20:2D:E2:D6:A4:3B:61:06:25:1E:
-                                22:2F:5E:2A:D4:76:0D:FE:12:E9:91:65:26:69:25:6C:
-                                53:E4:C3:2F:4B:89:AF
+                                30:46:02:21:00:88:AC:04:93:84:2C:2C:99:4F:B8:C4:
+                                06:B5:7F:9B:92:D3:51:25:72:1D:DB:12:D5:C0:AA:8F:
+                                D1:97:39:EF:4C:02:21:00:96:B1:11:40:0F:BA:03:28:
+                                88:BF:0C:DF:4B:4A:68:67:05:97:7E:F6:BD:6F:F3:0D:
+                                D8:9C:EB:90:75:FF:79:AB
                 Signed Certificate Timestamp:
                     Version   : v1 (0x0)
-                    Log ID    : 35:CF:19:1B:BF:B1:6C:57:BF:0F:AD:4C:6D:42:CB:BB:
-                                B6:27:20:26:51:EA:3F:E1:2A:EF:A8:03:C3:3B:D6:4C
-                    Timestamp : May  5 16:12:54.786 2022 GMT
+                    Log ID    : 48:B0:E3:6B:DA:A6:47:34:0F:E5:6A:02:FA:9D:30:EB:
+                                1C:52:01:CB:56:DD:2C:81:D9:BB:BF:AB:39:D8:84:73
+                    Timestamp : Jun  2 08:44:16.066 2023 GMT
                     Extensions: none
                     Signature : ecdsa-with-SHA256
-                                30:45:02:20:46:F0:9E:AE:32:A7:A7:CA:22:C6:64:EE:
-                                7F:9F:C6:EA:3C:BE:3B:2F:E2:09:C0:ED:A9:E1:EE:16:
-                                F9:F1:6C:1E:02:21:00:BD:1A:42:73:A5:CE:E6:8A:CE:
-                                BC:4C:3E:42:D4:0B:FC:89:3B:D5:1E:29:E8:38:F7:44:
-                                5B:22:9A:FA:1D:B3:57
+                                30:45:02:21:00:B0:C8:4E:75:71:84:DE:71:8F:D0:27:
+                                71:6C:4D:B1:E8:20:A3:54:AF:27:A8:A2:FA:AC:46:43:
+                                4A:08:C1:A9:94:02:20:1A:E1:63:79:E6:DB:4A:98:72:
+                                FF:BD:C4:A2:D8:F5:11:36:4C:95:6E:68:B4:CF:E7:35:
+                                84:BB:AC:A2:E6:D0:A0
                 Signed Certificate Timestamp:
                     Version   : v1 (0x0)
-                    Log ID    : B7:3E:FB:24:DF:9C:4D:BA:75:F2:39:C5:BA:58:F4:6C:
-                                5D:FC:42:CF:7A:9F:35:C4:9E:1D:09:81:25:ED:B4:99
-                    Timestamp : May  5 16:12:54.779 2022 GMT
+                    Log ID    : DA:B6:BF:6B:3F:B5:B6:22:9F:9B:C2:BB:5C:6B:E8:70:
+                                91:71:6C:BB:51:84:85:34:BD:A4:3D:30:48:D7:FB:AB
+                    Timestamp : Jun  2 08:44:16.030 2023 GMT
                     Extensions: none
                     Signature : ecdsa-with-SHA256
-                                30:45:02:20:2F:72:9B:CC:88:04:F1:3B:8B:BC:01:85:
-                                B2:16:B4:AF:4A:CB:45:E8:0B:29:D1:77:6C:2C:56:0C:
-                                8D:1C:61:7C:02:21:00:A4:67:AC:08:40:83:82:9E:22:
-                                75:3A:92:91:1E:12:23:01:B9:04:3B:A3:60:5F:E8:D1:
-                                59:4E:88:DB:C5:DE:DD
+                                30:44:02:20:51:59:38:3B:FF:76:EC:A2:3E:15:4B:60:
+                                23:84:20:88:66:C3:64:7C:94:51:B9:1A:BD:87:15:73:
+                                43:F5:96:BA:02:20:22:F3:7B:75:F4:58:58:C7:48:18:
+                                85:22:17:FC:ED:07:A2:8B:A0:07:0E:32:0F:16:9C:8B:
+                                E5:AA:6B:86:86:6B
     Signature Algorithm: sha256WithRSAEncryption
     Signature Value:
-        33:bb:fa:5f:59:e5:d0:5c:a1:22:4e:73:c8:35:e4:d7:8c:28:
-        67:b2:85:b0:1b:0c:e4:ec:aa:a7:49:09:e7:52:63:e5:8a:51:
-        a9:de:b8:35:74:66:0f:66:fa:b4:91:1f:b0:3a:5e:0a:21:0d:
-        a2:73:5b:1f:2c:fa:cd:59:72:85:8c:db:95:33:66:64:a2:e7:
-        6f:0d:9e:cf:a7:e3:de:82:7a:24:55:56:f8:b8:c0:95:0d:ba:
-        07:01:fe:ce:35:48:2f:23:2d:29:7b:6b:00:e6:f7:2c:31:6f:
-        9c:67:35:90:f0:65:9f:41:f9:bf:ad:2c:e9:8e:f0:c4:cf:8c:
-        5d:02:c4:8e:22:7f:62:bb:62:21:cf:9d:59:7b:96:4e:c4:7d:
-        ca:a2:6f:37:66:34:88:ac:a3:c1:6e:95:ec:e3:3a:07:27:6d:
-        31:b7:38:70:ce:93:42:86:20:3b:f8:d8:f9:ef:a9:9e:43:4f:
-        ed:9b:4b:c7:90:5b:d1:9b:16:66:8f:42:11:8a:97:bb:c7:e1:
-        b5:67:f5:a1:f4:7b:8b:f0:88:a8:5f:39:83:4c:2e:3d:e9:8f:
-        34:b8:fb:e8:1e:e2:0f:90:02:7c:ad:c7:9e:00:c2:fa:0a:41:
-        43:61:e2:2b:6d:f8:b7:fa:70:71:04:f3:2c:7f:c0:16:7a:6b:
-        a1:34:45:c0
+        be:97:77:21:55:1d:c7:18:5f:52:de:a8:85:a5:d2:ee:48:29:
+        b3:46:2d:31:02:12:5b:88:1e:09:ac:f4:7e:fc:4d:e4:e8:c9:
+        56:7e:de:1e:dc:53:1f:27:1b:dd:07:68:39:1c:2e:91:86:20:
+        2a:f0:9a:d1:ff:c4:b5:c2:85:a7:ec:9c:b8:62:31:aa:5b:69:
+        8e:37:d4:cc:ec:7b:86:86:b2:a9:6d:60:8b:70:44:ca:72:7a:
+        d9:a8:54:96:f8:5a:e5:2b:2a:48:5d:4f:53:fb:b3:c9:67:70:
+        a3:a6:d5:be:6d:99:11:51:89:31:e3:b1:43:54:e0:7c:cd:96:
+        fd:2d:12:12:eb:a3:8e:b4:12:25:23:bf:a6:5d:70:c5:3f:8c:
+        9f:0e:5d:38:d0:4b:aa:67:30:d7:c3:47:76:20:bd:1a:55:2a:
+        b2:df:37:92:67:88:65:11:15:62:a8:08:3f:93:ee:c1:b1:79:
+        33:03:03:0b:46:d4:5f:d2:82:31:77:6c:42:12:89:f4:f8:af:
+        32:ab:45:59:91:bf:82:44:69:d2:49:7f:1b:f3:49:28:37:85:
+        74:3c:4d:92:1b:56:30:4c:69:79:5d:fc:4d:a6:7b:c6:e7:f5:
+        34:e6:dd:c0:25:f8:69:b2:86:3a:44:a9:41:ba:34:cd:fe:aa:
+        20:d2:57:ad
 
 -----BEGIN CERTIFICATE-----
-MIIGxDCCBaygAwIBAgIQDvuL9HM+mN8dSG3a0gjKDTANBgkqhkiG9w0BAQsFADBP
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE
-aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMjA1MDUwMDAwMDBa
-Fw0yMzA2MDIyMzU5NTlaMGcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9y
-azERMA8GA1UEBxMITmV3IFlvcmsxFjAUBgNVBAoTDU1vbmdvREIsIEluYy4xGjAY
-BgNVBAMMESoubW9uZ29kYi1kZXYubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEAs0DrSiavB6PqWMc7UFyUR1oKnYa3uwOcWkojXnyxzfxPVtUFTs74
-8rFVCRP/zOak+0EuDI8ao+pLo1HG95HmR0muO5TfaIBoKmKi0UJbicFwKxynhN+Y
-hhpp64jEUU3JfxCQoArFfAcSnsKU4YguLSGUpIonHyAbbxQnIQxLb3JbGOd1epBM
-qBjnTQ/t5AP0ELdlI5+yXw3rhTywNNOaLICFLlM1GUQDE7Pf+w3bgG8SYBq41iUN
-bSP9ptiq3IJzzNImm3FQmgnP72C60SLL+y9poqbGqTB3HNe8wm5t5mll85E97gT5
-oEFfUNx1FU0VQ3Y16Zdletg/AE1EKXxdJQIDAQABo4IDgjCCA34wHwYDVR0jBBgw
-FoAUt2ui6qiqhIx56rTaD5iyxZV2ufQwHQYDVR0OBBYEFCJ8L8f01nUOoQIyIRqm
-KSVGSLhqMC0GA1UdEQQmMCSCESoubW9uZ29kYi1kZXYubmV0gg9tb25nb2RiLWRl
-di5uZXQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
-BQcDAjCBjwYDVR0fBIGHMIGEMECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5j
-b20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0ExLTQuY3JsMECgPqA8hjpodHRw
-Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0Ex
-LTQuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6
-Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB/BggrBgEFBQcBAQRzMHEwJAYIKwYBBQUH
-MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcwAoY9aHR0cDov
-L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENB
-MS0xLmNydDAJBgNVHRMEAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgDo
-PtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYCU/uZGAAAEAwBHMEUC
-IQDE2BoAPL3l4ROyXdp/aalMg6vMwgCf+5gJRF+TDJLN9wIgLeLWpDthBiUeIi9e
-KtR2Df4S6ZFlJmklbFPkwy9Lia8AdgA1zxkbv7FsV78PrUxtQsu7ticgJlHqP+Eq
-76gDwzvWTAAAAYCU/uaCAAAEAwBHMEUCIEbwnq4yp6fKIsZk7n+fxuo8vjsv4gnA
-7anh7hb58WweAiEAvRpCc6XO5orOvEw+QtQL/Ik71R4p6Dj3RFsimvods1cAdgC3
-Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmBJe20mQAAAYCU/uZ7AAAEAwBHMEUC
-IC9ym8yIBPE7i7wBhbIWtK9Ky0XoCynRd2wsVgyNHGF8AiEApGesCECDgp4idTqS
-kR4SIwG5BDujYF/o0VlOiNvF3t0wDQYJKoZIhvcNAQELBQADggEBADO7+l9Z5dBc
-oSJOc8g15NeMKGeyhbAbDOTsqqdJCedSY+WKUaneuDV0Zg9m+rSRH7A6XgohDaJz
-Wx8s+s1ZcoWM25UzZmSi528Nns+n496CeiRVVvi4wJUNugcB/s41SC8jLSl7awDm
-9ywxb5xnNZDwZZ9B+b+tLOmO8MTPjF0CxI4if2K7YiHPnVl7lk7EfcqibzdmNIis
-o8FulezjOgcnbTG3OHDOk0KGIDv42PnvqZ5DT+2bS8eQW9GbFmaPQhGKl7vH4bVn
-9aH0e4vwiKhfOYNMLj3pjzS4++ge4g+QAnytx54AwvoKQUNh4itt+Lf6cHEE8yx/
-wBZ6a6E0RcA=
+MIIG5zCCBc+gAwIBAgIQA6g5A+iEzSQcnLEnjAp6mzANBgkqhkiG9w0BAQsFADBZ
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTMwMQYDVQQDEypE
+aWdpQ2VydCBHbG9iYWwgRzIgVExTIFJTQSBTSEEyNTYgMjAyMCBDQTEwHhcNMjMw
+NjAyMDAwMDAwWhcNMjQwNjA0MjM1OTU5WjBnMQswCQYDVQQGEwJVUzERMA8GA1UE
+CBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYDVQQKEw1Nb25nb0RC
+LCBJbmMuMRowGAYDVQQDDBEqLm1vbmdvZGItZGV2Lm5ldDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAN+R2BibQZyDM1jpwkIAWqXAYejfOSBGM66HNMQT
+idLJTOtvpc3cMQe0z1N0mzwy6N9LZIiiA4fpJ/l7V8q+e1EP8rF4RQ5fBukKyf5n
+0Bk8jEEBX7dmoKQ7VBjCW16x155qePO9JB38zkPSdGCD9PNVpN2C8OnxPf/RtjIP
+yOSqlb9SM48vvijq6s+nE2V+PGEsOKCVRbgtfiAzODb9b8io4q1bPJGV2ewLCH2B
+JQy5QGuXitHE+U2bRVcDuVwDkNBoKVASh1lI8BnDTUI+gMNOfjDhu/74MFWDFcqv
+EhM62umRoxBt/LZkXYmjtBiafquQ3RH626+YElg8j0ci8lMCAwEAAaOCA5swggOX
+MB8GA1UdIwQYMBaAFHSFgMBmx9833s+9KTeqAx2+7c0XMB0GA1UdDgQWBBSX+6Yp
+MK6d8NmFBRKq1MHZ7A9bzjAtBgNVHREEJjAkghEqLm1vbmdvZGItZGV2Lm5ldIIP
+bW9uZ29kYi1kZXYubmV0MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwgZ8GA1UdHwSBlzCBlDBIoEagRIZCaHR0cDovL2NybDMu
+ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsRzJUTFNSU0FTSEEyNTYyMDIwQ0Ex
+LTEuY3JsMEigRqBEhkJodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRH
+bG9iYWxHMlRMU1JTQVNIQTI1NjIwMjBDQTEtMS5jcmwwPgYDVR0gBDcwNTAzBgZn
+gQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT
+MIGHBggrBgEFBQcBAQR7MHkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
+ZXJ0LmNvbTBRBggrBgEFBQcwAoZFaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
+L0RpZ2lDZXJ0R2xvYmFsRzJUTFNSU0FTSEEyNTYyMDIwQ0ExLTEuY3J0MAkGA1Ud
+EwQCMAAwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AO7N0GTV2xrOxVy3nbTN
+E6Iyh0Z8vOzew1FIWUZxH7WbAAABiHtHY1oAAAQDAEgwRgIhAIisBJOELCyZT7jE
+BrV/m5LTUSVyHdsS1cCqj9GXOe9MAiEAlrERQA+6AyiIvwzfS0poZwWXfva9b/MN
+2JzrkHX/easAdgBIsONr2qZHNA/lagL6nTDrHFIBy1bdLIHZu7+rOdiEcwAAAYh7
+R2NCAAAEAwBHMEUCIQCwyE51cYTecY/QJ3FsTbHoIKNUryeoovqsRkNKCMGplAIg
+GuFjeebbSphy/73Eotj1ETZMlW5otM/nNYS7rKLm0KAAdQDatr9rP7W2Ip+bwrtc
+a+hwkXFsu1GEhTS9pD0wSNf7qwAAAYh7R2MeAAAEAwBGMEQCIFFZODv/duyiPhVL
+YCOEIIhmw2R8lFG5Gr2HFXND9Za6AiAi83t19FhYx0gYhSIX/O0HoougBw4yDxac
+i+Wqa4aGazANBgkqhkiG9w0BAQsFAAOCAQEAvpd3IVUdxxhfUt6ohaXS7kgps0Yt
+MQISW4geCaz0fvxN5OjJVn7eHtxTHycb3QdoORwukYYgKvCa0f/EtcKFp+ycuGIx
+qltpjjfUzOx7hoayqW1gi3BEynJ62ahUlvha5SsqSF1PU/uzyWdwo6bVvm2ZEVGJ
+MeOxQ1TgfM2W/S0SEuujjrQSJSO/pl1wxT+Mnw5dONBLqmcw18NHdiC9GlUqst83
+kmeIZREVYqgIP5PuwbF5MwMDC0bUX9KCMXdsQhKJ9PivMqtFWZG/gkRp0kl/G/NJ
+KDeFdDxNkhtWMExpeV38TaZ7xuf1NObdwCX4abKGOkSpQbo0zf6qINJXrQ==
 -----END CERTIFICATE-----
\ No newline at end of file
diff --git a/spec/support/shared/app_metadata.rb b/spec/support/shared/app_metadata.rb
index 70bf0cc7fe..ba43efa9ed 100644
--- a/spec/support/shared/app_metadata.rb
+++ b/spec/support/shared/app_metadata.rb
@@ -1,6 +1,18 @@
 # frozen_string_literal: true
 # encoding: utf-8
 
+def target_arch
+  @target_arch ||= begin
+    uname = `uname -a`.strip
+    case uname
+    when /aarch/ then "aarch64"
+    when /x86/   then "x86_64"
+    when /arm/   then "arm64"
+    else raise "unrecognized architecture: #{uname.inspect}"
+    end
+  end
+end
+
 shared_examples 'app metadata document' do
   let(:app_metadata) do
     described_class.new({})
@@ -27,7 +39,7 @@
         # Ruby 2.7.3 uses linux.
         %w(linux linux-gnu).should include(document[:client][:os][:name])
       end
-      document[:client][:os][:architecture].should == 'x86_64'
+      document[:client][:os][:architecture].should == target_arch
     end
   end
 
@@ -45,7 +57,7 @@
       else
         document[:client][:os][:name].should =~ /darwin\d+/
       end
-      document[:client][:os][:architecture].should == 'x86_64'
+      document[:client][:os][:architecture].should == target_arch
     end
   end
 
