Skip to content

Publishing your own app

This tutorial takes you from "I have a binary release" to "anyone in the world can run my app with 0install run https://example.com/myapp.xml". We'll use 0template to generate the feed, 0publish to sign it, and GitHub Pages to host the result.

By the end you will have:

  • A signed feed at https://YOURNAME.github.io/myapp/myapp.xml.
  • A GPG public key alongside it, so 0install can verify signatures.
  • A repeatable command to add new versions.

Prerequisites

  • A GPG key you control. If you don't have one, run gpg --full-generate-key and accept the defaults. Just make sure to set a name and email address.
  • A binary release of your app accessible over HTTPS, e.g. an attached release on GitHub, an S3 bucket, or your own web server.
  • The Zero Install tools:
0install add 0template https://apps.0install.net/0install/0template.xml
0install add 0publish https://apps.0install.net/0install/0publish.xml
0install add feedlint https://apps.0install.net/0install/feedlint.xml

1. Create the GitHub repository

Create a new GitHub repo named myapp (or whatever your app is called). Enable GitHub Pages in Settings → Pages, with the source set to Deploy from a branch / main branch / root.

Once enabled, GitHub gives you a URL of the form https://YOURNAME.github.io/myapp/. This is the URL your feed will live under. Pick a permanent file name now; let's say myapp.xml. The full feed URL is then:

https://YOURNAME.github.io/myapp/myapp.xml

Attention

This URL must not change. Other feeds and your users' caches will reference it forever.

2. Create a template

Clone the repo and create a template:

git clone https://github.com/YOURNAME/myapp.git
cd myapp
0template myapp.xml.template
# choose option 2 (binary template)

Open myapp.xml.template in an editor and fill in the metadata. A minimal template for a cross-platform app might look like:

<?xml version="1.0"?>
<interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>MyApp</name>
  <summary>does something useful</summary>
  <description>A longer description, often copied from your project's homepage.</description>
  <homepage>https://github.com/YOURNAME/myapp</homepage>
  <icon href="https://YOURNAME.github.io/myapp/myapp.png" type="image/png"/>

  <feed-for interface="https://YOURNAME.github.io/myapp/myapp.xml"/>

  <group license="MIT">
    <command name="run" path="myapp"/>

    <implementation arch="Linux-x86_64" version="{version}" stability="stable">
      <manifest-digest/>
      <archive href="https://github.com/YOURNAME/myapp/releases/download/v{version}/myapp-{version}-linux-x64.tar.gz"/>
    </implementation>
    <implementation arch="Windows-x86_64" version="{version}" stability="stable">
      <manifest-digest/>
      <archive href="https://github.com/YOURNAME/myapp/releases/download/v{version}/myapp-{version}-windows-x64.zip"/>
    </implementation>
  </group>
</interface>

Match the runner, <command> and bindings to your runtime (see the relevant guide). The {version} placeholder is filled in by 0template.

3. Generate a feed for the first version

Tag a release in your project (e.g. v1.0), upload the binary archives to the GitHub release, and run:

0template myapp.xml.template version=1.0

This downloads each archive, computes its manifest digest, and writes myapp-1.0.xml. Test it locally:

0install run myapp-1.0.xml
feedlint myapp-1.0.xml

Tip

Fix any feedlint warnings before continuing. They catch missing metadata, broken URLs and other issues that would surface later when users try to install your feed.

4. Create and sign the master feed

The per-version file produced by 0template is an intermediate artifact; users will fetch the master feed at the permanent URL, so that is the one that needs to be signed. Copy the per-version feed to its permanent name, set the canonical URL on it and add an XML signature:

cp myapp-1.0.xml myapp.xml
0publish myapp.xml --set-interface-uri=https://YOURNAME.github.io/myapp/myapp.xml --xmlsign

You'll be prompted for your GPG passphrase. The signed feed contains a Base64 signature block at the end as an XML comment.

0publish also writes a public key file (e.g. 1234ABCD.gpg) alongside the feed. This is what users' 0install installs need to verify the signature.

5. Publish to GitHub Pages

Commit and push:

git add myapp.xml *.gpg myapp.png
git commit -m "Publish 1.0"
git push

Once GitHub Pages has rebuilt (a minute or two), the feed is live. Test it from a fresh machine or by clearing your local cache:

0install run https://YOURNAME.github.io/myapp/myapp.xml

The first time someone runs your feed, 0install fetches the .gpg file from the same directory and asks the user to confirm the key fingerprint. After that the feed is cached and signature-checked automatically.

6. Adding new versions

When you tag v1.1, generate the per-version feed and merge it straight into the master feed:

0template myapp.xml.template version=1.1
0publish myapp.xml --add-from=myapp-1.1.xml

--add-from copies the new <implementation> into the existing master feed and re-signs it automatically with the same key the master feed was already signed with. The per-version myapp-1.1.xml is intermediate and doesn't need to be signed or published. Push the updated master feed.

This works fine for a single feed you publish by hand. To get CI to do the signing for you (so the GPG private key isn't on every machine you publish from), see Automating a single feed with CI. Once you have several feeds and want stable URLs for keys and a shared catalog, switch to Managing multiple feeds with 0repo.

7. Provide a native installer (optional)

End-users who don't yet have Zero Install installed can't 0install run your feed. To give them a download that "just works", wrap the feed in a bootstrapper using 0bootstrap.

0install add 0bootstrap https://apps.0install.net/0install/0bootstrap.xml
0bootstrap https://YOURNAME.github.io/myapp/myapp.xml

This produces a single MyApp.exe (named after the feed's <name>, with the feed's icon) that downloads Zero Install on first launch if necessary and then runs your app. You can host the resulting .exe next to the feed on GitHub Pages or attach it to your release.

To make the generated executable register the app in the Start menu and file associations, pass the desired integration arguments like this:

0bootstrap https://YOURNAME.github.io/myapp/myapp.xml --integrate-args="--add-standard"

For a fully offline installer, combine 0bootstrap with 0install export so the bootstrapper bundles the implementation alongside Zero Install:

0install export --include-zero-install https://YOURNAME.github.io/myapp/myapp.xml myapp-export
0bootstrap https://YOURNAME.github.io/myapp/myapp.xml --content=myapp-export\content

See the 0bootstrap docs for instructions how to produce a .deb or .rpm package.

Troubleshooting

gpg: signing failed: secret key not available
The feed was last signed with a key you don't have. Pass -k YOURKEY to 0publish to switch keys.
Manifest digest sha256new=... does not match expected ...
The archive at the upstream URL changed after you generated the feed. Re-run 0template and re-sign.