{"id":46937461,"url":"https://github.com/hamzaydia/verifyfetch","last_synced_at":"2026-04-02T21:16:33.051Z","repository":{"id":334555007,"uuid":"1141810923","full_name":"hamzaydia/verifyfetch","owner":"hamzaydia","description":"Resumable, verified downloads for large browser files. Fail at 3.8GB, resume from 3.8GB.","archived":false,"fork":false,"pushed_at":"2026-02-02T23:36:44.000Z","size":257,"stargazers_count":146,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-26T11:30:43.377Z","etag":null,"topics":["ai-models","browser","cdn","fetch","hash","integrity","javascript","nodejs","npm","security","sha256","sri","streaming","subresource-integrity","supply-chain","typescript","verification","wasm","webassembly"],"latest_commit_sha":null,"homepage":"https://verifyfetch.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hamzaydia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"hamzaydia"}},"created_at":"2026-01-25T13:26:01.000Z","updated_at":"2026-03-14T03:11:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hamzaydia/verifyfetch","commit_stats":null,"previous_names":["hamzaydia/verifyfetch"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/hamzaydia/verifyfetch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamzaydia%2Fverifyfetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamzaydia%2Fverifyfetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamzaydia%2Fverifyfetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamzaydia%2Fverifyfetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hamzaydia","download_url":"https://codeload.github.com/hamzaydia/verifyfetch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamzaydia%2Fverifyfetch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31316425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-models","browser","cdn","fetch","hash","integrity","javascript","nodejs","npm","security","sha256","sri","streaming","subresource-integrity","supply-chain","typescript","verification","wasm","webassembly"],"created_at":"2026-03-11T06:00:28.195Z","updated_at":"2026-04-02T21:16:33.044Z","avatar_url":"https://github.com/hamzaydia.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/hamzaydia/verifyfetch/main/.github/logo.svg\" width=\"80\" alt=\"VerifyFetch\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eVerifyFetch\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eYour user downloads a 4GB AI model. It fails at 3.8GB.\u003cbr /\u003eVerifyFetch resumes from 3.8GB and verifies every byte.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/hamzaydia/verifyfetch/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/hamzaydia/verifyfetch/ci.yml?style=flat-square\u0026color=10b981\" alt=\"CI\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/verifyfetch\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/verifyfetch?style=flat-square\u0026color=10b981\" alt=\"npm\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/verifyfetch\"\u003e\u003cimg src=\"https://img.shields.io/bundlephobia/minzip/verifyfetch?style=flat-square\u0026color=10b981\" alt=\"size\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/hamzaydia/verifyfetch\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/hamzaydia/verifyfetch?style=flat-square\u0026color=10b981\" alt=\"stars\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/hamzaydia/verifyfetch/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/verifyfetch?style=flat-square\u0026color=10b981\" alt=\"license\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n```bash\nnpm install verifyfetch\n```\n\n```typescript\nimport { verifyFetch } from 'verifyfetch';\n\nconst response = await verifyFetch('/model.bin', {\n  sri: 'sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek='\n});\n```\n\n**That's it.** If the hash doesn't match, it throws. Your users are protected.\n\n---\n\n## Why?\n\nLoading large files in the browser is broken:\n\n- **Memory explosion** — `crypto.subtle.digest()` buffers the entire file. 4GB model = 4GB RAM = crash.\n- **No resume** — Network drops at 3.8GB? Start over from zero.\n- **Silent corruption** — CDN serves bad bytes? You won't know until inference gives garbage.\n- **Supply chain attacks** — [polyfill.io](https://sansec.io/research/polyfill-supply-chain-attack) compromised 100K+ sites.\n\nVerifyFetch fixes all of these: **streaming verification in constant memory, resumable downloads, fail-fast corruption detection.**\n\n---\n\n## Use with Transformers.js\n\n```bash\nnpm install @verifyfetch/transformers @huggingface/transformers\n```\n\n**1. Generate hashes for your model:**\n\n```bash\nnpx @verifyfetch/cli hash-model Xenova/distilbert-base-uncased-finetuned-sst-2-english\n```\n\n**2. Use it:**\n\n```typescript\nimport { verifiedPipeline } from '@verifyfetch/transformers';\n\nconst classifier = await verifiedPipeline(\n  'sentiment-analysis',\n  'Xenova/distilbert-base-uncased-finetuned-sst-2-english',\n  { manifestUrl: '/models.vf.manifest.json' }\n);\n\nconst result = await classifier('I love this!');\n// [{ label: 'POSITIVE', score: 0.99 }]\n```\n\nEvery file is verified and resumable. If the connection drops, it picks up where it left off.\n\n---\n\n## Use with WebLLM\n\n```bash\nnpm install @verifyfetch/webllm @mlc-ai/web-llm\n```\n\n```typescript\nimport { createVerifiedMLCEngine } from '@verifyfetch/webllm';\n\nconst engine = await createVerifiedMLCEngine('Phi-3-mini-4k-instruct-q4f16_1-MLC', {\n  manifestUrl: '/models.vf.manifest.json',\n  onProgress: ({ file, percent, resumed }) =\u003e {\n    console.log(`${file}: ${percent}%${resumed ? ' (resumed)' : ''}`);\n  }\n});\n```\n\n---\n\n## Use with any file\n\n```typescript\nimport { verifyFetchResumable } from 'verifyfetch';\n\nconst model = await verifyFetchResumable('/phi-3-mini.gguf', {\n  chunked: manifest.artifacts['/phi-3-mini.gguf'].chunked,\n  persist: true,\n  onProgress: ({ percent, resumed }) =\u003e {\n    console.log(`${percent}%${resumed ? ' (resumed)' : ''}`);\n  }\n});\n```\n\nOr use a **Service Worker** to verify every fetch without changing any code:\n\n```typescript\n// sw.js\nimport { createVerifyWorker } from 'verifyfetch/worker';\n\ncreateVerifyWorker({\n  manifestUrl: '/vf.manifest.json',\n  include: ['*.wasm', '*.bin', '*.onnx', '*.safetensors'],\n});\n```\n\n```typescript\n// app.js — no changes needed\nconst model = await fetch('/model.bin');  // automatically verified\n```\n\n---\n\n## How it works\n\n1. You generate SHA-256 hashes for your files (CLI does this in one command)\n2. Hashes are stored in a manifest JSON file you ship with your app\n3. VerifyFetch downloads files in chunks, verifying each one as it arrives\n4. If a chunk is corrupt, it stops immediately — no wasting bandwidth\n5. If the connection drops, it resumes from the last verified chunk via IndexedDB\n6. Memory stays constant (~2MB) regardless of file size\n\n---\n\n## Packages\n\n| Package | Description |\n|---------|-------------|\n| [`verifyfetch`](https://www.npmjs.com/package/verifyfetch) | Core library — verified fetch, streaming, resumable downloads |\n| [`@verifyfetch/transformers`](https://www.npmjs.com/package/@verifyfetch/transformers) | Drop-in Transformers.js integration |\n| [`@verifyfetch/webllm`](https://www.npmjs.com/package/@verifyfetch/webllm) | Drop-in WebLLM integration |\n| [`@verifyfetch/cli`](https://www.npmjs.com/package/@verifyfetch/cli) | CLI to generate hashes and manifests |\n| [`@verifyfetch/manifests`](https://www.npmjs.com/package/@verifyfetch/manifests) | Pre-computed hashes for popular models |\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eFull API Reference\u003c/strong\u003e\u003c/summary\u003e\n\n### `verifyFetch(url, options)`\n\n```typescript\nconst response = await verifyFetch('/file.bin', {\n  sri: 'sha256-...',\n  onFail: 'block',                // 'block' | 'warn' | { fallbackUrl }\n  onProgress: (bytes, total) =\u003e {},\n});\n```\n\n### `verifyFetchStream(url, options)`\n\nStreaming verification with constant memory.\n\n```typescript\nconst { stream, verified } = await verifyFetchStream('/file.bin', {\n  sri: 'sha256-...',\n});\n\nfor await (const chunk of stream) {\n  await processChunk(chunk);\n}\n\nawait verified;  // throws if verification fails\n```\n\n### `verifyFetchResumable(url, options)`\n\nResumable downloads with chunked verification via IndexedDB.\n\n```typescript\nconst result = await verifyFetchResumable('/model.bin', {\n  chunked: manifest.artifacts['/model.bin'].chunked,\n  persist: true,\n  onProgress: ({ bytesVerified, totalBytes, resumed, speed, eta }) =\u003e {},\n});\n```\n\n### `verifyFetchFromSources(sri, path, options)`\n\nMulti-CDN failover.\n\n```typescript\nconst response = await verifyFetchFromSources('sha256-...', '/file.bin', {\n  sources: ['https://cdn1.com', 'https://cdn2.com'],\n  strategy: 'race',  // 'sequential' | 'race' | 'fastest'\n});\n```\n\n### `createVerifyWorker(options)` (Service Worker)\n\n```typescript\ncreateVerifyWorker({\n  manifestUrl: '/vf.manifest.json',\n  include: ['*.wasm', '*.bin', '*.onnx'],\n  onFail: 'block',\n});\n```\n\n### CLI\n\n```bash\nnpx verifyfetch sign \u003cfiles...\u003e                    # Generate SRI hashes\nnpx verifyfetch sign --chunked \u003cfiles...\u003e          # With chunk hashes\nnpx verifyfetch hash-model \u003cmodel-id\u003e              # Hash a HuggingFace model\nnpx verifyfetch enforce --manifest vf.manifest.json # Verify in CI\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSecurity Model\u003c/strong\u003e\u003c/summary\u003e\n\n**Protects against:** CDN compromise, MITM attacks, accidental corruption, supply chain attacks.\n\n**Does NOT protect against:** Compromised build pipeline (you ship wrong hashes). Use `verifyfetch enforce` in CI for that.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eContributing\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\npnpm install\npnpm build:wasm   # Requires Rust\npnpm build\npnpm test         # 437 tests\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://verifyfetch.com\"\u003eDocs\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://github.com/hamzaydia/verifyfetch\"\u003eGitHub\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://github.com/hamzaydia/verifyfetch/releases\"\u003eChangelog\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003csub\u003eApache-2.0 License\u003c/sub\u003e\n\u003c/p\u003e\n","funding_links":["https://github.com/sponsors/hamzaydia"],"categories":["javascript","Libraries"],"sub_categories":["Cryptography"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamzaydia%2Fverifyfetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhamzaydia%2Fverifyfetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamzaydia%2Fverifyfetch/lists"}