Submitting

Get your droplet into Droppy's Store.

The Store is a repository: gitlab.com/droppyformac1/droplets, one folder per droplet. You open a merge request that adds your folder, a maintainer reads the code and merges it, and the pipeline builds it, signs it with Droppy's Developer ID and puts it on the shelf. Every Mac running Droppy can install it within minutes of the merge, with no app release in between. An update is the same thing again with a higher version.

Why open source

A droplet is native code in Droppy's process, with Droppy's permissions. There is no sandbox between the two — which is what lets a droplet draw real SwiftUI on the notch instead of shipping a JSON description of a layout.

The price of that is review, and review means readable code. The merge request is the review: what merges is what ships, from the commit that was read.

Your licence is yours. The SDK's licence covers the SDK; it claims nothing over what you build with it. Name yours in droplet.json under source.license.

Before you submit

droppykit validate

This is the same check the pipeline runs. It verifies:

  • droplet.json decodes and satisfies validate(submission:)
  • the id is lowercase, valid, and matches id and the Info.plist
  • every declared surface has a matching conformance
  • every capability is one DroppyKit knows
  • icon names an Icon Composer document, and the scaffold's placeholder, when it is there, is well formed (the Store icon is Jordy's; see below)
  • the creator avatar is a square PNG of at least 256×256
  • the built bundle links the resilient framework, and carries no direct-access imports — see Add the package

The repository adds a few rules of its own, all in its CONTRIBUTING.md: the folder is named after the id, source.repository is your folder's page in that repository, creator.gitlab is your GitLab username (it generates CODEOWNERS, which is what gets you asked before someone else changes your droplet), CHANGELOG.md starts with an entry for the version being submitted, and Package.swift depends on DroppyKit alone. droppykit submit fills in the first two for you.

Replace everything the scaffold filled in, except the icon. droppykit new leaves placeholders so the package builds and validates from the first minute, and a submission that still carries one is refused by the Store's checks or sent back in review:

  • Assets/Creator.png: your own mark, not the blue disc;
  • summary, description, category and creator in droplet.json, and id while it can still change (it is permanent from the first publish);
  • CHANGELOG.md: a first entry that says what the droplet does, never only "First release.";
  • README.md: what the droplet does and how to use it, for the person deciding whether to install it.

The icon placeholder stays as droppykit new wrote it: Jordy makes the icon (next section).

Send only what the Store takes. The folder holds what the repository's CONTRIBUTING.md lists: droplet.json, Package.swift, Sources/, Assets/, CHANGELOG.md and README.md, and the icon document once Jordy has added it. No Tests/ folder or test target, no scripts or install helpers, and no build-tool plugins, macros, binary targets, executables besides the harness, or unsafeFlags in Package.swift. droppykit submit leaves the icon, your .gitignore, Package.resolved, build products, shots and agent files behind on its own.

What it does not ask for is screenshots, though it welcomes them: name up to eight PNGs in Assets/ under screenshots and the Store shows them on your droplet's page before it is installed.

Jordy makes the icon

Do not make your droplet's icon. Jordy designs every Store droplet's icon himself, in Icon Composer and in Droppy's current icon style, so each one adapts properly to light, dark, clear and tinted icons and matches the rest of the Store. Your merge request carries no icon: droppykit submit leaves the scaffold's placeholder out, and the pipeline refuses an icon in anyone's commit but Jordy's. If you have an idea for it, describe it in the merge request's Icon concept section: a sentence, a reference sketch, or an SF Symbol to start from. Jordy adds the icon before the droplet publishes. See The icon.

Submitting

brew install glab && glab auth login    # once
droppykit submit

It forks the Store repository (once), copies your package into droplets/<id>/ (everything but the icon), generates CODEOWNERS, commits on a branch droplet/<id>-<version>, pushes to your fork and opens the merge request with the checklist filled in and an empty Icon concept section. Read it over, tick what applies, add an icon idea if you have one, and it is submitted. droppykit submit --print shows what it would do without doing it.

You can do the same by hand: fork the repository, add your folder under droplets/ without its .icon document, run scripts/codeowners.py, and open a merge request against main. The pipeline validates the folder the moment the merge request opens.

Review

A maintainer reads the code and runs the droplet. What sends a merge request back, most often first:

  1. Capabilities you do not use. The user sees the list before installing. Ask for what you use and nothing else.
  2. Design that ignores the surface. Light backgrounds on dark chrome, Title Case labels, borders, invented spacing. See Design guidelines.
  3. Work that does not stop. A timer or observer that survives deactivate(). Swift cannot unload code, so it runs until Droppy relaunches.
  4. A live activity that never yields. Holding the compact seat when there is nothing to show.
  5. A droplet that is not a good citizen. Work while nothing is visible, blocking calls on the main actor, defaults that take something over, or secrets in preferences. See Good citizen.

None of these are rejections. They come back as a comment on the merge request, and a new commit on the same branch usually settles it.

Publishing

Once merged, the pipeline builds the folder universal against the framework Droppy actually ships and signs it with Droppy's Developer ID. It is not notarized: Droppy downloads it itself and strips the quarantine flag before loading, so there is no Gatekeeper pass for a notarization to satisfy.

The Store sorts droplets by maker, All droplets / By Droppy / Community, and every row carries its creator's mark, so a published droplet is listed beside your name and your avatar. The row's switch is the install: Droppy downloads the signed bundle from the catalogue, checks its hash and signature, copies it into

~/Library/Application Support/Droppy/Droplets/<your-id>/<YourDroplet>.droplet

and loads it, running the same admission checks described in Host support. Before the first droplet from another maker is installed, Droppy tells the user once that such droplets can be unreliable. The droppy://extension/<your-id> deep link on getdroppy.app opens your Store page inside Droppy, the Source code row on that page opens your folder in the repository, and a newer published version is offered as an update when the catalogue refreshes, which it does every six hours and every time the Store opens.

Versions

version is major.minor.patch, numeric only. A pre-release tag cannot be published: the fleet's catalogue decode is strict, and one undecodable entry refuses the whole catalogue.

Raise it on every merge request. The pipeline publishes only folders whose version is newer than the shelf's, and the Store compares versions to decide who needs an update. The text under the newest CHANGELOG.md heading is what the Update button shows.

Your responsibility

Opening a merge request means accepting the Droplet developer terms (version 2026-09-17) at getdroppy.app/droplet-developer-terms; the merge request template says so and asks you to confirm it. You stay responsible for your droplet: its bugs, and how it handles user data. Droppy reviews the merged commit and signs the build, so someone has read the code before it ships. That review and signature do not mean Droppy vouches for the droplet or maintains it; it remains yours. Droppy shows users a one-time notice that droplets from other makers can be unreliable before their first Store community install or first local install.

See also