Vendor repositories as sources
Connect a vendor Composer repository to a project, store that project's vendor credentials, then check installs and access from a second project. Project-owned sources serve only their owner project.
Prerequisites
- A deployed broker with two active projects,
client-aandclient-b. The walkthrough uses an organization admin session for setup because registering names and enabling private sources requireorg:sources:write. - Separate project service principals with
project:readandproject:packages:read, and areadservice token for each. Do not use an organization-wide token for this isolation check. - A Composer project and a vendor package/version that the first project's vendor account may download. Composer 1 requests to the broker are refused.
- For the Magento Marketplace example, credentials for
repo.magento.comthat entitle the first project to the chosen package. In the examples,<vendor>/<package>,<version>and<broker-host>are placeholders. Vendor credential placeholders are<magento-public-key>and<magento-private-key>; broker service tokens usepb_svc_<selector>_<secret>. Vendor keys do not use a broker token prefix.
1. Add a project-owned source
Select project client-a in the UI, open Sources, then Add source. Set the following fields. Creating through the project view binds the source to that project; using the organization view would create an organization-owned source.
| Field | Value |
|---|---|
| Source name | marketplace-client-a |
| Ecosystem | composer |
| Source type | Composer repository (composer_repository) |
| Repository URL | https://repo.magento.com |
| Package names (one per line) | The purchased package, written as <vendor>/<package> after replacing both placeholders. |
| Package patterns (one per line) | Leave empty for this check. |
| Visible to new projects | Select it so discovered packages receive the visibility flag. Project ownership still limits them to client-a. |
| Additional archive hosts | Leave empty until a vendor-documented download hostname is required. Credentials are also sent to listed hosts. |
Save the source. It starts paused. Keep it paused while setting its credential and registering package names. Empty package and pattern selections select no packages.
The API equivalent is POST /_pb/projects/client-a/sources with the following JSON body. Project-scoped routes use the project slug (client-a), not its ID. The response is 201 with {"id":"<source-id>"}; use that ID as :source below. Creating a project source requires project:sources:write.
{
"name": "marketplace-client-a",
"ecosystem": "composer",
"type": "composer_repository",
"url": "https://repo.magento.com",
"config": {
"selection": { "packages": ["<vendor>/<package>"] },
"distHosts": []
},
"defaultVisibleToNewProjects": true
}
2. Set the project's vendor credentials
In the source's More menu, choose Set credential. Select http_basic, enter <magento-public-key> in username and <magento-private-key> in password, replacing the placeholders with the vendor-issued values, then choose Save credential. Reauthenticate if prompted and enter the credential again. The broker stores this credential with the source's project ownership and does not show it again.
The corresponding credential operation is PUT /_pb/projects/:project/sources/:source/credential. It requires project:sources:write and a freshly reauthenticated user session. Send kind and a JSON-encoded material string as follows, replacing the placeholders. A package-install service token cannot perform it.
{
"kind": "http_basic",
"material": "{\"username\":\"<magento-public-key>\",\"password\":\"<magento-private-key>\"}"
}
For another vendor, choose the credential kind required by that repository. The broker supports HTTP Basic, bearer, allowed header and query credentials for Composer repositories. Keep credential values out of repository URLs.
3. Register names, enable, attach and sync
Switch the top-bar scope to Organization, open Sources, and find marketplace-client-a (include project-owned sources in the list). Open More → Registered names and add the exact purchased package as the registered pattern. Registration prevents public proxies from supplying matching names; it does not grant another project access to this source. Registration and the enable route require a user session with org:sources:write; enabling this project-owned source also checks project:sources:write in its owner project. The organization admin role supplies both.
Still in Organization → Sources, choose More → Enable. Switch the top-bar scope back to project client-a, open Sources, and choose Attach if the source is not attached, then Sync now. Wait for a successful sync before installing. Enabling, attaching and syncing are separate actions; an accepted sync request is not a completed sync.
| Operation | HTTP route | Result |
|---|---|---|
| Register the package name | POST /_pb/sources/:source/names, body {"pattern":"<vendor>/<package>"} | 201 with the registration id. |
| Enable the source | POST /_pb/sources/:source/enable | 204. |
| Attach to its owner project | PUT /_pb/projects/:project/sources/:source/attachment, body {} | 204. |
| Request sync | POST /_pb/projects/:project/sources/:source/sync | 202 when accepted. |
Attachment and sync require project:sources:write in the owner project.
Keep client-b without this package for the isolation check below. If it later purchases the same package, create its own project source and credential. Exact registered patterns are unique within the organization and ecosystem: repeating <vendor>/<package> on its second source returns pattern_taken. Registrations belong to a source; the first source's registration cannot enable the second source. An organization operator must choose a distinct valid pattern covering the purchased package for the second source, reviewing the additional names that pattern reserves; keep its package selection restricted to its purchases. If no acceptable distinct pattern is available, this setup cannot enable the second source. Sharing the vendor URL does not require sharing credentials.
4. Install as the first project
In a disposable Composer project directory, configure the broker URL and the first project's token. Replace all angle-bracket placeholders and supply the first project’s service token through the BROKER_TOKEN environment variable. The bearer credential is a broker service token, separate from the vendor keys saved in step 2. The authentication command writes local Composer credentials; keep that file out of version control.
composer config repositories.client-a composer 'https://<broker-host>/client-a'
composer config --auth 'bearer.<broker-host>' "$BROKER_TOKEN"
composer require '<vendor>/<package>:<version>' --no-interaction --no-plugins --no-scripts --prefer-dist
Composer writes a repository entry with "name": "client-a"; repositories are canonical by default. In an existing project, move chosen locked packages to broker URLs with composer update <vendor>/<package> (name each package to re-resolve). composer update --lock leaves existing dist URLs unchanged. A full composer update re-resolves all dependencies and can change versions across the project.
Verify isolation
Use an HTTP client to request the following metadata and archive URLs with the indicated bearer token. Replace the package placeholders with the same package that installed above. Give the second service principal access to client-b so the requests to client-b check package isolation within an accessible project. Do not use a PAT belonging to an organization admin for these checks.
| Request | Credential | Expected result |
|---|---|---|
GET https://<broker-host>/client-a/p2/<vendor>/<package>.json | First project's pb_svc_ token | 200 with the purchased package's metadata after successful sync. |
GET https://<broker-host>/client-a/p2/<vendor>/<package>.json | Second project's pb_svc_ token | 404 when the token has no access to client-a. |
GET https://<broker-host>/client-b/p2/<vendor>/<package>.json | Second project's pb_svc_ token | 404 if client-b has no source supplying that package. The first project's cached metadata or archive does not make it visible. |
GET <client-a-dist-url> | Second project's pb_svc_ token | 404 when the token has no access to client-a, even with cached bytes. |
GET <same-dist-url-with-client-b-slug> | Second project's pb_svc_ token | 404 if client-b has no source supplying the package, even with cached bytes. |
Copy a version's dist.url from the first successful metadata response as <client-a-dist-url>. Fetch it with the first project's token to warm the archive cache (expect 200), then run both negative archive requests above. For the last request, replace only the /client-a/ project segment with /client-b/, keeping the package and archive filename.
A 401 means authentication failed and does not prove project isolation. A 403 can mean the principal sees the project but lacks the requested operation; check its permissions before interpreting the result. Do not remove the source ownership boundary to resolve a denial.
Source behavior and cache
Configure source type, package selection, archive retention and removal policy. Archive fetching and cache reuse follow the behavior below.
| Choice or behavior | Effect |
|---|---|
| Composer repository source | Sync reads selected package metadata from the vendor. An archive request first checks visible stored bytes; missing bytes for a current version can trigger a fetch and storage through the source. |
| Public proxy | Admits public names on demand, subject to private-name reservations; it is a separate source type. |
Removal policy flag | A package marked removed at the vendor can remain eligible. This does not guarantee that an archive never fetched can still be downloaded from the vendor. |
Removal policy exclude | A removed package becomes ineligible; possession of cached bytes does not bypass resolution. |
| Archive retention override | Can lengthen organization retention. It does not widen project visibility or promise permanent retention. |
Troubleshoot credentials
If sync or install fails, check the source's last error and whether it has a credential. The source response exposes hasCredential and error state, not credential material.
| Symptom | Check | Action |
|---|---|---|
| Vendor authentication or entitlement failure | Confirm the keys belong to the purchasing project and the selected package is entitled. | Replace the credential in that project's source, then sync again. |
| Reauthentication required when saving | Check whether the UI asks you to sign in again. | Reauthenticate and re-enter the credential; the UI does not replay the secret. |
| Metadata works, archive fails | Check whether the archive host differs from the repository host. | Add only a vendor-confirmed hostname to Additional archive hosts; the source credential is sent there too. |
| No packages after sync | Check the package selection, attachment, source state and Visible to new projects. | Select the exact package name, attach to the owner project, register names, enable and sync. With the empty attachment body above, discovered packages need Visible to new projects selected; an explicit project package selection can also grant visibility. |
Next
Use Access model to check roles and token scopes, Composer for client setup, or return to Troubleshoot credentials for vendor credential failures. A separate troubleshooting page is planned.