Addons move through a defined lifecycle: Available → Installing → Active, with paths through Updating, Inactive, Uninstalling, and Removed. State is tracked in two places — the addons database table (metadata, versions, status) and storage/addons_statuses.json (enabled/disabled flags for laravel-modules).
During installation the system runs pre-install checks (dependencies, license), then your install_addon() lifecycle function from the addon's root functions.php. It takes no parameters and must return an array with a success key — return false with a message to abort. After success the system automatically creates the addons record, enables the module, runs migrations, executes versioned SQL files from Settings/install/, publishes config and translations, creates the public asset symlink (public/Addons/{Name} → addon public/), and clears caches. Updates call your update_addon() function, which must return success plus an update_url; the system downloads and extracts the ZIP, runs new migrations, applies install SQL files newer than the current version, and updates the version record. Activation/deactivation only toggles the module's enabled state — the symlink is created at install time, not activation.
There is no uninstall_addon() function to implement: on uninstall the system rolls back migrations, removes the symlink, and disables the module automatically; put extra database cleanup in Settings/uninstall/uninstall.sql. Version your install SQL files (v1.0.sql, v1.1.sql), test migration rollbacks, and make sure uninstall removes everything your addon created.