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:
bondry-package.json the manifest
bondry-package.sig the signature
files/<relative path> the files, relative to the install root
The manifest#
{
"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:
- the signature does not verify;
- the sha256 of any file does not match the manifest;
- 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#
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_toset to the core version you really tested.- Migrations only touch your own prefix.
onUninstalldrops 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.