Scrydon
ExtensionsAdministering

Delivering & Managing Custom Extensions

Deliver custom extensions through extensions (Git/OCI sources), and manage installed extensions

Manual tarball upload has been removed. Custom extensions are now delivered exclusively through extensions — a Git or OCI source your CI/CD publishes to, installed from your org's extension catalog. Publish your .archive.tar.gz inside an extension instead of uploading it directly. See Extension sources and Packaging a custom extension in an extension.

Custom Extension Scopes

Extension identities reserve three scopes. Organization uploads are the currently supported custom-extension path:

ScopeOriginVisibilityHow it's delivered
First-party (org:scrydon:)Baked into the Docker imageAll organizationsShipped with Scrydon releases
Platform (org:platform:)Reserved for a platform-scoped extensionAll organizationsNot currently enabled; requires dedicated platform-owned artifact storage
Organization (org:{orgId}:)Org extension sourceSingle organizationOrg admin installs from the org's extension catalog

Every organization-installed tool has a platform microVM floor. Only exact reviewed code shipped by Scrydon may use the approved Worker Thread tool backend; a matching vendor name never grants uploaded code that trust. A custom tool is selectable by an isolated Agent only while the exact Runtime Plane backend is qualified and attested.

Delivering via an extension source

Publish your built .archive.tar.gz as an extension content entry inside an extension, to a Git or OCI source your CI/CD controls. See Packaging a custom extension in an extension.

