← Back to the dashboard

Authoring marketplace packages and scripts

How to create, test, and publish content from the admin portal, and exactly what each field does when it reaches the sshwiz app. (Portal setup and deployment are covered in the repo's README.md.)

Lifecycle

Everything you create starts as a Draft β€” invisible to the app. Click Publish when it's ready; the app's Market tab lists only published items. Users copy an item into their local Shelf with "Add", where it behaves like any custom entry: it works offline and they can edit or delete their copy. That has one important consequence:

Publishing an update does not change copies users already added. The app marks an already-added item "βœ“ Added" and won't re-add it. To pick up your update, a user deletes their local copy and adds the item again. So treat published content as append-mostly: get it right before publishing, and prefer a new item (or a clear description bump) for breaking changes.

Creating a package

A package is an install recipe: sshwiz runs check, then install, then verify over SSH on the target server.

Open πŸ“¦ Packages β†’ οΌ‹ New package.

FieldWhat it does
NameShown on the package tile in the app.
IconOne emoji, shown on the tile.
CategoryGrouping label shown under the name (e.g. Databases, Web).
Document IDThe Firestore doc ID, auto-slugged from the name. Fixed after creation β€” it also becomes the ID of users' local copies, so changing it later would create a "different" item.
DescriptionOne sentence under the tile. Say what's installed and from where (distro repo? vendor repo?).

Then pick the Distro family and fill in one set of commands, like the desktop app's editor:

Each box takes one command per line, run in order:

Worked example β€” Redis (Ubuntu)

Save, then Publish. To cover another distribution, either edit the same package and switch the family selector (one item, per-family recipes), or create a separate item per distro (e.g. doc IDs redis-ubuntu, redis-alpine) β€” the AI agent suggests suffixed IDs like that automatically when a family is selected.

Creating a script

A script is a reusable, parameterized shell task: the user fills in your inputs, reviews the composed script, and runs it over SSH.

Open πŸ“œ Scripts β†’ οΌ‹ New script.

Top fields (Name, Icon, Description, Document ID) work as for packages.

Inputs

Each input renders as a form field in the app and is injected into the script as a shell variable, safely single-quoted. Per input:

FieldWhat it does
Shell variableUPPER_SNAKE_CASE name; reference it in the body as $VAR. Anything else is normalized (db name β†’ DB_NAME).
LabelThe form field's caption in the app.
PlaceholderExample text shown in the empty field.
DefaultPre-filled value.
ChoicesOptional; one value|Label per line turns the input into a dropdown (e.g. yes|Yes β€” NOPASSWD:ALL). Leave empty for free text.
RequiredThe app blocks Run until it's filled.
SecretMasked input, and redacted as β€’β€’β€’β€’β€’β€’β€’β€’ in the script preview. The app also auto-treats names like PASSWORD/TOKEN as secret, but set the flag explicitly β€” don't rely on the heuristic.

Body

The app prepends set -e and your input assignments, then runs the whole body as one bash script (via sudo bash when it contains sudo, with the password primed over stdin β€” never prompt for it yourself).

Rules of thumb, learned the hard way:

Worked example β€” Create sudo user

Inputs:

  1. NEW_USER, label Username, placeholder deploy, required.
  2. PUBKEY, label SSH public key, placeholder ssh-ed25519 AAAA….
  3. NOPASSWD, label Passwordless sudo, default no, choices no|No β€” require password and yes|Yes β€” NOPASSWD:ALL.

Body:

if id "$NEW_USER" >/dev/null 2>&1; then
    echo "User $NEW_USER already exists." >&2
    exit 1
fi

sudo useradd -m -s /bin/bash "$NEW_USER"
sudo usermod -aG sudo "$NEW_USER"

if [ -n "$PUBKEY" ]; then
    sudo mkdir -p "/home/$NEW_USER/.ssh"
    echo "$PUBKEY" | sudo tee "/home/$NEW_USER/.ssh/authorized_keys" > /dev/null
    sudo chmod 700 "/home/$NEW_USER/.ssh"
    sudo chmod 600 "/home/$NEW_USER/.ssh/authorized_keys"
    sudo chown -R "$NEW_USER:$NEW_USER" "/home/$NEW_USER/.ssh"
fi

if [ "$NOPASSWD" = "yes" ]; then
    echo "$NEW_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee "/etc/sudoers.d/90-$NEW_USER" > /dev/null
    sudo chmod 440 "/etc/sudoers.d/90-$NEW_USER"
fi

echo "Created sudo user $NEW_USER"

Testing before (and after) publishing

  1. Publish to a staging Firebase project first if you run one; otherwise publish, verify quickly, and unpublish if something's off.
  2. In sshwiz, open Shelf β†’ πŸ›’ Market β†’ Refresh, add your item, and read the composed preview ("Show script preview") β€” this is exactly what users review.
  3. Run it against a disposable server (a fresh VM or container) for each distro family you claim to support.
  4. Remember the update caveat at the top: your own added copy also won't refresh β€” delete it from the Shelf and re-add after each published change.

Editing, unpublishing, deleting