Repository navigation
feat: Registry Service (v2) client, instance registration and installs - #44
Merged
Merged
Conversation
…g test, extension.json schema - RegistryClient (Http) with cache and circuit breaker; CentralCatalog maps the cloud public extensions endpoint into the RegistryExtension resource shape when registry.self_hosted is false; install/purchase return a clear error for remote records - POST registry-extensions/config/test and a Test connection button; token masked with ClickToReveal in the admin config component - extension.json JSON Schema (server/resources/schemas) validated in bundleUpload via opis/json-schema; ExtensionManifest support class - Explore empty state component with registry-config link; category page titled by category - Test tooling: ember-ui 0.4.6 / ember-core 0.3.26 pins, eager engine loading under ember test, dummy app models and intl config, host stubs in tests/helpers Part of the Fleetbase Extensions Registry stabilisation (plan: https://claude.ai/artifact/6i2o4Qvq4HhsJje9YyqjYB).
Phase 1 Workstream D of the extensions registry re-architecture (plan: https://claude.ai/artifact/6i2o4Qvq4HhsJje9YyqjYB). - RegistryClient v2 mode (default when registry.service_url is set): catalog from /v1/catalog/*, updates from /v1/instances/me/updates, install reports to POST/DELETE /v1/instances/me/installs; the cloud ~registry/v1/extensions path stays the fallback without a service URL. 4xx no longer trips the circuit. - Instance registration: service URL and encrypted instance token in settings; POST registry-extensions/instance/register and DELETE .../instance; admin config page gets service URL, register with code, masked token, unregister and a token check in Test connection. - Installer: lookup resolves from the service catalog; Composer auth via COMPOSER_AUTH, npm via NPM_CONFIG_//host/npm/:_authToken plus a placeholder-only npmrc (pnpm 11 ignores //host keys in env); engines added by tarball URL; installs and uninstalls reported to the instance API; local registry_service_installs rows keep engines loading. - Explore/Installed/Purchased map service listings into the registry-extension shape (is_installed, is_purchased, update_available, current_bundle_version, install_blocked_reason). - Pest: 55 tests on responses recorded from a local service.
Conflicts: the categories endpoint checks the central catalog before main's live-connection query; the lockfile is the one matching the merged package.json (main's only package.json change is the version).
… mode Follows main's sandbox fix: RegistryServiceInstall is pinned to the mysql connection like the other registry models; installed() and purchased() can be called without a request again; the sandbox harness creates registry_service_installs, counts seven models and runs as the cloud registry so categories take the local-tables path.
CodeQL flagged the array .includes() assertions as incomplete URL substring sanitization (js/incomplete-url-substring-sanitization). Use an explicit equality check so the PR's CodeQL gate passes.
This was referenced Oct 23, 2026
D21: the registration (service URL, instance token, instance) was merged into config only when a worker booted, so under Octane `php artisan registry:register` (flb instance register) and the admin endpoint left other web workers on the old registration until octane:reload. RegistrySettings now reads the registry settings with one query on first use through RegistryConfig::current(), once per Octane request (reset on RequestReceived) and per queued job (reset on JobProcessing). Keys with no stored value fall back to the config file and environment, so a forgotten registration clears everywhere too. D17: re-registering created a second instance on the service. The register payload now carries the current instance token as previous_token, sent only to the service that issued it, so the service rotates the same instance (registry release/v2.0.0 d9c3661).
…record rotated registrations D21 follow-up: RegistrySettings read the settings table once per Octane request and queued job. The stored values now sit in the application cache for REGISTRY_SETTINGS_CACHE_TTL seconds (30 by default, 0 to turn it off), so a request costs a cache read. Writing or deleting a registry setting forgets the cache: a Setting saved/deleted listener, plus InstanceRegistration::store() and forget() (whose query-builder delete fires no model events). With a shared store, a `registry:register` run in another process applies to every worker on the next request; with a per-container store, within the TTL. An unreadable settings table is never cached. D17 follow-up: the service now answers `rotated` (200) when it re-issues the token of an instance it already had (by previous token or by host, registry 68331fe). RegistryClient::registerInstance() returns it, the stored instance summary keeps it, and the admin config payload exposes instance.rotated, so the admin page and registry:register can say the same instance kept its installs and seats. README documents re-registration and the settings cache.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1, Workstream D of the extensions registry re-architecture: the instance module reads the new Registry Service (
fleetbase/registrybranchv2, fleetbase/registry#4).What changes
RegistryClientv2 mode. Used by default whenregistry.service_urlis set;registry.api_versioncan pinv1. Catalog fromGET /v1/catalog/extensions,extensions/{slug}andcategories; updates fromGET /v1/instances/me/updates; install reports toPOST/DELETE /v1/instances/me/installs. The cloud~registry/v1/extensionspath stays the fallback without a service URL. 4xx responses no longer count towards the circuit breaker.POST registry-extensions/instance/register(registration code) callsPOST /v1/instances/registerwith name, host, kind, console and API versions;DELETE registry-extensions/instanceforgets the registration. The service URL and the instance token are stored in core-api settings (registry-bridge.registry.*), the token encrypted with the app key and only returned masked.registry-admin-config). Service URL, register with a code, registered status with the masked token, unregister, and Test connection also checks the instance token.lookupresolves package names from the service catalog. Composer authenticates withCOMPOSER_AUTH; npm withNPM_CONFIG_//<host>/npm/:_authTokenplusnpm_config_userconfigpointing atserver/resources/npm/registry.npmrc, which holds only${…}placeholders (pnpm 11 ignores//hostkeys in the environment). Noauth.jsonor.npmrcwith a token is written. Engines are added by tarball URL so their npm scope is never remapped. Localregistry_service_installsrows keep engines loading.registry-extensionshape:is_installed,is_purchased(active paid licence),current_bundle_version(latest release),update_available, andmeta.install_blocked_reason, shown in the details modal. Cards show an update badge.The synchronous installer in this PR is replaced by the queued installer in the stacked PR.
Testing
server/tests/Fixtures/registry-service, with a re-record script); tokens in fixtures are placeholders.Decisions needed
composer.json(no secret in it).self_hostedlistings install through the installer, not as manual installs.DELETE /v1/instances/me).app.urlas the host and the core-api version as the API version.