A fully working example of a custom format handler plugin for Artifact Keeper. This plugin handles Unity .unitypackage files (gzipped tarballs), demonstrating real-world format validation, metadata extraction, and index generation.
Use this repo as a starting point for building your own plugins. Fork it, change the format key, and implement your logic.
| Capability | Description |
|---|---|
| Validate | Checks gzip magic bytes and correct file extension |
| Parse metadata | Extracts version from path/filename, detects content type |
| Generate index | Creates a unity-index.json listing all packages in a repository |
- Rust (stable)
- The
wasm32-wasip1target (installed automatically viarust-toolchain.toml)
# Clone this repo
git clone https://github.com/artifact-keeper/artifact-keeper-example-plugin.git
cd artifact-keeper-example-plugin
# Build the WASM component
cargo build --release
# Output: target/wasm32-wasip1/release/unity_format_plugin.wasmUnit tests run on the host target (not WASM):
cargo test --target $(rustc -vV | grep host | awk '{print $2}')curl -X POST https://your-registry/api/v1/plugins/install/git \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://github.com/artifact-keeper/artifact-keeper-example-plugin.git",
"ref": "v0.1.0"
}'Download the ZIP from the Releases page, then:
curl -X POST https://your-registry/api/v1/plugins/install/zip \
-H "Authorization: Bearer $TOKEN" \
-F "file=@unity-format-plugin-v0.1.0.zip"curl -X POST https://your-registry/api/v1/plugins/install/local \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"path": "/path/to/artifact-keeper-example-plugin"}'- Fork this repo or use it as a template
- Update
plugin.tomlwith your format key, extensions, and description - Implement the four functions in
src/lib.rs:format_key()-- return your unique format identifierparse_metadata()-- extract metadata from uploaded artifactsvalidate()-- reject invalid artifacts before storagegenerate_index()-- create repository index files (or returnNone)
- The WIT contract in
wit/format-plugin.witdefines the interface -- don't modify it - Push a tag to trigger the release workflow
.
├── .cargo/config.toml # Default WASM target
├── .github/workflows/
│ ├── ci.yml # Lint + test + build on push/PR
│ └── release.yml # Build + package + GitHub Release on tag
├── src/lib.rs # Plugin implementation
├── wit/format-plugin.wit # WIT contract (from Artifact Keeper)
├── plugin.toml # Plugin manifest
├── Cargo.toml # Rust project config
└── rust-toolchain.toml # Rust toolchain + WASM target
Plugins implement the artifact-keeper:format@1.0.0 interface:
interface handler {
record metadata {
path: string,
version: option<string>,
content-type: string,
size-bytes: u64,
checksum-sha256: option<string>,
}
format-key: func() -> string;
parse-metadata: func(path: string, data: list<u8>) -> result<metadata, string>;
validate: func(path: string, data: list<u8>) -> result<_, string>;
generate-index: func(artifacts: list<metadata>) -> result<option<list<tuple<string, list<u8>>>>, string>;
}MIT