Scrydon
ExtensionsBuilding & publishing

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:

  1. Fetches the scrydon.yaml catalog file from your source.
  2. Compares it against the catalog versions already synced.
  3. For each new or changed entry: fetches the artifact, verifies its signature, and publishes it to the organization’s catalog.
  4. 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.yaml that 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 typeBest for
GitTeams that version extension contents directly in a Git repository and don't want to run a container registry
OCITeams 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

FieldRequiredDescription
idYesUnique extension identifier, dotted lowercase (e.g. acme.fraud). Combined with version it identifies an extension installation.
versionYesSemver 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.kindYesgit-tar or oci.
artifact.refYesFor git-tar: the branch, tag, or commit ref to check out. For oci: the full image reference — prefer @sha256:… digests over mutable tags.
artifact.pathgit-tar onlySubdirectory inside the repository to package.
signature.kindYescosign-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 PR

Validate 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.yaml

OCI 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@v1
bunx @scrydon/sdk-authoring extension build extensions/fraud \
  --out dist/fraud-${{ github.ref_name }}.tar.gz
oras 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.gzip
COSIGN_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+yaml

Full 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:

FieldDescription
NameA human-readable identifier for this source (e.g. acme-extension-repo). Must be unique within your organization.
TypeGit or OCI.
URLFor Git: the HTTPS clone URL of your repository. For OCI: the registry reference (e.g. ghcr.io/acme/scrydon-catalog).
RefGit branch/tag (default: main) or OCI tag (default: catalog).
Catalog pathPath to scrydon.yaml inside the source (default: scrydon.yaml).
Access credentialOptional. 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 policySee Signature Policies below.
Sync intervalHow 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 read scope on the repository.
  • Git over SSH — a deploy-key private key in PEM form (begins with -----BEGIN).
  • OCI — a username:token pair 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 triggerActivation 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 sourceActive
Non-interactive install, unsigned artifactPending 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.

PolicyUse case
cosign-keylessArtifacts 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-keyArtifacts signed with a static key pair. Provide the PEM-encoded public key.
unsigned-allowedExplicit opt-in to skip signature checks. Not recommended for production. Surfaces a permanent warning banner in the UI.
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: write permission (for keyless).
  • Confirm the certificateIdentityRegexp matches the GitHub Actions run URL for your repository (e.g. https://github.com/acme/scrydon-extensions/.*).
  • Confirm the certificateOidcIssuer matches the OIDC provider your CI uses (https://token.actions.githubusercontent.com for 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.yaml will 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 read scope 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, artifact ref missing 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.json validation error.
  • Confirm the extension.json package.id matches the dotted-lowercase pattern and that installOrder matches the kinds in contents.

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 ontologyAutoApply in the underlying Sync now API response. Compare targetAttempts with the applied, skipped, and failed counts.
  • not_installed is 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 manifestVersion of 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.

On this page

On this page