Packaging and Distribution

May 9, 2026 · View on GitHub

How to ship a plugin to users — local sideloading, marketplace submission, and version updates.

Distribution artifact: a zip

A distributable plugin is a single zip named <plugin_id>-<version>.zip containing the compiled dylib and any companion files Osaurus surfaces in the UI:

mYplugin-0.1.0.zip
├── MyPlugin.dylib              # the compiled plugin
├── web/                        # optional static web UI
│   ├── index.html
│   └── assets/
├── SKILL.md                    # optional skill bundle (or skills/ dir of them)
├── README.md                   # surfaces in the plugin detail page
└── CHANGELOG.md                # surfaces in the plugin detail page

Build the zip with:

osaurus tools package <plugin_id> <version> [dylib_path]

osaurus tools package auto-detects .dylib files in the current directory, and includes web/, SKILL.md, README.md, CHANGELOG.md if present. Output: <plugin_id>-<version>.zip in the current directory.

Installed layout (created by Osaurus)

When a user runs osaurus tools install <zip> (or installs from the marketplace), Osaurus unpacks the zip into the user's tools directory and writes a receipt.json recording the install metadata, hashes, and signatures. The author never writes receipt.json — it's host-generated.

~/Library/Application Support/Osaurus/Tools/my-plugin/
├── 0.1.0/                       # version directory (created on install)
│   ├── MyPlugin.dylib           # from the zip
│   ├── receipt.json             # generated by Osaurus, not the author
│   ├── web/                     # from the zip
│   ├── SKILL.md                 # from the zip
│   ├── README.md                # from the zip
│   ├── CHANGELOG.md             # from the zip
│   └── .user_consent            # created on first user grant (release builds only)
├── current → 0.1.0              # symlink Osaurus maintains

The current symlink lets multiple versions coexist and atomically switch on upgrade.

Build configuration

Swift

Your Package.swift must produce a dynamic library:

.library(name: "my-plugin", type: .dynamic, targets: ["MyPlugin"])

For release builds:

swift build -c release --product my-plugin
strip -x .build/release/libmy-plugin.dylib

Symbols you must export:

  • osaurus_plugin_entry_v2(host: *const osr_host_api) -> *const osr_plugin_api

The legacy osaurus_plugin_entry symbol still loads but is not recommended for new plugins (no host API access).

Rust

Cargo.toml:

[lib]
crate-type = ["cdylib"]

Build:

cargo build --release
strip target/release/libmy_plugin.dylib

Code signing

Required for release builds. Without a signature, Osaurus refuses to load the plugin.

codesign --sign "Developer ID Application: Your Name (TEAMID)" \
    --options runtime \
    --timestamp \
    MyPlugin.dylib

Verify:

codesign -vv MyPlugin.dylib

For Apple notarization (required when distributing outside the Osaurus marketplace), submit a zip:

zip MyPlugin.zip MyPlugin.dylib
xcrun notarytool submit MyPlugin.zip \
    --apple-id you@example.com \
    --team-id TEAMID \
    --password '@keychain:notary' \
    --wait

receipt.json (host-generated, informational)

After install, Osaurus writes a receipt.json next to the dylib recording install metadata:

{
  "plugin_id": "dev.example.MyPlugin",
  "version": "0.1.0",
  "installed_at": "2026-05-08T17:42:01Z",
  "dylib_filename": "MyPlugin.dylib",
  "dylib_sha256": "sha256-...",
  "platform": "macOS",
  "arch": "arm64",
  "public_keys": {...},
  "artifact": { "url": "...", "sha256": "...", "minisign": null, "size": 12345 }
}

osaurus tools verify reads each receipt and re-hashes the dylib to confirm the bundle hasn't been tampered with after install. As an author you don't write or ship receipt.json — Osaurus generates it from the artifact's metadata when the user installs your zip.

On first install in release builds, Osaurus shows the user a permission summary and creates .user_consent once they grant. Until that file exists the plugin fails to load with consent_required: prefixed errors. DEBUG builds (running osaurus tools dev) skip consent.

What's surfaced in the prompt:

  • Plugin name, version, author
  • Declared requirements per tool (e.g. network, filesystem)
  • Description from the manifest
  • Link to your README

Plan your requirements and description fields with this prompt in mind — they're the user's first impression.

Local distribution (sideload)

After running osaurus tools package, install the zip you produced:

osaurus tools install ./my-plugin-0.1.0.zip

Osaurus unpacks the zip into the tools directory, generates a receipt.json, updates the current symlink, and triggers a reload. To trigger a reload manually after files change in place:

osaurus tools reload

For an even tighter dev loop, use osaurus tools dev — it builds, installs, and watches your sources for changes.

Marketplace submission

The Osaurus plugin registry is github.com/osaurus-ai/osaurus-tools.

Steps:

  1. Tag a release in your plugin repo (git tag v0.1.0 && git push --tags)
  2. The CI workflow .github/workflows/release.yml (included in the scaffold) builds, signs, notarizes, and attaches the bundle to the GitHub Release
  3. Open a PR against osaurus-ai/osaurus-tools adding your plugin's metadata to registry.json
  4. The Osaurus team reviews for security and DX, then merges

Once merged, your plugin appears in the in-app marketplace within ~24 hours.

Version updates

Semver guidance:

  • Patch — bug fixes, no manifest changes, no new permissions
  • Minor — new tools or routes, no removed/renamed capabilities, no new top-level permissions
  • Major — breaking changes (renamed tools, removed routes, new required secrets)

When publishing a new version:

  1. Tag the new version (git tag v0.2.0)
  2. Bump version in your manifest's get_manifest
  3. Update osaurus-plugin.json
  4. Add a CHANGELOG.md entry (surfaced in the plugin detail page)
  5. Open a PR against the registry to update the version pin

Existing users will be prompted to upgrade. Their .user_consent carries forward unless your requirements widened, in which case re-consent is required.

What you can include in a bundle

  • The dylib (required)
  • web/ static UI directory
  • skills/ directory of SKILL.md files (auto-loaded into the agent)
  • README, CHANGELOG, LICENSE (auto-surfaced in the UI)

What you should NOT include:

  • Any source code (ship a separate source repo)
  • Secrets, API keys, .env files
  • Files outside web/ that you'd be embarrassed to have a user cat

See also