Neatbo.

Wheel archive and RECORD audit

Compare a local wheel filename, metadata, tags and every RECORD hash/size, distinguishing integrity failures, unsupported hashes and unverified signatures.

Browser-local processingInputWheel .whl archiveOutputAudit report JSON / CSV / HTMLUp to 32 MiB per file · File limit: 1
  1. 1Add input
  2. 2Adjust settings
  3. 3Get your result

Tool input and files are processed in this browser without being uploaded.

Your input

Inputs are kept temporarily in this tab when switching tools. Refreshing or closing clears them; large results may need to be regenerated.

⌘ / Ctrl + Enter to run

or drag and drop it here

Files stay on this device. Your originals stay unchanged.

.whl

Up to 32 MiB per file · File limit: 1

    Options

    Complete the required options first. You can keep the defaults for the rest.

    Preparing the tool…

    Before you start

    Before installing an unfamiliar wheel, inspect whether its archive declarations agree with its bytes. This task reads the file without importing the package; a bad hash produces a failed audit, so generating a report never substitutes for passing verification.

    How to use this tool

    1. Select a wheel with its original filename, or load the fixed small-file example.
    2. Review integrity status, verified files, failures, unsupported hashes and unverified signatures.
    3. Download the complete audit and check failures with a trusted producer; the report is not a security or installation approval.

    Supported inputs and limits

    Select exactly one .whl file up to 32 MiB; no pasted archive. ZIP: 10,000 entries, 64 MiB expanded and compression ratio 200 per entry. Unsafe paths, duplicates, CRC failures, encryption, multiple disks and ZIP64 reject. Report: 200,000 JSON values and 10 MiB combined downloads. Any exceeded budget rejects atomically, without a partial audit.

    Supports Wheel-Version 1.0 and Metadata-Version 1.1, 1.2 and 2.1–2.6. Exactly one filename-matching dist-info directory must contain METADATA/WHEEL/RECORD. Names compare after dash/underscore/dot normalization; versions compare literally, without inferring PEP440 equivalence. Optional build and compressed tags are compared with WHEEL. Folded/repeated headers, description body and entry points remain literal data.

    Checks every archive file against canonical URL-safe Base64 SHA256/384/512 RECORD hashes and byte sizes. Missing, unrecorded, mismatched hash/size or inconsistent metadata means integrityStatus=failed. SHA224 and other unknown hashes are unsupported_hash/incomplete; MD5/SHA1 are forbidden and fail. A failed audit still delivers its complete diagnosis; it does not claim success.

    RECORD itself must have empty hash/size. Legacy RECORD.jws/p7s files are signature_not_verified. verified_recorded_payload describes recorded payload checks only, not package authenticity, installation safety, host ABI or platform suitability. Nothing is installed, imported, executed or unpacked to disk.

    Table preview: first 200 rows; cells over 2,000 characters show their first 2,000 plus an ellipsis. Above 20,000 report characters, text/copy becomes a summary preview. Full JSON, CSV and HTML remain downloadable, with unsupported hashes and exceptions visible.

    Worked example

    Example input

    Selected file: demo_pkg-1.2.3-py2.py3-none-any.whl (1461 bytes)
    Example options
    {"secondary":"","params":{"spreadsheetSafe":true},"files":[{"name":"demo_pkg-1.2.3-py2.py3-none-any.whl","type":"application/zip","base64":"UEsDBBQAAAAIADOQRl2Z6IfyFgAAABQAAAAUAAAAZGVtb19wa2cvX19pbml0X18ucHkrKMrMK9FQcvFX8PMPUQgK9VPS5AIAUEsDBBQAAAAIADOQRl2sis5GCQAAAAcAAAAYAAAAZGVtb19wa2cvZGF0YSxxdW90ZWQudHh0y0jNycnn5QIAUEsDBBQAAAAIADOQRl14NdQjlgAAANUAAAAhAAAAZGVtb19wa2ctMS4yLjMuZGlzdC1pbmZvL01FVEFEQVRBXYy9CoMwFEb3PMXFPQF1KWl1crVDC13LRS821Pw0uRZ9+ypCB8fDd87XEmOPjPJBMRnvNBQqF1e0pKEn62V4D+K/5apQpbjRZzKRkmxMYg1xRUqc6qo4Q1j45d3zuxd1lZXqlB0LXoJxg6SZyW1aOnaXPbtP1mJcNNCMNowkoPOOjZuQV0mIhlIXTdgAwojGAa+fSvwAUEsDBBQAAAAIADOQRl0x1dR9WgAAAGkAAAAeAAAAZGVtb19wa2ctMS4yLjMuZGlzdC1pbmZvL1dIRUVMRcg7CoAwEAXAPqfIBVb8dLmA2ImI1lGeGpBd2UTQ22sjljPjBuw0QGMQdrbIclODoT6JOquI8DpvtIQrnQrTiSRqIrUv9jA5m/SE6f3q7HGXxMIgz/c31T8PUEsDBBQAAAAIADOQRl1s0YtXKgAAACsAAAApAAAAZGVtb19wa2ctMS4yLjMuZGlzdC1pbmZvL2VudHJ5X3BvaW50cy50eHSLTs7PK87PSY0vTi7KLCgpjuVKSc3NV7BVAFHxBdnpesk5mVa5iZl5XABQSwMEFAAAAAgAM5BGXe4nb4A4AQAAzAEAAB8AAABkZW1vX3BrZy0xLjIuMy5kaXN0LWluZm8vUkVDT1JEfdDLtkJQAMbxeY/ReJNLLg3OANsldCpJNLFkE6lN7E54+rPOwLDzAr//tz6UPeq4qa6LOC5xSeKYbgbQFQkniF+GnHLjLtBgYtm7wtjY+1bT7uc1WcF3zCzDKxcWsuzKZOwAx8zmaLJQQhLwfNUkQzTpyXwCx4PkfPsBYw1Z3VYQmu9eZZVAzPDLc8at0JKiKpC19nAEpNnEUSzN0TyNyo5QJc7rxUb3Faj4ysQe+iK1sSeprhXh2Bwj/8B39sZx71sBmXu+Fx5UQC1XlqkDjuU/wydL191JvekwYBj1+JMzj/ypQdEQ3IS/peFR0LgVV7MGS50yN0zyDrCM8FnNMGmHuKlLTLq/O6bAWgvKS4VzIXg1t8h8e0gy1XPLi8pYhMZwVND3Dl5c596lYPnPak/Xth4EYPYLUEsBAhQDFAAAAAgAM5BGXZnoh/IWAAAAFAAAABQAAAAAAAAAAAAAALSBAAAAAGRlbW9fcGtnL19faW5pdF9fLnB5UEsBAhQDFAAAAAgAM5BGXayKzkYJAAAABwAAABgAAAAAAAAAAAAAALSBSAAAAGRlbW9fcGtnL2RhdGEscXVvdGVkLnR4dFBLAQIUAxQAAAAIADOQRl14NdQjlgAAANUAAAAhAAAAAAAAAAAAAAC0gYcAAABkZW1vX3BrZy0xLjIuMy5kaXN0LWluZm8vTUVUQURBVEFQSwECFAMUAAAACAAzkEZdMdXUfVoAAABpAAAAHgAAAAAAAAAAAAAAtIFcAQAAZGVtb19wa2ctMS4yLjMuZGlzdC1pbmZvL1dIRUVMUEsBAhQDFAAAAAgAM5BGXWzRi1cqAAAAKwAAACkAAAAAAAAAAAAAALSB8gEAAGRlbW9fcGtnLTEuMi4zLmRpc3QtaW5mby9lbnRyeV9wb2ludHMudHh0UEsBAhQDFAAAAAgAM5BGXe4nb4A4AQAAzAEAAB8AAAAAAAAAAAAAALSBYwIAAGRlbW9fcGtnLTEuMi4zLmRpc3QtaW5mby9SRUNPUkRQSwUGAAAAAAYABgDHAQAA2AMAAAAA"}]}

    Example output

    {"format":"Wheel1.0 archive and RECORD audit","summary":{"files":6,"zipEntries":6,"expandedBytes":848,"verifiedFiles":5,"integrityFailures":0,"unsupportedHashes":0,"unverifiedSignatures":0,"integrityStatus":"verified_recorded_payload","metadataVersion":"2.1","distribution":"demo-pkg","version":"1.2.3","build":null,"packageAuthenticityVerified":false},"filename":"demo_pkg-1.2.3-py2.py3-none-any.whl","distInfo":"demo_pkg-1.2.3.dist-info","filenameTags":["py2-none-any","py3-none-any"],"declaredTags":["py2-none-any","py3-none-any"],"expandedTags":["py2-none-any","py3-none-any"],"metadata":{"headers":[{"key":"Metadata-Version","value":"2.1"},{"key":"Name","value":"demo-pkg"},{"key":"Version","value":"1.2.3"},{"key":"Requires-Dist","value":"requests>=2; python_version>=\"3.8\""},{"key":"Requires-Dist","value":"typing-extensions; python_version<\"3.8\""},{"key":"Summary","value":"example\n continuation"}],"body":"Description plain text.\n"},"wheel":{"headers":[{"key":"Wheel-Version","value":"1.0"},{"key":"Generator","value":"research-fixture"},{"key":"Root-Is-Purelib","value":"true"},{"key":"Tag","value":"py2-none-any"},{"key":"Tag","value":"py3-none-any"}],"body":""},"checks":[{"path":"demo_pkg/__init__.py","status":"verified","algorithm":"sha256","recordedBytes":"20","actualBytes":20,"expectedHash":"sha256=F8c2zPVCDaHJPhFMJQrCClZIt9Dw_04Xg2Xh88L8tzs","actualHash":"sha256=F8c2zPVCDaHJPhFMJQrCClZIt9Dw_04Xg2Xh88L8tzs","sizeMatches":true},{"path":"demo_pkg/data,quoted.txt","status":"verified","algorithm":"sha256","recordedBytes":"7","actualBytes":7,"expectedHash":"sha256=zS7KNTV0HyeorkDDGwxB1AV6enuRKzO5rthkhdHIRnY","actualHash":"sha256=zS7KNTV0HyeorkDDGwxB1AV6enuRKzO5rthkhdHIRnY","sizeMatches":true},{"path":"demo_pkg-1.2.3.dist-info/METADATA","status":"verified","algorithm":"sha256","recordedBytes":"213","actualBytes":213,"expectedHash":"sha256=SxhcJnR7BLHYn_GzYTS3sJMKLlO5dGQ3x5m-V-49HGE","actualHash":"sha256=SxhcJnR7BLHYn_GzYTS3sJMKLlO5dGQ3x5m-V-49HGE","sizeMatches":true},{"path":"demo_pkg-1.2.3.dist-info/WHEEL","status":"verified","algorithm":"sha256","recordedBytes":"105","actualBytes":105,"expectedHash":"sha256=jEDV00BUvf0mfqCD6F5La3jcXU5C292o1F1-WeLXafs","actualHash":"sha256=jEDV00BUvf0mfqCD6F5La3jcXU5C292o1F1-WeLXafs","sizeMatches":true},{"path":"demo_pkg-1.2.3.dist-info/entry_points.txt","status":"verified","algorithm":"sha256","recordedBytes":"43","actualBytes":43,"expectedHash":"sha256=ICVibknf5VupjYGwRd7GBZr36AzhXFyUAdNPDbLKlsc","actualHash":"sha256=ICVibknf5VupjYGwRd7GBZr36AzhXFyUAdNPDbLKlsc","sizeMatches":true},{"path":"demo_pkg-1.2.3.dist-info/RECORD","status":"record_self_unhashed","algorithm":null,"recordedBytes":null,"actualBytes":460,"expectedHash":null,"actualHash":null,"sizeMatches":null}],"issues":[],"entryPoints":"[console_scripts]\ndemo = demo_pkg.cli:main\n","files":[{"path":"demo_pkg/__init__.py","bytes":20},{"path":"demo_pkg/data,quoted.txt","bytes":7},{"path":"demo_pkg-1.2.3.dist-info/METADATA","bytes":213},{"path":"demo_pkg-1.2.3.dist-info/WHEEL","bytes":105},{"path":"demo_pkg-1.2.3.dist-info/entry_points.txt","bytes":43},{"path":"demo_pkg-1.2.3.dist-info/RECORD","bytes":460}],"unverifiedExceptions":["RECORD self entry has no hash/size","legacy signatures are not verified","no package authenticity/ABI/security/installation verdict"]}

    When something does not work

    Use the producer’s complete original wheel and filename. Check the unique dist-info directory, required headers, tags, canonical RECORD CSV and paths. Obtain a trusted replacement for failed hashes; do not rewrite hashes to hide a mismatch. For exceeded budgets, generate a smaller archive with the producer.

    Frequently asked questions

    Why can a failed hash still have downloads?

    The download is a failed diagnosis: integrityStatus is explicitly failed. Each declaration and observed value remains available for investigation.

    Does this prove a wheel is safe?

    No. RECORD is not a trusted signature. This tool does not assess code safety, author identity or your machine ABI.

    Why is SHA224 unsupported?

    This contract implements SHA256/384/512 only. SHA224 is explicitly unsupported, so its files are never claimed fully verified.

    Documentation & further reading

    Related tools