Skip to content
Bart Reardon edited this page Mar 6, 2026 · 2 revisions

Outset 4.3.0 introduces Ed25519 asymmetric signing as the preferred method for verifying script integrity before execution. It replaces the SHA256 checksum feature, which is deprecated as of 4.3.0.

How it works

An admin generates an Ed25519 keypair. The private key stays on the admin workstation and is used to sign scripts. The public key is deployed to managed devices via MDM as the manifest_signing_key preference.

Each script carries its own signature embedded as a comment:

# ed25519: <base64-encoded-signature>

When Outset runs and manifest_signing_key is present, it verifies this comment against the script content before executing. Scripts without a valid signature are refused.

Why asymmetric signing instead of SHA256

The SHA256 checksum approach requires maintenance of an MDM profile whenever a script is modified. This can present friction in using the feature effectively.

With Ed25519:

  • The private key is never on the device — it cannot be stolen or misused from a compromised machine.
  • The public key on device can only verify, never forge. An attempt to modifies a script cannot produce a valid signature without the private key.
  • Each script is self-contained: the signature travels with the file and can be tracked in version control.

Setup

1. Generate a keypair

/usr/local/outset/outset --generate-keypair

Output:

Ed25519 signing keypair generated.

Private key (keep secret — used to sign scripts):
<base64-private-key>

Public key (deploy via MDM as 'manifest_signing_key'):
<base64-public-key>

Store the private key securely (password manager, secrets vault, etc.). It is never needed on a managed device.

2. Deploy the public key via MDM

Add the following to your Outset configuration profile:

<key>manifest_signing_key</key>
<string>BASE64_PUBLIC_KEY</string>

Security note: manifest_signing_key is only honoured when it is MDM-managed (i.e. delivered in a managed profile). If the key is present in /Library/Preferences/ but not managed, Outset ignores it and logs an error. This prevents a local admin from writing a bogus key to silently block script processing. In --debug mode a locally set value is accepted to allow testing without an MDM enrolment.

3. Sign your scripts

/usr/local/outset/outset --sign-script-file /path/to/script.sh --signing-key <base64-private-key>

Multiple files can be signed in one invocation:

/usr/local/outset/outset \
  --sign-script-file /path/to/a.sh \
  --sign-script-file /path/to/b.sh \
  --signing-key <base64-private-key>

The command embeds a # ed25519: <sig> comment in each file, inserting it after the shebang line if one is present.

Signing MDM payload scripts

For scripts delivered as MDM payloads, sign the script file before base64-encoding it for the payload:

/usr/local/outset/outset --sign-script-file my-script.sh --signing-key <base64-private-key>
base64 -i my-script.sh

The embedded signature comment is preserved in the base64-encoded content and verified when Outset decodes and runs the script.

4. Deploy scripts

Once signed, deploy scripts to the appropriate /usr/local/outset/ directory as normal. The signature travels with the file.

Verification

At runtime

Outset verifies each script automatically before execution whenever manifest_signing_key is configured. No extra steps are required.

Manual verification

To verify a script without executing it:

/usr/local/outset/outset --verify-script /path/to/script.sh --public-key <base64-public-key>

Multiple files:

/usr/local/outset/outset \
  --verify-script /path/to/a.sh \
  --verify-script /path/to/b.sh \
  --public-key <base64-public-key>

Output per file is <path>: OK or <path>: INVALID. The process exits non-zero if any file fails, making it suitable for use in CI pipelines or pre-deployment checks.

Supported script types

The signature is embedded as a #-prefixed comment, so signing works with any scripting language that treats # as a line comment: shell (sh/bash/zsh), Python, Perl, Ruby, and similar. These cover the overwhelming majority of Outset scripts in practice.

Swift scripts are not supported. Swift source files use // for comments; a bare # line (outside of a shebang) is a compile error. Signing a .swift script will cause it to fail at execution. Admins who need tamper protection for Swift scripts should use the SHA256 checksum mechanism or wrap the Swift invocation in a shell script that can carry the signature.

How the signature is computed

The signed payload is the script content with any existing # ed25519: line stripped out. This means:

  • Re-signing a script (e.g. after rotating keys) produces a new valid signature without accumulating multiple comment lines.
  • You can inspect, diff, and version-control a script with or without its signature comment — the canonical content is the same either way.

Key rotation

To rotate the signing key:

  1. Generate a new keypair with --generate-keypair.
  2. Re-sign all scripts with the new private key using --sign-script-file.
  3. Update the manifest_signing_key preference in your MDM profile with the new public key.
  4. Deploy updated scripts and updated profile together.

Outset will reject scripts signed with the old key once the new public key is deployed, so ensure all scripts are re-signed before pushing the profile update.

Migration from SHA256 checksums

Deprecation notice: The sha256sum checksum feature is deprecated as of Outset 4.3.0. It will receive no further maintenance and will be removed in a future release. New deployments should use Ed25519 signing instead.

If you are currently using sha256sum verification, the migration path is:

  1. Generate a keypair and deploy the public key via MDM as described above.
  2. Sign all scripts currently covered by sha256sum entries using --sign-script-file.
  3. Remove the sha256sum key from your MDM configuration profile.

Both mechanisms can coexist during a transition period: Outset checks checksums (if sha256sum is present) and signatures (if manifest_signing_key is present) independently. A script must pass whichever checks are active.

Clone this wiki locally