diff --git a/starttls_policy_cli/configure.py b/starttls_policy_cli/configure.py
index 8ec010d..bb13dec 100644
--- a/starttls_policy_cli/configure.py
+++ b/starttls_policy_cli/configure.py
@@ -20,8 +20,9 @@ class ConfigGenerator(object):
     """
     __metaclass__ = abc.ABCMeta
 
-    def __init__(self, policy_dir):
+    def __init__(self, policy_dir, enforce_testing=False):
         self._policy_dir = policy_dir
+        self._enforce_testing = enforce_testing
         self._policy_filename = os.path.join(self._policy_dir, constants.POLICY_FILENAME)
         self._config_filename = os.path.join(self._policy_dir, self.default_filename)
         self._policy_config = None
@@ -86,15 +87,6 @@ class ConfigGenerator(object):
     def default_filename(self):
         """The expected default filename of the generated configuration file."""
 
-def _policy_for_domain(domain, tls_policy, max_domain_len):
-    line = ("{0:%d} " % max_domain_len).format(domain)
-    if tls_policy.mode == "enforce":
-        line += " secure match="
-        line += ":".join(tls_policy.mxs)
-    elif tls_policy.mode == "testing":
-        line = "# " + line + "undefined due to testing policy"
-    return line
-
 class PostfixGenerator(ConfigGenerator):
     """Configuration generator for postfix.
     """
@@ -103,7 +95,7 @@ class PostfixGenerator(ConfigGenerator):
         policies = []
         max_domain_len = len(max(policy_list, key=len))
         for domain, tls_policy in sorted(six.iteritems(policy_list)):
-            policies.append(_policy_for_domain(domain, tls_policy, max_domain_len))
+            policies.append(self._policy_for_domain(domain, tls_policy, max_domain_len))
         return "\n".join(policies)
 
     def _generate_expired_fallback(self, policy_list):
@@ -122,6 +114,16 @@ class PostfixGenerator(ConfigGenerator):
             "And finally:\n\n"
             "postfix reload\n").format(abs_path=abs_path, filename=filename)
 
+    def _policy_for_domain(self, domain, tls_policy, max_domain_len):
+        line = ("{0:%d} " % max_domain_len).format(domain)
+        mode = tls_policy.mode
+        if mode == "enforce" or self._enforce_testing and mode == "testing":
+            line += " secure match="
+            line += ":".join(tls_policy.mxs)
+        elif mode == "testing":
+            line = "# " + line + "undefined due to testing policy"
+        return line
+
     @property
     def mta_name(self):
         return "Postfix"
diff --git a/starttls_policy_cli/main.py b/starttls_policy_cli/main.py
index 2963964..fe553e6 100644
--- a/starttls_policy_cli/main.py
+++ b/starttls_policy_cli/main.py
@@ -9,12 +9,26 @@ GENERATORS = {
 }
 
 def _argument_parser():
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--generate", help="The MTA you want to generate a configuration file for.",
+    parser = argparse.ArgumentParser(
+        description="Generates MTA configuration file according to STARTTLS-Everywhere policy",
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument("-g", "--generate",
+                        choices=GENERATORS,
+                        help="The MTA you want to generate a configuration file for.",
                         dest="generate", required=True)
     # TODO: decide whether to use /etc/ for policy list home
-    parser.add_argument("--policy-dir", help="Policy file directory on this computer.",
+    parser.add_argument("-d", "--policy-dir",
+                        help="Policy file directory on this computer.",
                         default="/etc/starttls-policy/", dest="policy_dir")
+    parser.add_argument("-e", "--early-adopter",
+                        help="Early Adopter mode. Processes all \"testing\" domains in policy list "
+                        "same way as domains in \"enforce\" mode, effectively requiring strong TLS "
+                        "for domains in \"testing\" mode too. This mode is useful for participating"
+                        " in tests of recently added domains with real communications and earlier "
+                        "security hardening at the cost of increased probability of delivery "
+                        "degradation. Use this mode with awareness about all implications.",
+                        action="store_true",
+                        dest="early_adopter")
     return parser
 
 
@@ -24,19 +38,15 @@ def _ensure_directory(directory):
 
 def _generate(arguments):
     _ensure_directory(arguments.policy_dir)
-    config_generator = GENERATORS[arguments.generate](arguments.policy_dir)
+    config_generator = GENERATORS[arguments.generate](arguments.policy_dir,
+                                                      arguments.early_adopter)
     config_generator.generate()
     config_generator.manual_instructions()
 
-def _perform(arguments, parser):
-    if arguments.generate not in GENERATORS:
-        parser.error("no configuration generator exists for '%s'" % arguments.generate)
-    _generate(arguments)
-
 def main():
     """ Entrypoint for CLI tool. """
     parser = _argument_parser()
-    _perform(parser.parse_args(), parser)
+    _generate(parser.parse_args())
 
 if __name__ == "__main__":
     main()  # pragma: no cover
diff --git a/starttls_policy_cli/policy.py b/starttls_policy_cli/policy.py
index b4ab17a..8b5aedb 100644
--- a/starttls_policy_cli/policy.py
+++ b/starttls_policy_cli/policy.py
@@ -210,7 +210,6 @@ class PolicyNoAlias(Policy):
     @property
     def policy_alias(self):
         """ This type of policy can't be aliased. Returns None."""
