# Publishing (/extensions/publishing)



The store keeps one promise: &#x2A;*the reviewed source is the shipped package.**
You submit source, pinned to a commit; the store's own build compiles it,
packages it, signs it and publishes it. No binary you built is ever
published, so nothing can be swapped after review.

That is also why a package you build yourself installs as **not signed**: only
the store's build signs, and Lumi says so rather than pretending otherwise.
(A package downloaded from the store and installed from a file keeps its
signature if its `.sig` file sits beside it, as `<package>.sig`.)

## Submitting [#submitting]

<Steps>
  <Step>
    ### Put the source in a public repository [#put-the-source-in-a-public-repository]

    A Rust crate depending on `lumi-extension-api`, with `manifest.toml` beside
    its `Cargo.toml`. Anything under `ui/` is packed exactly as it is in the
    repository, so commit it as source — see
    [Building for the store](/extensions/windows#what-the-page-is).
  </Step>

  <Step>
    ### Open a pull request against the store [#open-a-pull-request-against-the-store]

    The store lives at
    [github.com/thiennguyen93/lumi-store](https://github.com/thiennguyen93/lumi-store).
    The pull request:

    * adds your repository as a submodule under `ext/<your-extension-id>`, pinned
      to the exact commit you are submitting, and
    * adds one entry to `extensions.toml`:

    ```toml
    [[extension]]
    id = "dev.you.thing"       # must equal the id in your manifest.toml
    path = "ext/dev.you.thing" # the submodule
    subdir = "."               # where Cargo.toml and manifest.toml live inside it
    ```
  </Step>

  <Step>
    ### Review [#review]

    Review is by a person, of the source: the capabilities the manifest declares
    against what the code actually calls, nothing phoning home, and no
    misbehaviour shipped as a feature. The pull request's checks also hold the
    manifest to the rules Lumi's installer applies, so a manifest Lumi would
    refuse fails there rather than on somebody's Mac.
  </Step>

  <Step>
    ### Merge [#merge]

    Merging is publishing: the store rebuilds every listed extension from source,
    signs the packages and rewrites the index Lumi reads.
  </Step>

  <Step>
    ### How people find it [#how-people-find-it]

    **Store**, in the row above the list in **Settings → Extensions**, opens the
    store's page on lumikeys.app in the browser. Its Install button hands the
    extension back to Lumi, which downloads the signed package and shows the same
    review sheet a file install gets — this time saying the package came from the
    store and was verified against its key. Nothing installs until the person
    presses the sheet's button — **Install**, or &#x2A;*Continue…** when the extension
    ships [an installer of its own](/extensions/windows#an-installer-of-your-own).
  </Step>
</Steps>

## Updates [#updates]

An update is a pull request that moves your submodule to a new commit and
raises `version` in the manifest. The `id` never changes — it is how Lumi
knows the new package replaces the old one.

Lumi reads the store's index at launch, when its window comes forward, and
once an hour in the background, at most every six hours — and whenever the
person presses &#x2A;*Check for updates…** in Settings → Extensions. An update is
offered when the store's `version` is higher than the installed one, compared
as numbers, so `1.10.0` is newer than `1.9.0`. Both must be written
`major.minor.patch`; a version of any other shape is never offered as an
update. An update whose `min-lumi-version` is above the running Lumi is
listed as needing a newer Lumi.

An update keeps the person's settings, and your
[installer](/extensions/windows#an-installer-of-your-own) is not shown again.
If your extension is switched on, it is told with
[`Updated`](/extensions/sdk#on_lifecycleevent), carrying the version it
replaced.

Keep capabilities as narrow in an update as in the first release: a new
capability is a new line on the review sheet.
