From c123fb3977dec09fa771f121b2449208d6d5e2b5 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:28:01 +0400 Subject: [PATCH 1/3] Stop trusting the compromised eMudhra CA certificates in the Modal image The hosted MCP server's image still carries certifi 2026.7.22 (cached pip_install layer built 2026-08-12) and Debian's unfixed ca-certificates, both of which trust "emSign ECC Root CA - G3". Append two layers at the end of the image chain: refresh ca-certificates from Debian and require certifi>=2026.10.19. Appending keeps every earlier layer cached, which matters because rebuilding the pip_install layer fails today (policyengine-us==1.764.6 is no longer on PyPI). Co-Authored-By: Claude Opus 5.5 --- integration/modal_app.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/integration/modal_app.py b/integration/modal_app.py index a91851cf..fdcdbbbf 100644 --- a/integration/modal_app.py +++ b/integration/modal_app.py @@ -284,6 +284,20 @@ f"pip install -e {OBR_REPO} -e {BOE_REPO} -e {FRB_REPO} " f"-e {HOME}/macro/integration" ) + # Refresh the two CA lists that the server's outbound HTTPS (Hugging Face + # microdata downloads, PolicyEngine data fetches) verifies against: + # Debian's ca-certificates, read by urllib, aiohttp and hf-xet, and certifi, + # read by requests and httpx. The cached pip_install layer above was built + # on 2026-08-12 with certifi 2026.7.22, on a debian_slim base built before + # Debian released ca-certificates 20250419~deb12u2; both lists trusted the + # compromised eMudhra root "emSign ECC Root CA - G3" + # (https://emudhra.we-have-a-problem.com). + # These two steps go at the END of the chain on purpose. Modal caches each + # layer by its definition, so appending leaves every layer above cached. + # Adding certifi to the pip_install list instead would rebuild that layer, + # and the rebuild fails today: policyengine-us==1.764.6 is no longer on PyPI. + .apt_install("ca-certificates") + .pip_install("certifi>=2026.10.19") ) app = modal.App("policyengine-macro-mcp") From 932f5134d5834e612e3cf6270cd0cb83b25baf13 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 21:53:32 +0400 Subject: [PATCH 2/3] Move the Modal image to policyengine 6.10 and policyengine-us 2.2.1 policyengine-us 1.764.6 is no longer on PyPI, so any rebuild of the Modal image's pip_install layer fails, and the nightly contract job has failed on every run since its uv install could not resolve that pin. No policyengine 4.x or 5.x release can resolve either: every one with a [models] extra pins a 1.x policyengine-us that PyPI no longer hosts. Pin policyengine[models]>=6.10,<7 and policyengine-us==2.2.1, in both integration/pyproject.toml and the image definition. 2.2.1 is the newest policyengine-us that the newest policyengine (6.10.0) can import: its bundled manifest certifies the US data release for 2.2.1, and the release accepts only >=2.0.0,<2.3. With policyengine-us 2.59.0 (the latest on PyPI), `import policyengine` raises "Data release manifest is not certified for the runtime model version 2.59.0", which would break every UK and US tool. The resolved set is policyengine 6.10.0, policyengine-core 3.33.3, policyengine-uk 2.132.6, policyengine-us 2.2.1 and spm-calculator 1.0.0. Changing the pip_install list rebuilds that layer and every layer after it; the ca-certificates and certifi steps stay at the end of the chain. Co-Authored-By: Claude Opus 5.5 --- integration/modal_app.py | 29 ++++++++++++++++------------- integration/pyproject.toml | 15 +++++++++------ integration/tests/test_og.py | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/integration/modal_app.py b/integration/modal_app.py index fdcdbbbf..3221991f 100644 --- a/integration/modal_app.py +++ b/integration/modal_app.py @@ -216,12 +216,14 @@ # imports it lazily inside the pe_* adapters — module import at # container start stays fast; only the first policyengine tool call # in a fresh container pays the load. - "policyengine[models]>=4,<5", # [models] extra REQUIRED: base install leaves pe.uk/pe.us as None - # policyengine's bundled data-release manifest certifies an exact - # country-package version; a newer policyengine-us makes every US - # (and via import, UK) call raise at import time. Keep this pin in - # sync with the manifest version reported by the certification error. - "policyengine-us==1.764.6", + "policyengine[models]>=6.10,<7", # [models] extra REQUIRED: base install leaves pe.uk/pe.us as None + # policyengine's bundled data-release manifest certifies the US data + # for one policyengine-us version (policyengine 6.10.0: 2.2.1). Any + # other version fails certification when `import policyengine` runs, + # so every US and UK call raises. Keep this pin equal to the + # policyengine-us pin in the [models] extra of the policyengine + # release that resolves, and to integration/pyproject.toml. + "policyengine-us==2.2.1", # US HANK (hank_shock / hank_summary). Installed from GitHub as a # normal wheel — no data files or repo checkout needed at runtime; # the calibration is code. Cost profile: the first hank_shock call @@ -287,15 +289,16 @@ # Refresh the two CA lists that the server's outbound HTTPS (Hugging Face # microdata downloads, PolicyEngine data fetches) verifies against: # Debian's ca-certificates, read by urllib, aiohttp and hf-xet, and certifi, - # read by requests and httpx. The cached pip_install layer above was built - # on 2026-08-12 with certifi 2026.7.22, on a debian_slim base built before - # Debian released ca-certificates 20250419~deb12u2; both lists trusted the + # read by requests and httpx. The pip_install layer built on 2026-08-12 + # held certifi 2026.7.22, on a debian_slim base built before Debian + # released ca-certificates 20250419~deb12u2; both lists trusted the # compromised eMudhra root "emSign ECC Root CA - G3" # (https://emudhra.we-have-a-problem.com). - # These two steps go at the END of the chain on purpose. Modal caches each - # layer by its definition, so appending leaves every layer above cached. - # Adding certifi to the pip_install list instead would rebuild that layer, - # and the rebuild fails today: policyengine-us==1.764.6 is no longer on PyPI. + # The pip_install layer now rebuilds (its policyengine pins changed), which + # would pull the current certifi on its own; these two explicit steps + # keep the fixed minimums enforced for any later rebuild, whatever the + # layers above resolve to, and refresh the Debian package regardless of + # how old Modal's cached debian_slim base layer is. .apt_install("ca-certificates") .pip_install("certifi>=2026.10.19") ) diff --git a/integration/pyproject.toml b/integration/pyproject.toml index d411ed2e..d7ec039f 100644 --- a/integration/pyproject.toml +++ b/integration/pyproject.toml @@ -54,12 +54,15 @@ models = [ "frbus @ git+https://github.com/PolicyEngine/us-frb-model@2508a2d3ee7d078fd82f38bc17567e4433489913", "us-hank-model @ git+https://github.com/PolicyEngine/us-hank-model@19a8e5e5cd8e7120fbbd2e3de3c5b63509824f3c", # [models] pulls the pinned policyengine-uk / policyengine-us country - # packages that pe.uk / pe.us need at runtime. - "policyengine[models]>=4,<5", - # policyengine's bundled data-release manifest certifies an exact - # policyengine-us version; anything newer fails certification at import - # (every pe.* call raises). Keep in sync with the manifest pin. - "policyengine-us==1.764.6", + # packages that pe.uk / pe.us need at runtime. policyengine 6.x needs + # Python >= 3.11. + "policyengine[models]>=6.10,<7", + # policyengine's bundled data-release manifest certifies the US data for + # one policyengine-us version (policyengine 6.10.0: 2.2.1; the data release + # also accepts >=2.0.0,<2.3). Any other version fails certification when + # `import policyengine` runs, so every pe.* call (UK included) raises. + # Keep in sync with the [models] pin of the chosen policyengine release. + "policyengine-us==2.2.1", ] [project.scripts] diff --git a/integration/tests/test_og.py b/integration/tests/test_og.py index e11d32f7..026ef90e 100644 --- a/integration/tests/test_og.py +++ b/integration/tests/test_og.py @@ -8,7 +8,7 @@ from the PolicyEngine enhanced-FRS microdata and pins policyengine-uk==2.88.0; policyengine-uk >= 2.89 renamed the dataset keys (enhanced_frs_2023_24_* -> populace_uk_*), so calibration KeyError-fails under a newer PE. The policyengine-macro -package itself requires policyengine[models]>=4 (which brings pe-uk >= 2.89 for +package itself requires policyengine[models]>=6.10 (which brings pe-uk >= 2.89 for the household/population tools), so a single env cannot satisfy both. The real end-to-end solve is therefore skipped when the installed PE is incompatible with oguk's calibration; the adapter's translation of that failure into an From 71ad06932f516b53dc472a48f3a9e3c0a2c6e1b5 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 22:09:21 +0400 Subject: [PATCH 3/3] Keep policyengine-us 1.764.6, installed from its source commit Replaces the move to policyengine 6.10 and policyengine-us 2.2.1 in the previous commit, which changes results and breaks the US household tools. policyengine 6.10 cannot import the latest policyengine-us (2.59.0): its manifest certifies the US data for 2.2.1 only, so `import policyengine` raises. With 2.2.1, policyengine-us 2.x requires a county FIPS code or an explicit SPM geography for the default US household outputs, so every calculate_household and household_reform_impact call for the US raises SPM_GEOGRAPHY_REQUIRED; with a geography selected, outputs still change (for one New York single parent on $30,000, household_benefits falls from 26,172.92 to 4,919.13). Keep the deployed versions instead. policyengine-us has no release tags, so install 1.764.6 from the commit that set that version (92e6052d3e057c4e03b955d01a7ee4e3c2fb3131). A wheel built from that archive has the same 16,080 package files, byte for byte, as the PyPI wheel the policyengine 4.22.3 manifest records (sha256 2b2c5c02...). Also pin spm-calculator 0.3.1 and microdf-python 1.3.0, the versions the deployed image holds: policyengine-us 1.764.6 declares spm-calculator>=0.2.0 and policyengine 4.x microdf_python>=1.2.1, so a rebuild would otherwise take spm-calculator 1.0 and microdf-python 1.5. Both pip and uv resolve this to policyengine 4.22.3, policyengine-core 3.30.0, policyengine-uk 2.89.2 and policyengine-us 1.764.6, on Python 3.12 and 3.13. The rebuilt pip_install layer pulls certifi 2026.10.19. Co-Authored-By: Claude Opus 5.5 --- integration/modal_app.py | 34 +++++++++++++++++++++------------- integration/pyproject.toml | 25 ++++++++++++++++--------- integration/tests/test_og.py | 2 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/integration/modal_app.py b/integration/modal_app.py index 3221991f..18f6cd94 100644 --- a/integration/modal_app.py +++ b/integration/modal_app.py @@ -216,14 +216,22 @@ # imports it lazily inside the pe_* adapters — module import at # container start stays fast; only the first policyengine tool call # in a fresh container pays the load. - "policyengine[models]>=6.10,<7", # [models] extra REQUIRED: base install leaves pe.uk/pe.us as None - # policyengine's bundled data-release manifest certifies the US data - # for one policyengine-us version (policyengine 6.10.0: 2.2.1). Any - # other version fails certification when `import policyengine` runs, - # so every US and UK call raises. Keep this pin equal to the - # policyengine-us pin in the [models] extra of the policyengine - # release that resolves, and to integration/pyproject.toml. - "policyengine-us==2.2.1", + "policyengine[models]>=4,<5", # [models] extra REQUIRED: base install leaves pe.uk/pe.us as None + # policyengine's bundled data-release manifest certifies an exact + # country-package version; a newer policyengine-us makes every US + # (and via import, UK) call raise at import time. Keep this pin in + # sync with the manifest version reported by the certification error + # (1.764.6) and with integration/pyproject.toml. PyPI no longer hosts + # 1.764.6, so it installs from the policyengine-us commit that set + # that version; a wheel built from this archive has the same package + # files as the PyPI wheel. Tarball URL, not git+, for the same reason + # as us-hank-model below. + "policyengine-us @ https://github.com/PolicyEngine/policyengine-us/archive/92e6052d3e057c4e03b955d01a7ee4e3c2fb3131.tar.gz", + # Unpinned upstream (policyengine-us 1.764.6 declares + # spm-calculator>=0.2.0, policyengine 4.x microdf_python>=1.2.1), so a + # rebuild would take newer releases than the image has run with. + "spm-calculator==0.3.1", + "microdf-python==1.3.0", # US HANK (hank_shock / hank_summary). Installed from GitHub as a # normal wheel — no data files or repo checkout needed at runtime; # the calibration is code. Cost profile: the first hank_shock call @@ -294,11 +302,11 @@ # released ca-certificates 20250419~deb12u2; both lists trusted the # compromised eMudhra root "emSign ECC Root CA - G3" # (https://emudhra.we-have-a-problem.com). - # The pip_install layer now rebuilds (its policyengine pins changed), which - # would pull the current certifi on its own; these two explicit steps - # keep the fixed minimums enforced for any later rebuild, whatever the - # layers above resolve to, and refresh the Debian package regardless of - # how old Modal's cached debian_slim base layer is. + # The pip_install layer now rebuilds (its policyengine-us requirement + # changed), which would pull the current certifi on its own; these two + # explicit steps keep the fixed minimums enforced for any later rebuild, + # whatever the layers above resolve to, and refresh the Debian package + # regardless of how old Modal's cached debian_slim base layer is. .apt_install("ca-certificates") .pip_install("certifi>=2026.10.19") ) diff --git a/integration/pyproject.toml b/integration/pyproject.toml index d7ec039f..32f8de55 100644 --- a/integration/pyproject.toml +++ b/integration/pyproject.toml @@ -54,15 +54,22 @@ models = [ "frbus @ git+https://github.com/PolicyEngine/us-frb-model@2508a2d3ee7d078fd82f38bc17567e4433489913", "us-hank-model @ git+https://github.com/PolicyEngine/us-hank-model@19a8e5e5cd8e7120fbbd2e3de3c5b63509824f3c", # [models] pulls the pinned policyengine-uk / policyengine-us country - # packages that pe.uk / pe.us need at runtime. policyengine 6.x needs - # Python >= 3.11. - "policyengine[models]>=6.10,<7", - # policyengine's bundled data-release manifest certifies the US data for - # one policyengine-us version (policyengine 6.10.0: 2.2.1; the data release - # also accepts >=2.0.0,<2.3). Any other version fails certification when - # `import policyengine` runs, so every pe.* call (UK included) raises. - # Keep in sync with the [models] pin of the chosen policyengine release. - "policyengine-us==2.2.1", + # packages that pe.uk / pe.us need at runtime. + "policyengine[models]>=4,<5", + # policyengine's bundled data-release manifest certifies an exact + # policyengine-us version; anything newer fails certification at import + # (every pe.* call raises). Keep in sync with the manifest pin, 1.764.6. + # PyPI no longer hosts 1.764.6, so it installs from the policyengine-us + # commit that set that version (the repository has no release tags); a + # wheel built from this archive has the same package files as the PyPI + # wheel. It satisfies the policyengine-us==1.764.6 pin in [models]. + "policyengine-us @ https://github.com/PolicyEngine/policyengine-us/archive/92e6052d3e057c4e03b955d01a7ee4e3c2fb3131.tar.gz", + # policyengine-us 1.764.6 declares spm-calculator>=0.2.0 and policyengine + # 4.x declares microdf_python>=1.2.1, so a fresh install would take + # releases newer than the ones these models were run and certified with. + # Pin the versions the deployed image holds. + "spm-calculator==0.3.1", + "microdf-python==1.3.0", ] [project.scripts] diff --git a/integration/tests/test_og.py b/integration/tests/test_og.py index 026ef90e..e11d32f7 100644 --- a/integration/tests/test_og.py +++ b/integration/tests/test_og.py @@ -8,7 +8,7 @@ from the PolicyEngine enhanced-FRS microdata and pins policyengine-uk==2.88.0; policyengine-uk >= 2.89 renamed the dataset keys (enhanced_frs_2023_24_* -> populace_uk_*), so calibration KeyError-fails under a newer PE. The policyengine-macro -package itself requires policyengine[models]>=6.10 (which brings pe-uk >= 2.89 for +package itself requires policyengine[models]>=4 (which brings pe-uk >= 2.89 for the household/population tools), so a single env cannot satisfy both. The real end-to-end solve is therefore skipped when the installed PE is incompatible with oguk's calibration; the adapter's translation of that failure into an