Go to Settings > Platform > Extensions → the Sources tab (direct URL: /settings/platform/extensions#sources) and register your extension source. Sources sync extensions into your org's extension catalog. See Extension sources.

Synced custom extensions appear in Add extension as rows that name their extension (extension name and version) with a Connect action when the first step after install is a credential, and Install when it is not — either one installs and activates it immediately (your explicit install is the review). A cosign-signed source is also trusted for non-interactive installs; unsigned automated syncs land as Pending Review.

When a new artifact is published, the card shows an Update badge; clicking it takes you to the extension's sheet (Settings → Platform → Extensions → Installed → open the extension → Version & security), where you review workflow impact and apply the update. See Extension version management.

What Happens on Install

When an extension carrying an extension entry is installed, the platform runs the same archive pipeline it always has:

Extracts the .tar.gz with security checks:

  • No symlinks allowed
  • No absolute paths
  • No path traversal (../)
  • No device, FIFO, duplicate-path, or unsupported archive entries
  • A bounded entry count and bounded decompression output
  • Only allowed file extensions (.js, .js.map, .json, .svg, .png, .jpg)
  • A required dist/index.js entry point
  • Maximum extracted size: 100 MB

Validates manifest.json against the ManifestSchema (Zod):

  • manifestVersion must be 1, the sole supported contract; any other version is rejected
  • Product workflow UI belongs under workflow; resources under resources; app consent under systemMode.permissions
  • Vendor ID format: /^[a-z][a-z0-9-]*$/
  • Tool IDs reference valid vendor prefixes
  • Auth credential types are valid
  • No duplicate product IDs
  • Required fields are present

Writes the artifact to blob storage:

00_organization/extension-archives/{extensionId}/{version}/archive.tar.gz

Registers the extension with the vendor ID, version, manifest JSON, storage key, SHA-256 hash, and scope in the org's installed-extension record (extension_code_release).

There is no compatibility upgrade during install. Rebuild any archive built against an older contract with the current authoring SDK before publishing it. Do not hand-edit manifest.json inside the archive.

Managing Installed Custom Extensions

Settings > Platform > Extensions > Installed lists every extension your organization has explicitly installed — through Add extension or an extension-catalog install — plus the built-in Scrydon vendor (shipped with every organization, cannot be removed) and your registered MCP servers, one row each. Catalog vendors nobody has added are not rows; they live in the Add-extension flow. A row reads as a sentence: the vendor's mark and name, its publisher (Scrydon, a vendor, or your org), one line of what it brings in words, a single line of state, and its version — an available update shows in the version column as 1.3.1 → 1.4.2 so it never displaces a state that needs a human. Enabling happens inside the sheet, where its org-wide scope is spelled out. Rows that need attention sort to the top, and the Filter button narrows by what a row includes (AI models, agent tools, content, MCP servers), state, and publisher.

States

StateMeaning
AvailableReady to use — credentials in place, products available in workflows. Content extensions append adoption: · on in 3 of 5 workspaces, or · not on in any workspace yet when no workspace has turned anything on
Needs credentials · step 2 of 3Installed, but an org-owned setup step is unfinished. The state names the next step and, in grey, where it sits in the count — the same count the sheet's Setup checklist shows. There is no separate "in progress" state
Credential failingThe last health check against the vendor failed — reconfigure or reconnect
Source sync failingThe extension source that delivers this extension cannot sync — see the Sources tab
Deprecated until …Scheduled for removal on that date; workflows still run until then
Not enabledEvery product is switched off — the extension stays listed so you can switch it back on
DisabledDisabled by lifecycle policy
Awaiting approval / UnreachableMCP servers only: pending review, or the last discovery could not reach the server. On a content extension whose next step is an MCP approval, the state reads Awaiting approval · step 2 of 2

The extension sheet

Click a row to open the extension's sheet. Everything about it lives there, in tabs:

  • Setup — while something is still owed, your org-owned steps as a checklist ("Microsoft account connected — OAuth · 12 products") with a progress meter, and the connection sections (console instructions, the OAuth redirect URL, organization-wide access) laid out flat beneath it. Once nothing is owed the checklist goes and the tab reads as the connection overview — the state it would have reported is already on the extension's row in the list. An extension that needs no account at all keeps a single line saying so.
  • Includes — what the extension gives you: AI models first (with the model list and the allowlist policy editor behind each capability), then agent tools grouped by product, each with its Enabled · org-wide switch — Not enabled when it is off. Turn on is the workspace word and never appears here. Every product gets a row, however many there are; the section header states the scale ("142 tools across 18 products") before the list starts.
  • Connections — the vendor's connections per product, their environments, and the default execution identity of each connection.
  • Access — which workspaces may use the vendor.
  • Usage — workflows and knowledge bases that reference it.
  • Version & security — present for every extension, and read top to bottom. Provenance first: who published it, whether its code runs in the platform process or in an isolated microVM (a trust shield marks third-party code; a Scrydon-published extension carries none), and May reach — the destinations its tools declare, and whether your organization's egress policy treats that declaration as a grant. Then Updates: the installed version and the one available, what changed, the update policy, and which workflows use it. An extension that ships with the platform has no separate version to check or policy to set, so that half reads as a single line and the provenance half is the tab.

The ··· menu in the sheet's header holds Enable / Disable for the organization and the lifecycle actions — Review new version, Deprecate / Cancel deprecation, Remove. The sheet is deep-linkable: /settings/platform/extensions?extension=<slug> opens it directly (older ?vendor= links still resolve).

The Models & defaults tab

Settings > Platform > Extensions > Models & defaults is the inventory of AI capabilities (LLM, embeddings, speech to text, text to speech, image, video, OCR, moderation, web search) and where the organization's defaults are set: one row per capability, always all of them, with the vendor that serves it and the model it currently resolves to, tagged automatic (the platform ranks what is installed) or pinned. The model name never repeats the vendor beside it. Change opens the picker in place: Select automatically, or a specific provider/model, saved per row. A capability whose vendor is present but resolves no model reads No model resolved rather than a dash. A capability nobody serves offers Add a vendor (the Add-extension flow). A note beside the row names what is worth knowing: a vendor that is installed but has every product switched off (with an Enable shortcut), or a vendor that is being retired and by when. One line heads the tab — Configured capabilities · 6 of 9 · Video, Moderation and Web search have no vendor.

The former Platform Defaults page is this tab; its address (/settings/platform/defaults) redirects here. Mute the sidebar badge in the tab's footer acknowledges the current gaps organization-wide so the badge on the Extensions menu item stays quiet until a new gap appears — the inventory itself is never hidden.

Enable / Disable

Enable / Disable for the organization in the sheet's ··· menu enables or disables every product of the vendor at once; the switch on a product row in the sheet's Includes tab toggles that product alone, org-wide. Disabling removes the products from the workflow editor but does not delete the stored files or credentials.

Versioning

Publish a new extension version to update the extension. The platform stores each version by {extensionId}/{version}/, so multiple versions can coexist; the live version is the semver-max active one (or the pinned version if your org pinned one — see Extension version management).

Uninstalling or superseding a custom extension will break any workflows that reference its tools. Verify no workflows depend on it first.

Catalog and Execution Sequence

When the platform starts:

  1. Scan first-party archives — parse manifest.json from /app/archives/ (Docker image)
  2. Scan platform archives — parse manifests from platform storage
  3. Scan org archives — parse manifests from each organization's storage
  4. Populate Manifest Catalog — metadata only, no vendor code loaded
  5. Publish dynamic readiness — the Runtime Plane reports whether the exact digest-pinned vendor profile has current candidate and deployment proof
  6. Admit on demand — the compiler exposes a custom Agent tool only when its verified SHA-256 source and exact backend pass the security floor
  7. Execute once — the trusted broker revalidates tenant, environment, connection, source, policy, and binding revisions, then starts a fresh vendor_archive microVM

Custom extension code is never loaded on the Agentic/API host at boot. The platform reads manifest metadata only. At execution, the active archive bytes are re-hashed, transferred through a one-use sealed grant, unpacked inside the vendor guest, and deleted with that single-use VM. The Agent's own agent_turn VM never receives the archive or reusable credentials.

Agent tool readiness

For an isolated Agent, a non-Scrydon tool is available only when all of these are true:

  • the active archive has a verified SHA-256 identity and still matches the compiled source revision;
  • the authenticated Runtime Plane supports the vendor_archive contract;
  • runner, broker, and enforcer images are digest-pinned;
  • a schedulable Kata/KVM substrate is observed;
  • signed exact-candidate and deployment proofs match the configured images, profile, and evidence digests;
  • actual-request egress, outbound DLP, and audit are ready; and
  • the encrypted one-use grant store is ready.

Any missing condition produces a typed remediation error. The platform never degrades an organization-installed Agent tool to a Worker Thread.

Organization Policies

Organization policies (configured by admins) govern custom extension behavior:

Execution Policy

{
  "maxConcurrentExecutions": 10,
  "maxTimeoutSeconds": 30,
  "allowedCapabilities": ["llm", "stt", "tools"]
}

Governance Policy

{
  "allowedVendorScopes": ["scrydon", "platform"],
  "blockedVendorIds": []
}
  • blockedVendorIds — vendor IDs blocked from installing (enforced on extension install)
  • allowedCapabilities — which capability types are permitted

The legacy allowOrgUploads / requireApprovalForUploads fields governed the removed manual upload flow and no longer have any effect. Whether an extension-delivered extension activates on install is governed by source signing and the explicit-install policy (see the delivery steps above), not these fields.

Package Format

Every custom extension is packaged as a .tar.gz archive with this structure:

{extensionId}-{version}.archive.tar.gz
├── manifest.json           (vendor metadata, JSON Schemas, UI definitions)
├── dist/
│   └── index.js            (compiled, minified ESM; all dependencies inlined)
├── meta/                   (automatically generated)
│   ├── sbom.cdx.json       (CycloneDX 1.6 SBOM)
│   └── metafile.json       (esbuild dependency graph)
└── assets/                 (optional)
    └── icon.svg, icon.png  (vendor/product icons)

SBOM Generation

Every custom extension artifact automatically includes a CycloneDX 1.6 Software Bill of Materials (SBOM) at meta/sbom.cdx.json. The SBOM is generated during sdk-authoring extension build with no additional tools or configuration required.

What's in the SBOM

The SBOM lists every NPM package that esbuild archived into dist/index.js:

  • Package name and version — exactly what's in the archive
  • License — SPDX license identifier from package.json
  • Package URL (purl) — machine-readable identifier (pkg:npm/zod@3.24.0)
  • Evidence — file paths proving the package is actually archived (tree-shaken code is excluded)

The SBOM only includes packages that esbuild actually archived. If a dependency was tree-shaken away, it won't appear — making the SBOM an accurate reflection of what ships in your archive.

How it's generated

During sdk-authoring extension build, the CLI:

  1. Runs esbuild with metafile: true to capture all input files
  2. Extracts unique NPM packages from node_modules/ paths in the metafile
  3. Reads each package's package.json for name, version, license, and description
  4. Serializes everything to CycloneDX 1.6 JSON
  5. Writes the result to meta/sbom.cdx.json alongside the esbuild metafile

No external tools or dependencies are required — the generation is built into the CLI.

How admins use it

After installing a custom extension, the Version & security tab of the extension's sheet shows the SBOM component count and lists all auto-detected packages alongside declared dependencies. See Custom Extension Review Before Activation for the full review workflow.

Compliance

The CycloneDX format is widely supported by compliance tools. For environments that require SPDX, convert using:

cyclonedx-cli convert --input-file meta/sbom.cdx.json --output-file sbom.spdx.json --output-format spdxjson

The raw esbuild dependency graph is also available at meta/metafile.json for debugging. You can visualize it at esbuild.github.io/analyze.

Custom Extension Review Before Activation

After a custom extension is installed, administrators can review its contents before enabling it for the organization. The extension's sheet provides a structured review experience across its tabs.

Includes

Shows all products, tools, triggers, and runtime capabilities the custom extension provides, each with its Enabled · org-wide switch. Intelligence capabilities (LLM, STT, TTS, Embedding) display model counts and support allowlist-mode policy configuration. See Capabilities for details.

Version & security — dependencies

The dependency facts sit in Version & security, in two complementary views of the custom extension's dependency tree:

  • Declared dependencies — Hand-authored entries from defineToolkit({ dependencies: [...] }) that include a human-readable reason for each dependency.
  • Auto-detected dependencies (SBOM) — The full set of NPM packages that esbuild archived into dist/index.js, extracted from meta/sbom.cdx.json. Each entry shows the package name, version, license, and a machine-readable package URL (purl).

The section shows the total SBOM component count so administrators can quickly gauge the custom extension's dependency footprint. Organization dependency policies (risk levels, blocklists, post-install script blocking) are evaluated against both declared and auto-detected dependencies.

If a dependency is blocked by the organization's policy, it is flagged in Version & security with the reason (e.g., "Risk 'high' exceeds max 'medium'" or "Has post-install scripts"). Blocked dependencies prevent the custom extension from being activated until the policy is adjusted or the .archive.tar.gz is rebuilt without the offending package.

Setup

Shows the vendor's authentication configuration (OAuth, API Key, Bot Token, or None) and allows administrators to configure credentials before enabling the vendor's capabilities.

Storage Layout

First-party (baked into Docker image):
  /app/archives/{extensionId}/dist/index.js

Platform uploads:
  {platformPrefix}/extension-archives/{extensionId}/{version}/archive.tar.gz

Org uploads:
  {storageKeyPrefix}/00_organization/extension-archives/{extensionId}/{version}/archive.tar.gz

Troubleshooting

Install fails with "Invalid manifest"

Run the CLI validator locally before publishing the extension to see detailed errors:

bunx @scrydon/sdk-authoring extension test --level static

Common issues:

  • Vendor ID contains uppercase or special characters
  • Tool ID doesn't start with the vendor ID prefix
  • Missing required fields (id, name, version, icon, auth)
  • Zod schema uses unsupported types (the extractor converts Zod → JSON Schema)

Custom extension status shows "Failed"

Check the error message in the extension's sheet (and server logs). Common causes:

  • dist/index.js has a syntax error
  • Default export is not a defineExtension() result
  • Missing export default statement
  • Runtime import fails (ensure all deps are archived — esbuild should inline them)

Tool doesn't appear in the workflow editor

  1. Verify the extension that carries the extension is installed (the extension's row on Settings > Platform > Extensions > Installed) and its version is active
  2. Verify the product is enabled for your organization in Settings > Platform > Extensions
  3. Check the block's category — it must be "tools", "blocks", or "triggers" to appear in the correct palette section

Build warns that block tool configuration will be omitted

tools.config.tool and tools.config.params are functions, so they cannot be represented in manifest.json. Replace dynamic routing with tools.toolSelector, expose direct parameters as block inputs, and move any remaining parameter coercion into the tool's execute() function. Do not ignore this warning: an installed workflow block only has the JSON-safe manifest definition at serialization time.

On this page

On this page