Extension sources
Automatically sync extensions from your own Git repositories and OCI registries into your Scrydon installation.
Extension sources let your developers push extensions to infrastructure they already control — a Git repository or an OCI registry — and have your Scrydon installation pick them up automatically on a schedule. No inbound traffic, no manual uploads for every release.
When to use extension sources vs. manual upload
Use extension sources when you have developers who push extension updates from CI and want those updates to land in Scrydon without an administrator running a manual upload for each release. Use manual upload for one-off testing, air-gapped environments, or extensions with no automated publish step.
How It Works
Your Scrydon installation runs a reconciler that polls each registered source on a configurable interval (default 5 minutes, minimum 30 seconds). On each tick it:
- Fetches the
scrydon.yamlcatalog file from your source. - Compares it against the catalog versions already synced.
- For each new or changed entry: fetches the artifact, verifies its signature, and publishes it to the organization’s catalog.
- Records the catalog result and ordinary entry counts in the source's run history. A manual sync response and the reconciler log also carry the bounded per-environment ontology summary described below.
When an organization admin clicks Sync in Settings → Platform → Extensions → Sources, Scrydon also installs the successfully synced extensions’ org-wide content (code, ontologies, and MCP servers) using the same authorization and installation pipeline as Add Extension. They then appear under Installed without a separate Add step. Unchanged catalog entries are included, so Sync can finish installations that were missing or previously failed. Installation errors are shown separately from catalog counts; one failure does not prevent other extensions from installing.
Credentials and workspace activation remain separate setup steps. Workflows, process flows, data sources, and notebooks retain their workspace-specific setup. Scheduled background sync refreshes the catalog and follows the existing additive update policy; it does not receive the interactive admin’s installation authorization. Run Sync to install newly discovered org-wide content.
The reconciler is pull-only. Your CI pipeline never needs inbound access to your Scrydon cluster.
Ontology reconciliation across environments
Ontology state belongs to a workspace environment, so a source sync never sends one organization-only ontology request. The reconciler enumerates the organization's real workspace environments in a stable order and applies each eligible ontology extension once per environment, with at most eight applies in flight. Static reference data sources remain organization-scoped and are applied once per extension, not once per environment.
For extensions already installed in an environment, automatic ontology updates
are additive. An environment where the extension is not installed is counted as
skipped with reason not_installed; the sync does not install it implicitly.
The reconciler revisits all targets even when the source entry itself is
unchanged, so retrying a partially successful run can converge without
republishing the artifact.
One environment's failure does not roll back or prevent attempts for the other
environments. The underlying Sync now API response reports targetAttempts,
applied, skipped, and failed target counts, plus a digest covering every failure
and at most the first 16 bounded target/error summaries. The Settings toast
currently shows only entry counters; failed target tuples and aggregate counts
are also written to the current run's structured log. The stored run-history row keeps
the normal status, error, and entry counts; it does not persist the full target
summary or manifest body.
If the organization has no workspace environments, ontology auto-apply reports
no_workspace_environments and makes no ontology call. Catalog reconciliation
and independent content kinds can still complete, so always inspect the
ontology counters in the Sync now result instead of treating an otherwise
successful catalog run as proof that every ontology target applied.
Every successful target records the real catalog row ID, source ID, immutable source entry reference, and the complete normalized manifest snapshot. These fields let the Settings → Platform → Extensions page detect missing or divergent environments without guessing from timestamps or environment-local revision IDs.
The source catalog wins
The scrydon.yaml catalog file is the source of truth for which extensions appear in your organization's catalog. It wins in both directions:
- Listed but retired → restored. If an administrator retires a source-managed extension from the catalog UI, the next sync brings it back as long as it is still listed — the same way re-uploading a retired extension manually does.
- Unlisted → retired. When you remove an entry from
scrydon.yaml, the next successful sync retires that extension's catalog rows. It disappears from the catalog and can no longer be installed. Re-add the entry and the following sync restores it.
To remove a source-managed extension permanently:
- remove its entry from the source's
scrydon.yaml, or - disable (or delete) the source itself.
Three things the sweep deliberately does not do:
- It never touches older versions of an extension that is still listed. Your catalog keeps version history; only extensions the file no longer names at all are retired.
- It never runs on an empty catalog. A
scrydon.yamlthat parses to zero extensions is left alone, so a truncated or mis-edited file cannot clear your catalog on an unattended sync. To empty a source deliberately, disable it or retire its extensions by hand. - It never touches manually uploaded extensions or other sources' extensions.
Retiring an extension is a catalog-visibility change, not an uninstall: content already installed into workspaces keeps working. Each sync reports how many extensions it retired alongside how many it installed, in the source's run history.
Retiring by hand is still useful for manually uploaded extensions (no source manages them, so they stay retired) and as a temporary hide for source-managed extensions between syncs.
Choosing a Source Type
| Source type | Best for |
|---|---|
| Git | Teams that version extension contents directly in a Git repository and don't want to run a container registry |
| OCI | Teams that publish extension artifacts as OCI images (e.g. to GHCR) as part of their release pipeline |
You can register multiple sources of either type per organization.
The scrydon.yaml Catalog File
Every source is described by a single scrydon.yaml file at a configurable path in your repository or registry. This file is the source of truth for which extensions should be installed and at what version.
apiVersion: scrydon.io/v1
kind: ExtensionCatalog
extensions:
- id: example.sample
version: 1.0.0
artifact:
kind: git-tar
ref: main
path: extensions/sample
signature:
kind: none
- id: example.fraud
version: 1.2.0
artifact:
kind: oci
ref: ghcr.io/example/scrydon-fraud@sha256:abc123def456...
signature:
kind: cosign-keyless
certificateIdentityRegexp: "https://github.com/example/.*"
certificateOidcIssuer: "https://token.actions.githubusercontent.com"Extension catalog fields
| Field | Required | Description |
|---|---|---|
id | Yes | Unique extension identifier, dotted lowercase (e.g. acme.fraud). Combined with version it identifies an extension installation. |
version | Yes | Semver string. The reconciler keys on this field — a release is only picked up when this entry is bumped, even if extension.json and the artifacts changed. See Extension versioning for the full release checklist. |
artifact.kind | Yes | git-tar or oci. |
artifact.ref | Yes | For git-tar: the branch, tag, or commit ref to check out. For oci: the full image reference — prefer @sha256:… digests over mutable tags. |
artifact.path | git-tar only | Subdirectory inside the repository to package. |
signature.kind | Yes | cosign-keyless, cosign-key, or none. |
Unknown keys in scrydon.yaml are rejected. Typos in field names cause a
parse_failed error — check the source's run history for details.
Publishing from CI
Git source layout
For a Git source, your repository contains the scrydon.yaml catalog alongside your extension directories. The reconciler extensions the declared subdirectory at the configured ref into a .scrydon-extension.tar.gz on the fly.
Minimal layout:
my-extensions/
├── scrydon.yaml # Extension catalog
├── extensions/
│ └── fraud/ # Extension directory — each has an extension.json
│ ├── extension.json
│ └── ontology/
│ └── manifest.json
└── .github/
└── workflows/
└── validate.yml # Validates scrydon.yaml on every PRValidate scrydon.yaml on pull requests so catalog errors are caught before they reach your Scrydon installation:
# .github/workflows/validate.yml
name: validate-catalog
on:
pull_request:
paths: ['scrydon.yaml', 'extensions/**']
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Validate catalog schema
run: |
bunx @scrydon/sdk-authoring sources validate scrydon.yamlOCI source layout
For an OCI source, your CI pipeline builds a .scrydon-extension.tar.gz for each extension, pushes it as an OCI artifact, signs it with cosign, and updates the scrydon.yaml catalog (also pushed as an OCI artifact).
Your workflow needs cosign for signing and oras for pushing generic OCI artifacts:
- uses: sigstore/cosign-installer@v3
- uses: oras-project/setup-oras@v1bunx @scrydon/sdk-authoring extension build extensions/fraud \
--out dist/fraud-${{ github.ref_name }}.tar.gzoras push ghcr.io/${{ github.repository_owner }}/scrydon-fraud:${{ github.ref_name }} \
--artifact-type application/vnd.scrydon.extension.v1.tar.gzip \
dist/fraud-${{ github.ref_name }}.tar.gz:application/vnd.scrydon.extension.v1.tar.gzipCOSIGN_EXPERIMENTAL=1 cosign sign --yes \
ghcr.io/${{ github.repository_owner }}/scrydon-fraud:${{ github.ref_name }}No secrets needed — cosign uses GitHub Actions OIDC automatically when id-token: write permission is granted.
Resolve the digest and update scrydon.yaml, then push it as the :catalog tag:
DIGEST=$(oras manifest fetch --output - \
ghcr.io/${{ github.repository_owner }}/scrydon-fraud:${{ github.ref_name }} \
| jq -r '.config.digest // .digest')
sed -i "s|REPLACE_WITH_DIGEST|$DIGEST|" catalog/scrydon.yaml
oras push ghcr.io/${{ github.repository_owner }}/scrydon-catalog:catalog \
--artifact-type application/vnd.scrydon.catalog.v1+yaml \
catalog/scrydon.yaml:application/vnd.scrydon.catalog.v1+yamlFull working examples are in the examples/extension-sources/ directory of the Scrydon SDK repository.
Registering a Source via the Settings UI
Go to Settings > Platform > Extensions > Sources.
Fill in the source details:
| Field | Description |
|---|---|
| Name | A human-readable identifier for this source (e.g. acme-extension-repo). Must be unique within your organization. |
| Type | Git or OCI. |
| URL | For Git: the HTTPS clone URL of your repository. For OCI: the registry reference (e.g. ghcr.io/acme/scrydon-catalog). |
| Ref | Git branch/tag (default: main) or OCI tag (default: catalog). |
| Catalog path | Path to scrydon.yaml inside the source (default: scrydon.yaml). |
| Access credential | Optional. For a private source, paste the credential directly (Git PAT, deploy-key PEM, or OCI username:token). It's saved as an encrypted organization secret and referenced automatically. Leave blank for public sources. See Authenticating a private source. |
| Signature policy | See Signature Policies below. |
| Sync interval | How often the reconciler checks for changes (30 seconds–1 day). |
Click Test connection to verify the reconciler can reach your source and parse scrydon.yaml without persisting anything.
The source is enabled immediately. The first sync runs within one interval.
For installations managed with Helm, extension sources can also be seeded via extensionSources: in your chart values file. Helm-managed sources appear read-only in the UI with a "managed by chart" banner. See the Helm reference for the full schema.
Authenticating a private source
Private Git repositories and private OCI registries need a credential. Paste it straight into the source's Access credential field — Scrydon saves it as an encrypted organization secret for you and wires up the reference automatically. You never manage a separate secret by hand.
In Access credential, enter:
- Git over HTTPS — a personal access token (PAT) with
readscope on the repository. - Git over SSH — a deploy-key private key in PEM form (begins with
-----BEGIN). - OCI — a
username:tokenpair for the registry.
Click Test connection — it uses the credential you just entered (before anything is persisted), so a green result confirms it works. On Save, the credential is stored as an encrypted org secret (named extension-source-<name>) and the source references it; every sync resolves and decrypts it automatically.
To rotate, edit the source and enter a new value (leaving it blank keeps the current one). Saved credentials are never displayed again. The underlying secret is visible (and manageable) under Settings → Secrets.
Credentials are stored as organization-scoped secrets in Scrydon (LOCAL strategy). External-KMS-backed secrets (BYOK/HYOK) aren't used for extension sources yet — a source falls back to anonymous access if its credential is external or missing.
Delivering custom extensions
An extension can include an extension content entry in its extension.json. When such an extension is installed, the platform registers the custom extension alongside any other content kinds (ontology, process-flow, etc.) — no separate upload step.
Sync only places extensions in your org catalog — installation is an explicit second step. The Settings → Platform → Extensions → Add extension catalog is where custom extensions are discovered and first installed. An extension's row on the Installed tab (and its sheet's Version & security) is where you track install state across all content kinds and apply updates.
In the Add extension catalog, a synced custom extension is a row that names its extension (from the <extension> extension), listed with the vendors it belongs among — and Filter → Source → From your extensions shows only those:
- Not yet installed — shows Connect when the first step after install is a credential, and Install when it is not; either one installs and activates the extension immediately, and Connect continues straight into its connect step.
- Already installed and current — is the vendor's own row, collapsed under its group's N installed line, with Manage opening the vendor sheet.
- Update available — is not offered in Add extension. The vendor's row on the Installed tab carries it in the version column (
1.3.0 → 1.4.0), and the update is reviewed and applied in the extension's sheet under Version & security. See Extension version management.
Installing a whole extension (including its non-extension content kinds such as ontology, workflows, and data sources) is done from the Add extension catalog's Solutions & content group.
Connecting or installing an extension from the Add extension catalog activates it immediately — the explicit admin action is the review. Signature policy gates non-interactive flows:
| Install trigger | Activation status |
|---|---|
| Explicit admin install (Add extension Connect / Install, or Update in an extension's Version & security tab) | Active |
Non-interactive install, cosign-keyless / cosign-key source | Active |
| Non-interactive install, unsigned artifact | Pending Review — approve from the extension's row on Extensions → Installed |
Size limits for extension artifacts: 8 MB per archive.tar.gz, inside an extension archive of at most 16 MB compressed. Both are enforced by the same pipeline whichever way the extension arrives — a source sync or a one-off upload — so there is no larger door; an artifact above 8 MB has to get smaller. See the extension authoring guide for the full subdir layout and extension.json declaration.
Signature Policies
Every extension artifact must pass signature verification before it is installed. The signature policy is set per source at registration time.
New sources default to cosign-keyless. Regulated deployments can set Helm value auth.requireSignedExtensionSources: true; this rejects policies with any unsigned-success branch during source creation, update, connection testing, reconciliation, and workspace activation. Existing unsigned catalog content cannot be activated while the setting is enabled.
| Policy | Use case |
|---|---|
cosign-keyless | Artifacts signed via GitHub Actions OIDC (no key management required). Specify certificateIdentityRegexp to pin to a specific repository and certificateOidcIssuer to pin to the OIDC provider. |
cosign-key | Artifacts signed with a static key pair. Provide the PEM-encoded public key. |
unsigned-allowed | Explicit opt-in to skip signature checks. Not recommended for production. Surfaces a permanent warning banner in the UI. |
cosign-keyless (recommended)
signature:
kind: cosign-keyless
certificateIdentityRegexp: "https://github.com/acme/scrydon-extensions/.*"
certificateOidcIssuer: "https://token.actions.githubusercontent.com"The reconciler calls cosign verify with these constraints. The OIDC issuer and certificate identity must match exactly — an extension signed in a different repository or by a different CI system will fail verification.
For this to work, your publish workflow must request id-token: write permission and use sigstore/cosign-installer@v3.
cosign-key
signature:
kind: cosign-key
publicKey: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----Generate a key pair with cosign generate-key-pair and paste the public key above. Keep the private key in a CI secret (COSIGN_PRIVATE_KEY) and sign with cosign sign --key env://COSIGN_PRIVATE_KEY.
unsigned-allowed
Use only for local development or air-gapped lab environments where running a signing infrastructure is impractical. In scrydon.yaml, entries under a unsigned-allowed source use signature: { kind: none }.
unsigned-allowed is logged as a security event at registration time. Every
sync run under this policy is also flagged in the audit log. Enable it for a
production source only after explicit acceptance by your security team.
If auth.requireSignedExtensionSources is enabled, unsigned-allowed is unavailable even for an explicit administrator action. Configure cosign-keyless, cosign-key, or another policy whose every branch requires signature verification.
Troubleshooting
verify_failed
Signature verification failed for one or more extension artifacts.
- Check that your publish workflow has
id-token: writepermission (for keyless). - Confirm the
certificateIdentityRegexpmatches the GitHub Actions run URL for your repository (e.g.https://github.com/acme/scrydon-extensions/.*). - Confirm the
certificateOidcIssuermatches the OIDC provider your CI uses (https://token.actions.githubusercontent.comfor GitHub Actions). - For
cosign-key: verify the public key in the source configuration matches the private key used to sign the artifact. - Check that the extension was signed after being pushed to the registry — signing a different tag or digest than the one referenced in
scrydon.yamlwill fail.
fetch_failed
The reconciler could not reach your source.
- For Git sources: verify the URL is an HTTPS clone URL and that any credentials (PAT or deploy key) have
readscope on the repository. - For OCI sources: verify the registry reference is correct and that the pull credentials have read access to the repository.
- Verify the source is reachable from your Scrydon cluster's network. The reconciler makes outbound HTTPS requests — ensure egress is not blocked.
- Click Test connection in Settings to get a more detailed error.
parse_failed
scrydon.yaml failed schema validation.
- Run the validator locally:
bunx @scrydon/sdk-authoring sources validate scrydon.yaml - Common causes: unknown keys (typos), missing required fields, invalid extension ID format (must be dotted lowercase, e.g.
acme.fraud), invalid semver, artifactrefmissing for OCI entries. - Confirm the file is valid YAML before checking the schema.
install_failed
The extension was fetched and verified but failed to install.
- The extension archive itself may be malformed. Test it locally:
bunx @scrydon/sdk-authoring extension test extensions/fraud - Check the run history error detail in Settings > Platform > Extensions > Sources — click a source row (or its View history action) to open the run-history panel — for the specific
extension.jsonvalidation error. - Confirm the
extension.jsonpackage.idmatches the dotted-lowercase pattern and thatinstallOrdermatches the kinds incontents.
no_workspace_environments
The organization has no workspace environment to receive ontology content. No
ontology request was made. Create the intended workspace environment, then run
Sync now again. The new-environment hook installs the built-in
knowledge-base ontology for that environment; source-managed extensions
still converge on the next source sync.
Some ontology targets failed or were skipped
- Check
ontologyAutoApplyin the underlying Sync now API response. ComparetargetAttemptswith the applied, skipped, and failed counts. not_installedis an intentional skip: automatic reconciliation updates an installed extension but does not enroll a new environment in that extension.- For failures, use the returned/logged workspace and environment tuple plus the bounded error text. The failure digest covers the complete set when more than 16 summaries exist.
- Fix the target-specific cause and retry Sync now. Successful targets are idempotent and unchanged source entries are deliberately revisited.
- Do not copy a manifest body into tickets or logs. Use the catalog/source provenance fields and digests to correlate the artifact.
Source shows "managed by chart" and is read-only
This source was seeded via extensionSources: in your Helm values. Edit the chart values to change it — do not create a duplicate UI-managed source with the same name.
Sync stops after repeated failures
After three consecutive failed runs, the reconciler backs off to double the configured interval (capped at one hour). Fix the underlying error — the backoff resets automatically on the next successful sync. Clicking Sync now in the UI resets the counter immediately.
A retired extension came back / an extension is missing from the catalog
Source-managed extensions follow the source catalog: a retired extension that is still listed in scrydon.yaml is restored on the next sync, and one that is no longer listed is retired (see The source catalog wins). If an extension you expect is missing, check that it is listed in the source's scrydon.yaml, that the latest run in the source's history succeeded, and that the Sync now toast reports it as installed.
Installed list is incomplete after a successful sync
The Installed list reads installation state from the services that own each content type. If one of those services is unavailable, the list displays an incomplete-list warning. Restore the affected service, then select Try again. You do not need to add or reinstall an extension that was already installed successfully.
Sync progress
Manual sync shows catalog fetching, checked extension counts, and installation counts with the extension name. Independent checks and installs run up to three at a time, and Git sources reuse their checkout during each run. Current code and ontology releases are reported as already installed; older releases are updated through the normal installation flow.
Inspect installed extensions
The Installed list groups your organization's extensions under its current name and third-party extensions under Other vendors. Vendor versions reflect the running vendor manifest; source extension versions reflect their installed release.
Open an extension's Provides tab to see capability badges directly in each row. Select a badge to inspect its members, including ontology types and data sources. Ontology bindings show the source table and target type instead of numbered placeholders. The Connections tab shows each credential's account, status, environment, and covered products; connection actions are available from its row menu.
An install fails with marketplace_content_invalid
One of the extension's content entries is present in the catalog row but could not be decoded, so there is nothing to install for it. The message names which check failed — for example marketplace_content_invalid:invalid_content_entry — and your operator's logs carry the underlying schema error (manifestVersion: Invalid input: expected 1, say).
The usual cause is content authored against a newer manifest contract than the installation accepts. Other entries in the same extension are unaffected and stay installable; only the entry that failed to decode is refused.
- Validate the extension locally:
bunx @scrydon/sdk-authoring extension test extensions/<name> - Check the
manifestVersionof the failing content's manifest against the SDK version your installation runs.
Extension identity fields
Extension catalog and installation requests use extensionId and extensionVersion. Ontology provenance uses extensionOrigin. MCP and code-release records distinguish their code/vendor extensionId from their containing catalogExtensionId.
When rebuilding authored workflows, the Create Process Instance block takes extensionId. Update older definitions to use this field before publishing them with the current SDK. Installed workflows are migrated with the platform; published source archives remain immutable and require a new release to change their contents.