Skip to content

Module Registry

KoAkademy distributes standalone modules through the public module registry. The registry contains two related indexes:

  • registry.json is the signed Marketplace catalog.
  • packages.json is the Composer repository used during application builds.

The Marketplace is a catalog and module-status interface. It is not a live package installer. Composer and the application image remain the code delivery boundary.

If you are creating a new module from the beginning, follow the complete Create a Module guide. This page focuses on the registry, signing, installation, and update boundary.

module repository and vX.Y.Z tag
├─ registry updater ──▶ signed registry.json + packages.json
│ │
│ └─ GitHub Pages
└─ KoAkademy composer.json/lock ──▶ application image ──▶ Swarm
└─ Marketplace enable/disable

Updating the registry changes catalog information. It does not modify a running container, install vendor/ dependencies, rebuild Vite assets, run migrations, or restart Swarm.

The Marketplace fetches the signed catalog at runtime, using the configured cache TTL. Contributors can therefore publish new catalog entries without changing the KoAkademy core repository. A catalog entry describes an available release; it does not mean that the PHP package is installed in a particular image.

From the KoAkademy application repository, configure the Composer registry and require the package:

Terminal window
composer config repositories.koakademy composer https://yukazakiri.github.io/koakademy-modules
composer require koakademy/forms:^1.1

Use this command after the package has a catalog entry in the public registry. The KoAkademy core repository may temporarily carry a direct package repository definition while a newly released module is waiting for its signed registry entry; that fallback is not a substitute for publishing the module to the catalog.

Commit composer.json and composer.lock, then build the normal KoAkademy image. Run migrations in the release container when the package includes database migrations, clear/rebuild application caches, and roll the Swarm service. Only after the package is in the image should a super administrator open Administrators → Marketplace and enable it. The enable/disable decision is stored in module_installations, and the application shows a restart-required notice until the workers have been rolled. At container startup, modules:sync-statuses records installed package metadata without overwriting existing administrator choices or clearing restart markers. After every application replica has been updated and verified healthy, acknowledge the completed rollout explicitly with php artisan modules:sync-statuses --acknowledge-restart.

The current Forms package supports authenticated, guest-identifier, anonymous, and record-bound invitation forms, protected submissions, reusable templates, safe suggestions, and KoAkademy student-model mappings. Its built-in Student Profile Completion template renders only missing fields and applies answers with blank-only concurrency protection. Sparse fields use the host students.profile_details JSON column while existing normalized columns remain the first storage target.

When a newer catalog release is available, Marketplace shows an Update button. For Composer-installed modules it displays and can copy the exact composer require command for the release. For legacy source-tree modules it explains that the source must be updated or migrated in the KoAkademy repository. In both cases, updating still requires a new image build and a rollout; the button never changes PHP code inside a running container. See the Forms module repository for its package-specific behavior and configuration.

After installing or updating Forms:

  1. Run php artisan migrate --force so the template, invitation, field presentation, and students.profile_details migrations are applied.
  2. Confirm the package is installed in the image and the provider is visible under vendor/koakademy/forms.
  3. Build the host frontend so the module’s Inertia pages and generated route helpers are included.
  4. Open Administrators → Marketplace and enable the installed module if the persistent status is disabled.
  5. Open Online Forms → Templates, choose Student Profile Completion, publish the generated form, then use Invitations to preview eligible students and explicitly queue email delivery.

Deployment and Marketplace refreshes never send invitations. Links are hashed at rest, expire after 30 days by default, are revoked on resend, and become unusable after completion. The queue must be running for delivery.

  1. Keep the module source in its own repository.

  2. Update module.json and composer.json; keep the package name, provider, and repository identity stable.

  3. Create a matching public semver tag: manifest 1.2.3 must use tag v1.2.3.

  4. In a checkout of the registry repository, download the exact tag archive:

    Terminal window
    curl -L https://github.com/OWNER/REPOSITORY/archive/refs/tags/v1.2.3.zip \
    -o /tmp/module-v1.2.3.zip
    php scripts/update-module.php \
    --module=/path/to/module-repository \
    --archive=/tmp/module-v1.2.3.zip
    php scripts/validate-registry.php
  5. Open a pull request containing the generated registry.json and packages.json changes, the module release/tag, compatibility requirements, migrations, permissions, and rollback notes.

The updater reads the module manifest and Composer metadata, calculates the SHA-256 and Composer distribution checksums, updates both indexes, sorts the entries, and removes the old signature. Contributors must not generate or commit a signing key.

After reviewing the module and the generated diff, use the existing private registry key from protected storage:

Terminal window
php scripts/validate-registry.php
php scripts/sign-registry.php registry.json /secure/registry-private.key registry.json
php scripts/validate-registry.php
php scripts/verify-registry.php

Merge only after the signature verifies. Registry CI validates every change; the Pages workflow validates the signed files again before publishing them. Never generate a replacement key for an ordinary module update. Existing applications trust the current public key, so key rotation requires a separate migration of every MODULE_REGISTRY_PUBLIC_KEY secret.

The complete contributor checklist is in the registry repository’s CONTRIBUTING.md.

For a new Announcement release such as 1.0.2:

  1. Release v1.0.2 from the Announcement module repository.

  2. Add the release to the signed registry and wait for Pages publication.

  3. In KoAkademy, run:

    Terminal window
    composer update koakademy/announcement --with-dependencies
    php artisan test --compact
    git add composer.json composer.lock
    git commit -m "chore(modules): update announcement"
    git push

    If Announcement is not yet a Composer dependency, use composer require koakademy/announcement:^1.0.2 instead.

  4. Build and deploy the new stable or edge image through the normal release process.

  5. Run php artisan migrate --force when required, clear/rebuild caches, and roll every Swarm replica.

  6. Enable the installed version in Administrators → Marketplace if its persistent status is disabled, then roll the application workers when the Marketplace reports that a restart is required.

Existing deployments stay on the old module version until the application lockfile and image are updated. Marketplace refreshes do not perform these steps. The database state survives image replacement; the JSON status file is retained for first-boot defaults and compatibility with older releases.

The six original modules under the application’s Modules/ directory are source-tree modules and remain enabled by the image’s status file for backward compatibility. A standalone repository with the same module name does not automatically replace that local source.

Migrate a local module to its Composer package as a separate, tested change. Keep the existing module enabled during the migration unless the migration plan explicitly changes its status. The module_installations row preserves administrator choices across application upgrades.

The current installer and Swarm templates default to:

MODULE_MARKETPLACE_ENABLED=true
MODULE_REGISTRY_REQUIRE_SIGNATURE=true
MODULE_REGISTRY_URL=https://yukazakiri.github.io/koakademy-modules/registry.json
MODULE_SCAN_VENDOR=true
MODULE_ACTIVATOR=database

The registry public key must match the catalog trust root. If Marketplace shows a module but cannot enable it, confirm that the matching package is installed in the image, its provider is discoverable, its compatibility check passes, and its dependencies are enabled. If the signed catalog cannot be reached, installed modules remain visible but remote catalog information may be unavailable.

A core-only image should omit optional Composer packages. It can still display the signed Marketplace catalog, but catalog entries are shown as Not installed and cannot be enabled until the package is added to the application dependency manifest and a new image is built. An official full distribution can ship the same packages in its image while initializing optional modules as disabled for new installations.