diff --git a/apps/cli/executables/pexable/ingest_envoy/README.md b/apps/cli/executables/pexable/ingest_envoy/README.md
index 6efa7cfe81967dac5bb0849713d86919bd160eb4..4cd7fcaebd8c6083ffc5c20f6308f338a6f3ea8a 100644
--- a/apps/cli/executables/pexable/ingest_envoy/README.md
+++ b/apps/cli/executables/pexable/ingest_envoy/README.md
@@ -1 +1,4 @@
 # Ingestion Envoy
+
+This is a Python port of the ingestion manifest builder in archive-metaproject.
+See https://open-confluence.nrao.edu/display/SSA/Ingestion+Manifests.
diff --git a/apps/cli/executables/pexable/ingest_envoy/ingest_envoy/classes.py b/apps/cli/executables/pexable/ingest_envoy/ingest_envoy/classes.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9ae7667010099ba79b602c4aa432ccea193861f
--- /dev/null
+++ b/apps/cli/executables/pexable/ingest_envoy/ingest_envoy/classes.py
@@ -0,0 +1,49 @@
+""" Objects pertaining to the various ingestion manifests """
+
+from enum import Enum
+
+
+class Telescope(Enum):
+    """Codifying the names of our telescopes, because Janet can't abide magic strings"""
+
+    VLA = 1
+    EVLA = 2
+    ALMA = 3
+    VLBA = 4
+    GBT = 5
+    NONE = 6
+
+
+class IngestionType(Enum):
+    """Types of ingestion we'll have to do"""
+
+    # ALMA products
+    ALMA_SDM = Telescope.ALMA
+    ALMA_CAL = Telescope.ALMA
+    ALMA_AUDI = Telescope.ALMA
+
+    # EVLA products
+    EVLA_SDM = Telescope.EVLA
+    EVLA_BDF = Telescope.EVLA
+    EVLA_CAL = Telescope.EVLA
+
+    # RealFast projects
+    REALFAST_SDM = Telescope.EVLA
+
+    # VLASS projects
+    VLASS_QUICKLOOK = Telescope.EVLA
+
+    # VLBA ingestion. (IDI and UVFITS products are treated the same.)
+    VLBA_FITS = Telescope.VLBA
+
+    # Coming Real Soon Now: VLBA Mark 4 product ingestion
+    VLBA_MARK4 = Telescope.VLBA
+
+    # Also coming Real Soon: GBT execution block ingestion
+    GBT_EB = Telescope.GBT
+
+    # Hot on its heels: LVLA execution block ingestion
+    LVLA_EB = Telescope.VLA
+
+    # When we just don't know what we're dealing with
+    UNKNOWN = Telescope.NONE
diff --git a/apps/cli/executables/pexable/ingest_envoy/setup.py b/apps/cli/executables/pexable/ingest_envoy/setup.py
index 1fa3d5bda06efd6f600e4d25ec49f3ebedcdbf35..5cbc999a2d71fdb0026627f1a125ca5c682cfb5c 100644
--- a/apps/cli/executables/pexable/ingest_envoy/setup.py
+++ b/apps/cli/executables/pexable/ingest_envoy/setup.py
@@ -20,6 +20,7 @@ setup(
     url="TBD",
     license="GPL",
     install_requires=requires,
+    tests_require=["pytest"],
     keywords=[],
     packages=find_packages(),
     classifiers=["Programming Language :: Python :: 3.8"],
diff --git a/apps/cli/executables/pexable/ingest_envoy/test/__init__.py b/apps/cli/executables/pexable/ingest_envoy/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/cli/executables/pexable/ingest_envoy/test/test_alma_ing_manifests.py b/apps/cli/executables/pexable/ingest_envoy/test/test_alma_ing_manifests.py
new file mode 100644
index 0000000000000000000000000000000000000000..da37adf8fda3820ae08758060f0ffd4b9a4f3e49
--- /dev/null
+++ b/apps/cli/executables/pexable/ingest_envoy/test/test_alma_ing_manifests.py
@@ -0,0 +1,33 @@
+""" Test for the various types of ALMA ingestion manifests """
+
+import pytest
+
+
+@pytest.mark.skip("TODO: test_builds_alma_sdm_manifest")
+def test_builds_alma_sdm_manifest():
+    """
+    Have we built a well-formed ALMA SDM ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_alma_cal_manifest")
+def test_builds_alma_cal_manifest():
+    """
+    Have we built a well-formed ALMA calibration ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_alma_cal_manifest")
+def test_builds_alma_audi_manifest():
+    """
+    Have we built a well-formed ALMA image ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
diff --git a/apps/cli/executables/pexable/ingest_envoy/test/test_evla_ing_manifests.py b/apps/cli/executables/pexable/ingest_envoy/test/test_evla_ing_manifests.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d6bff8d57655411d7b46dff34cc5915833ce7ed
--- /dev/null
+++ b/apps/cli/executables/pexable/ingest_envoy/test/test_evla_ing_manifests.py
@@ -0,0 +1,33 @@
+""" Test for the various types of EVLA ingestion manifests """
+
+import pytest
+
+
+@pytest.mark.skip("TODO: test_builds_evla_sdm_manifest")
+def test_builds_evla_sdm_manifest():
+    """
+    Have we built a well-formed EVLA SDM ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_evla_bdf_manifest")
+def test_builds_evla_bdf_manifest():
+    """
+    Have we built a well-formed EVLA BDF ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_evla_cal_manifest")
+def test_builds_evla_cal_manifest():
+    """
+    Have we built a well-formed EVLA calibration ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
diff --git a/apps/cli/executables/pexable/ingest_envoy/test/test_miscellaneous_manifests.py b/apps/cli/executables/pexable/ingest_envoy/test/test_miscellaneous_manifests.py
new file mode 100644
index 0000000000000000000000000000000000000000..f16175ae14a64761e20cd6f402fe273163628a3a
--- /dev/null
+++ b/apps/cli/executables/pexable/ingest_envoy/test/test_miscellaneous_manifests.py
@@ -0,0 +1,45 @@
+""" Tests for all the other types of ingestion manifests """
+
+import pytest
+
+
+@pytest.mark.skip("TODO: test_builds_realfast_sdm_manifest")
+def test_builds_realfast_sdm_manifest():
+    """
+    Have we built a well-formed RealFast product ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_vlass_quicklook_manifest")
+def test_builds_vlass_quicklook_manifest():
+    """
+    Have we built a well-formed VLASS quicklook product ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_gbt_manifest")
+def test_builds_gbt_manifest():
+    """
+    Not yet in use, but sooner or later we'll need to create manifests
+    for ingestion of GBT products.
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_lvla_eb_manifest")
+def test_builds_lvla_eb_manifest():
+    """
+    Not yet in use, but one of these days we'll need to create manifests
+    for ingestion of LVLA execution blocks.
+
+    :return:
+    """
+    raise NotImplementedError
diff --git a/apps/cli/executables/pexable/ingest_envoy/test/test_vlba_manifests.py b/apps/cli/executables/pexable/ingest_envoy/test/test_vlba_manifests.py
new file mode 100644
index 0000000000000000000000000000000000000000..16cd3707f3a9f17edfe5620b8d6eb574d70e6a57
--- /dev/null
+++ b/apps/cli/executables/pexable/ingest_envoy/test/test_vlba_manifests.py
@@ -0,0 +1,23 @@
+""" Tests for VLBA product ingestion manifests """
+
+import pytest
+
+
+@pytest.mark.skip("TODO: test_builds_vlba_fits_manifest")
+def test_builds_vlba_fits_manifest():
+    """
+    Have we built a well-formed VLBA IDI/UVFITS ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError
+
+
+@pytest.mark.skip("TODO: test_builds_vlba_mark4_manifest")
+def test_builds_vlba_mark4_manifest():
+    """
+    Unused, but coming: Have we built a well-formed VLBA Mark 4 ingestion manifest?
+
+    :return:
+    """
+    raise NotImplementedError