The future of installation scripts

An introduction to ZSM, a modern, security-first replacement for bash-based installation scripts.

ZZaidon July 6, 2026

Running curl -fsSL <https://example.com/install.sh> | bash is a common but dangerous pattern. It grants arbitrary, unvetted execution privileges to a remote script, often requiring sudo.

You cannot trust a foreign script running on your local machine without reading it first, but for most people that is not the case they just trust whatever the script contains and run it.

The ZSM Solution

ZSM introduces a Capability-based Security model for installation scripts. Instead of running imperative shell code, you run a .zsm script that declares the permissions it needs. ZSM acts as a complete lifecycle manager.

Please note that this is mainly a proof-of-concept and may not be ideal for most applications, want to improve it? Contribute Here.

Key Features

  • Declarative Permissions: Scripts must explicitly request access to the network, filesystem, or system configuration.

  • Deep Supervision: Lock external binaries to specific argument patterns (e.g. require bin: curl --silent).

  • User Approval & Persistence: Users review and approve specific capabilities before any code runs. Approvals are securely cached based on the script's URL.

  • Full Lifecycle Management: ZSM tracks created files and supports zsm uninstall for clean removal, including custom uninstall hooks.

  • Small & Portable: Built with Zig 0.16.0 for zero-dependency, high-performance execution.

Getting Started

You can install ZSM via Zoi:

zoi install @zillowe/zsm

Usage

ZSM can fetch and run scripts natively or via pipes:

# Native execution (Recommended)
zsm install https://example.com/install.zsm

# Piped execution
curl -fsSL https://example.com/install.zsm | zsm

Script Example

ZSM scripts use a declarative format to specify metadata and requirements, followed by safe built-in logic:

#!/usr/bin/env zsm
# :: name: my-app
# :: version: 1.0.0
# :: require bin: gpg, tar
# :: perm net: example.com
# :: perm fs: ~/.local/bin

LATEST_TAG = web.get_json_field("https://example.com/api/latest", "tag_name")
web.download("https://example.com/${LATEST_TAG}.tar.gz", "/tmp/app.tar.gz")

fs.move("/tmp/app", "~/.local/bin/app")

Please check out the docs for more information.

A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.