-        pass
 
     @policy_alias.setter
     def policy_alias(self, value):
@@ -238,6 +237,7 @@ class Config(MergableConfig, collections.Mapping):
             yield domain
 
     def keys(self):
+        """ Returns iterable over policies even if this attribute is not set """
         if self.policies is None:
             return set([])
         return self.policies.keys()

Implement Early Adopter mode
Actual issue is created for tracking implementation progress of feature described [here](https://github.com/EFForg/starttls-everywhere/issues/124).
diff --git a/starttls_policy_cli/tests/configure_test.py b/starttls_policy_cli/tests/configure_test.py
index 5bd4b2e..706c16a 100644
--- a/starttls_policy_cli/tests/configure_test.py
+++ b/starttls_policy_cli/tests/configure_test.py
@@ -104,11 +104,15 @@ test_json_expired = '{\
     }'
 
 testgen_data = [
-    param("simple_policy", test_json, "# .testing.example-recipient.com "
+    param("simple_policy", test_json, False, "# .testing.example-recipient.com "
                                       "undefined due to testing policy\n"
                                       ".valid.example-recipient.com    "
                                       "secure match=.valid.example-recipient.com\n"),
-    param("expired_policy", test_json_expired, "# Policy list is outdated. "
+    param("simple_policy_early", test_json, True, ".testing.example-recipient.com  "
+                                      "secure match=.testing.example-recipient.com\n"
+                                      ".valid.example-recipient.com    "
+                                      "secure match=.valid.example-recipient.com\n"),
+    param("expired_policy", test_json_expired, False, "# Policy list is outdated. "
                                                "Falling back to opportunistic encryption.\n"),
 ]
 
@@ -127,10 +131,10 @@ class TestPostfixGenerator(unittest.TestCase):
         self.assertTrue("postfix reload" in instructions)
         self.assertTrue(generator.default_filename in instructions)
 
-    def config_test(self, conf, expected):
+    def config_test(self, conf, enforce_testing, expected):
         """PostfixGenerator test parameterized over various policies"""
         with TempPolicyDir(conf) as testdir:
-            generator = configure.PostfixGenerator(testdir)
+            generator = configure.PostfixGenerator(testdir, enforce_testing)
             generator.generate()
             pol_filename = os.path.join(testdir, generator.default_filename)
             with open(pol_filename) as pol_file:
diff --git a/starttls_policy_cli/tests/main_test.py b/starttls_policy_cli/tests/main_test.py
index 7c4a593..edca7de 100644
--- a/starttls_policy_cli/tests/main_test.py
+++ b/starttls_policy_cli/tests/main_test.py
@@ -24,21 +24,21 @@ class TestArguments(unittest.TestCase):
 
     def test_generate_arg(self):
         # pylint: disable=protected-access
-        sys.argv = ["_", "--generate", "lol"]
+        sys.argv = ["_", "--generate", "postfix"]
         parser = main._argument_parser()
         arguments = parser.parse_args()
-        self.assertEqual(arguments.generate, "lol")
+        self.assertEqual(arguments.generate, "postfix")
 
     def test_default_dir(self):
         # pylint: disable=protected-access
-        sys.argv = ["_", "--generate", "lol"]
+        sys.argv = ["_", "--generate", "postfix"]
         parser = main._argument_parser()
         arguments = parser.parse_args()
         self.assertEqual(arguments.policy_dir, "/etc/starttls-policy/")
 
     def test_policy_dir(self):
         # pylint: disable=protected-access
-        sys.argv = ["_", "--generate", "lol", "--policy-dir", "lmao"]
+        sys.argv = ["_", "--generate", "postfix", "--policy-dir", "lmao"]
         parser = main._argument_parser()
         arguments = parser.parse_args()
         self.assertEqual(arguments.policy_dir, "lmao")
@@ -50,7 +50,7 @@ class TestPerform(unittest.TestCase):
         sys.argv = ["_", "--generate", "lmao"]
         parser = main._argument_parser()
         parser.error = mock.MagicMock(side_effect=Exception)
-        self.assertRaises(Exception, main._perform, parser.parse_args(), parser)
+        self.assertRaises(Exception, parser.parse_args)
 
     @mock.patch("starttls_policy_cli.main._ensure_directory")
     def test_generate(self, ensure_directory):
@@ -58,7 +58,7 @@ class TestPerform(unittest.TestCase):
         main.GENERATORS = {"exists": mock.MagicMock()}
         sys.argv = ["_", "--generate", "exists"]
         parser = main._argument_parser()
-        main._perform(parser.parse_args(), parser)
+        main._generate(parser.parse_args())
         self.assertTrue(main.GENERATORS["exists"].called_with("/etc/starttls-policy"))
 
     @mock.patch("os.path.exists")

pip install -e .[dev]
parse_log_pytest
pytest
pytest pytest-cov
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.11.7
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
bleach==4.1.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
colorama==0.4.5
coverage==6.2
cryptography==40.0.2
DateTime==4.9
dill==0.3.4
docutils==0.18.1
idna==3.10
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
importlib-resources==5.4.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isort==5.10.1
jeepney==0.7.1
keyring==23.4.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
mock==5.2.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pkginfo==1.10.0
platformdirs==2.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycparser==2.21
Pygments==2.14.0
pylint==2.13.9
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
pytz==2025.2
readme-renderer==34.0
requests==2.27.1
requests-toolbelt==1.0.0
rfc3986==1.5.0
SecretStorage==3.3.3
six==1.17.0
-e git+https://github.com/EFForg/starttls-policy-cli.git@be1143dcfb2d54fe638a850f63f603aedb068e0c#egg=starttls_policy_cli
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
tqdm==4.64.1
twine==3.8.0
typed-ast==1.5.5
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
webencodings==0.5.1
wrapt==1.16.0
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work
zope.interface==5.5.2

name: starttls-policy-cli
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.11.7
      - bleach==4.1.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - colorama==0.4.5
      - coverage==6.2
      - cryptography==40.0.2
      - datetime==4.9
      - dill==0.3.4
      - docutils==0.18.1
      - idna==3.10
      - importlib-resources==5.4.0
      - isort==5.10.1
      - jeepney==0.7.1
      - keyring==23.4.1
      - lazy-object-proxy==1.7.1
      - mccabe==0.7.0
      - mock==5.2.0
      - pkginfo==1.10.0
      - platformdirs==2.4.0
      - pycparser==2.21
      - pygments==2.14.0
      - pylint==2.13.9
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - readme-renderer==34.0
      - requests==2.27.1
      - requests-toolbelt==1.0.0
      - rfc3986==1.5.0
      - secretstorage==3.3.3
      - six==1.17.0
      - tomli==1.2.3
      - tqdm==4.64.1
      - twine==3.8.0
      - typed-ast==1.5.5
      - urllib3==1.26.20
      - webencodings==0.5.1
      - wrapt==1.16.0
      - zope-interface==5.5.2
prefix: /opt/conda/envs/starttls-policy-cli

starttls_policy_cli/tests/configure_test.py::TestPostfixGenerator::test_expired_policy starttls_policy_cli/tests/configure_test.py::TestPostfixGenerator::test_simple_policy starttls_policy_cli/tests/configure_test.py::TestPostfixGenerator::test_simple_policy_early starttls_policy_cli/tests/main_test.py::TestPerform::test_generate_unknown
starttls_policy_cli/tests/main_test.py::TestPerform::test_generate
starttls_policy_cli/tests/configure_test.py::TestConfigGenerator::test_generate starttls_policy_cli/tests/configure_test.py::TestConfigGenerator::test_manual_instructions starttls_policy_cli/tests/configure_test.py::TestPostfixGenerator::test_instruct_string starttls_policy_cli/tests/configure_test.py::TestPostfixGenerator::test_mta_name starttls_policy_cli/tests/main_test.py::TestArguments::test_default_dir starttls_policy_cli/tests/main_test.py::TestArguments::test_generate_arg starttls_policy_cli/tests/main_test.py::TestArguments::test_generate_no_arg starttls_policy_cli/tests/main_test.py::TestArguments::test_no_args_require_generate starttls_policy_cli/tests/main_test.py::TestArguments::test_policy_dir starttls_policy_cli/tests/main_test.py::TestPerform::test_ensure_directory
starttls_policy_cli/configure.py starttls_policy_cli/policy.py starttls_policy_cli/main.py
starttls_policy_cli/configure.py starttls_policy_cli/policy.py starttls_policy_cli/main.py