# AssetForge
AssetForge is a Rust library for discovering and archiving freely licensed 3D assets through official provider APIs. The first provider is Poly Haven, with glTF, FBX, Blender, and USD bundles at 1K through 8K texture resolutions.
Poly Haven assets are CC0. Access to `api.polyhaven.com` is governed separately and is free for non-commercial, reasonable use; commercial API usage requires permission from Poly Haven. Applications must send a unique User-Agent and show Poly Haven attribution next to API-derived content.
AssetForge does not scrape websites, bypass access controls, or change provider licenses.
## Example
```rust
use assetforge::{
ArchiveOptions, AssetForge, AssetProvider, AssetResolution, AssetTarget,
};
let forge = AssetForge::with_user_agent(
AssetProvider::PolyHaven,
"my-asset-tool/1.0 (contact@example.com)",
)?;
let assets = forge.discover(&AssetTarget::Search("wooden chair".into()))?;
let options = ArchiveOptions {
resolution: AssetResolution::TwoK,
..ArchiveOptions::default()
};
let summary = forge.archive_assets(&assets[..1], &options)?;
assert!(summary.is_success());
# Ok::<(), assetforge::AssetForgeError>(())
```
Downloads are streamed to sibling temporary files, bounded by a configurable per-bundle limit, checked against provider byte counts and MD5 hashes, and renamed only after verification. Each archived asset includes `assetforge-asset.json` with source, license, attribution, selected bundle, dependencies, and checksums.
Target forms include a canonical asset id, `https://polyhaven.com/a/<id>`, `search:<text>`, `category:<name>`, `top`, and `latest`.
The default archive bundle is dependency-complete 2K glTF. Set `max_download_bytes` to `0` only when intentionally allowing unbounded bundles.
AssetForge disables HTTP redirects, bounds provider metadata and error responses, accepts downloads only from Poly Haven's exact HTTPS download host, rejects traversal/control-file collisions, stages complete bundles before replacement, and verifies every declared file through capability-relative handles before honoring `skip_existing`.
## Development
```bash
cargo fmt
cargo test
cargo clippy --all-targets -- -D warnings
```