Skip to main content

Send and receive a public model invitation

An invitation is a small JSON file containing the original signed public manifest, candidate public keys and up to 16 HTTP(S) mirror bases. It carries the complete declared model and companion selection without weights, private keys, a Foxlight account or a required online resolver. Reading it is offline. Sending this file does not start a server or prove that any mirror is reachable.

Publisher: prepare the handoff​

First create and verify a separate public publication, then host its manifests/ and objects/sha256/ directories on an ordinary read-only mirror. Export an invitation using that public copy's exact identity and keys:

model-preserve invite PUBLIC_MANIFEST_SHA256 --store /public/model \
--trust publisher-trust.json --mirror https://mirror.example/models \
--mirror https://another.example/models --output model.invitation.json

Export reads only bounded signed metadata. It does not rehash weights or check hosting. The output must be a new file. Mirror bases cannot contain credentials, query strings, fragments, filesystem paths or non-HTTP schemes. They are hints, not authority; they can change without changing the model's signed identity. Do not put private URLs or access tokens in an invitation.

Send the file and separately authenticate its manifest ID and publisher key fingerprints through a channel the recipient trusts. An attacker replacing the invitation and its keys can create a different self-signed model. A self-signature alone does not establish the publisher's identity or registry provenance.

Recipient: inspect before connecting​

model-preserve inspect-invitation model.invitation.json

This checks candidate signatures, exact manifest identity, public audience and structural bounds. It reports the name, total bytes, complete member list, fingerprints, claimed threshold and mirrors. It makes no network requests and does not verify stored weights, identify the publisher, enroll keys, restore a model or authorize execution. The original model-card data remains opaque to this container inspection, including unfamiliar future card profiles.

Use an independently retained trust file when available:

model-preserve fetch-invitation model.invitation.json \
--trust publisher-trust.json --store ./received \
--max-bytes 21474836480 --deadline-seconds 3600 --operation-id receive-model

For first contact, deliberately accept independently authenticated fingerprints instead of supplying --trust:

model-preserve fetch-invitation model.invitation.json \
--accept-key ed25519:REPLACE_WITH_AUTHENTICATED_FINGERPRINT \
--store ./received --operation-id receive-model --save-trust accepted-trust.json

Repeat --accept-key for a threshold publication. Authenticate the intended threshold too: it is candidate policy supplied by the sender. This creates a choice for this exact copy, not global enrollment or a replacement for an existing revocation policy. Keep the selected keys, manifest ID and invitation for later verification. The default object allowance is 20 GiB; choose a sufficient finite allowance for the model and possible retries. Reuse the same operation ID, store, trust and transfer policy after interruption to retain the cumulative allowance. Changing those inputs under the same ID refuses rather than resetting its budget.

--save-trust retains the selected public policy for later verification and recovery-kit export. It writes no private key. An existing identical policy is reused on retry; a different one refuses before networking. This is a local file you explicitly retain, without enrolling its keys globally or as registry keys. The selected policy must authenticate this invitation before it is saved. The final file appears atomically after its complete contents are flushed; a crash may leave a temporary file of public keys, without a partial final policy.

The downloader authenticates the included manifest before contacting a mirror, then authenticates the fetched manifest before requesting weights. Every actual object passes its signed length and SHA-256 check before completion. It tries only the supplied mirrors. HTTP and non-global addresses require separate --allow-http and --allow-private opt-ins; neither flag disables HTTPS certificate checks. See transfer limits.

Downloading does not share, seed, install, execute or grant registry authority. The network address used by the downloader and requested files are visible to the contacted mirrors. This is direct receiving with integrity checks, without an anonymity guarantee.

Keep and recover independently​

The downloaded store works with ordinary verify, kit-export and restore. Those commands require the same independently selected trust profile and exact manifest ID. See trust and recovery. A complete recovery kit is also the handoff when recipients cannot reach a public mirror or when metadata exceeds the invitation's 8 MiB bound. Kits and invitations are not encrypted. Keep recovery material outside the original host's failure domain.

Format and application API​

The schema is foxlight.model-preservation.invitation.v1. The wrapper is not separately signed. Its embedded DSSE payload and original signatures are preserved, so changing transport hints cannot alter the signed bundle or grant mirror authority. Unknown wrapper fields/versions, duplicate JSON keys, bad signatures/digests, private members and unsafe mirror syntax refuse before receiving. JSON schema describes shape; cryptographic and custom path/graph checks require the verifier.

invitations.read_invitation(bytes) is a bounded offline inspector. create_invitation(store, id, trust, mirrors) reads only selected metadata. authenticate_invitation(invitation, trust) uses caller-selected trust. accept_invitation_keys(invitation, key_ids) makes an explicit first-contact choice; a UI must make the trust implications clear. fetch_invitation(invitation, trust, destination, policy, operation_id=...) reuses the ordinary downloader and its cancellation/progress controls. Run it in a bounded worker. No peer-supplied value authorizes reading a local host path.