Skip to content
Bondry

Packaging and release format

The signed update package, what the installer refuses, and how a release is built.

Two artefacts exist per release: the update package that the updater applies, and the full package that a buyer installs from scratch. This page is about the first, because it is the one with a format.

The package#

A zip with exactly three things:

Code
bondry-package.json      the manifest
bondry-package.sig       the signature
files/<relative path>    the files, relative to the install root

The manifest#

JSON
{
  "product": "bondry-core",
  "version": "1.2.0",
  "min_from": "1.0.0",
  "php": ">=8.2",
  "files": { "app/Kernel/Foo.php": "<sha256>", "...": "..." },
  "delete": ["app/Kernel/Removed.php"]
}
Field Meaning
product The product slug: the core or your module
version The version this package installs
min_from The lowest version this package can be applied on top of
php The PHP constraint
files Every file in the package with its sha256
delete Files removed by this version

The signature#

bondry-package.sig is an RSA-SHA256 signature, base64 encoded, over the canonical form of the manifest: json_encode of {product, version, min_from, php, files (sorted by key), delete} with JSON_UNESCAPED_SLASHES.

The private key never leaves the release server, and signing only ever happens as part of publishing a release. A package is not something you can sign on a laptop, and that is the point.

What the installer refuses#

In this order, and any one of them rejects the whole package:

  1. the signature does not verify;
  2. the sha256 of any file does not match the manifest;
  3. any path is absolute, contains .., or falls outside the allowed roots.

Allowed roots: app/, bootstrap/, config/, database/, lang/, modules/, public/, resources/, routes/, vendor/, plus composer.*, artisan and the integrity manifest.

Never written by an update: .env, storage/, public/uploads/ and bootstrap/cache/. A buyer's configuration, uploads and data are not yours to overwrite.

Building a release#

Shell
php artisan release:build <product> <version> <folder>

It walks the tree, computes the hashes, diffs against the previous release of the same product to fill delete, writes the manifest and signs it.

Module and theme packages#

A module or theme installed from the panel is a plain zip of the directory, with the manifest at its root. It is not signed unless official is set, and official is only accepted for packages we sign.

The extraction is still hardened the same way: an isolated temporary directory, schema validation, no absolute paths, no .., no symlinks, and a cap on the uncompressed size so a zip bomb goes nowhere.

Designer exports#

A Designer document exports as .bondry-design.json, or .bondry-design.zip when media travels with it. Import validates the schema, remaps the media, warns about missing modules and about tokens with no match.

Nothing in an import executes code: it is plain JSON. The zip is capped at 500 entries and 64 MB uncompressed, entries with .. or absolute paths are refused, media must have an allowed extension and a matching sha256, and any divergence rejects the whole package.

Checklist before you ship#

  • Assets compiled and included; no build step for the buyer.
  • tested_up_to set to the core version you really tested.
  • Migrations only touch your own prefix.
  • onUninstall drops your tables and leaves the core alone.
  • Every string in your own language files, in English.
  • No inline script anywhere.
  • A clean install and an upgrade from the previous version both tested.