Skip to content

Publishing

When your extension works locally, publish it to the Aero marketplace. The web Publish page and the aero-ext CLI run exactly the same five-step, RLS-enforced flow against Supabase — pick whichever you prefer.

Before you publish

  • Your aero.json must pass the schema.
  • version must be a new, unique (extension_id, version) — published versions are immutable. To ship a fix, bump the version.
  • The packaged .aero-ext must be ≤ 50 MB.
  • You need a publisher account (Google sign-in) so the platform has your auth.uid().

The five-step flow

Both the web page and the CLI perform these steps, enforced by Supabase Row-Level Security (no server needed in v1):

  1. Authenticate (Google) → you have auth.uid().
  2. Upload the .aero-ext to the extension-packages Storage bucket at <uid>/<id>/<version>.aero-ext.
  3. Upsert the extensions row (publisher_id = auth.uid()).
  4. Insert the extension_versions row (package_path, parsed manifest, sha256, size_bytes).
  5. Update extensions.latest_version = version.

RLS restricts writes to your own <uid>/… Storage folder and rows owned by your auth.uid(), so you can only publish under your own publisher identity.

Publish with the CLI

bash
# From your extension folder:
npx aero-ext login        # opens Google sign-in, stores your session
npx aero-ext package      # validates aero.json, then zips → .aero-ext
npx aero-ext publish      # runs the five-step flow above

publish packages first if needed, uploads to Storage, upserts the catalog row, inserts the immutable version row, and bumps latest_version. On success it prints the canonical id and version, and the extension appears in the marketplace.

Publish from the web

  1. Go to the marketplace Publish page (aeroide.in/publish).
  2. Sign in with Google.
  3. Drag in your .aero-ext. The page parses aero.json, computes sha256 and size_bytes, and shows a preview.
  4. Confirm — the page runs the same upload → upsert → insert → update flow.

What lands in the registry

TableRow written
public.extensionsOne row per extension (latest metadata): id, publisher_id, publisher_handle, name, display_name, tagline, description, category, tags, icon, latest_version, downloads, rating, verified, featured, timestamps.
public.extension_versionsImmutable version: extension_id, version, manifest (jsonb), package_path, size_bytes, sha256, changelog, published_at. Unique (extension_id, version).
public.extension_installsOne per (extension_id, user_id) when a user installs.

Downloads = unique installers

You don't (and can't) set the download counter. An AFTER INSERT trigger on extension_installs bumps extensions.downloads — so the count reflects unique installers and clients can't forge it.

Categories

category in your manifest must be one of these (they match the marketplace):

Themes · Languages · Linters · Formatters · Tools · AI · Snippets

Storage & download URLs

The .aero-ext lives in the public extension-packages bucket at <uid>/<id>/<version>.aero-ext. Anyone can download via the public URL — no auth needed:

https://wfyvsawnyjewksxwvaco.supabase.co/storage/v1/object/public/extension-packages/<uid>/<id>/<version>.aero-ext

When a user installs from the marketplace, Aero downloads this URL, optionally verifies the sha256 against the version row, unzips into ~/.aero/extensions/<id>/<version>/, validates the manifest, and records an install row (which counts the download).

Shipping an update

  1. Bump version in aero.json (e.g. 1.0.01.1.0).
  2. npx aero-ext package && npx aero-ext publish (or re-upload on the web).

The new version is inserted as a fresh immutable row and latest_version is updated. Existing installs keep their pinned version until the user updates.

Lean, AI-ready, under your control.