Skip to content

Building Extensions (External Developers)

This page is for developers building extensions against Vectis from outside the core team — including commercial extensions you intend to sell. It covers how you get a runnable platform to build against, how to author against the open SDK, the marketplace-foundation manifest fields, the entitlement model, and where extensions live.

Where this fits

The commercial model in one paragraph

The Vectis core engine, admin/storefront shells, first-party value-add extensions, and the hosted cloud are the commercial product — proprietary and paid (SaaS subscription first; licensed self-host later). They are not open-source and are never published to public package indexes. The developer surface stays open and Apache-2.0 so building against the platform is frictionless: vectis_sdk (the frozen API facade), vectis_testkit (contract suites + fixtures), the public vectis-ext-example, and this documentation are the only artifacts on public PyPI. This is Article 7 of the project constitution. Your extension is authored against the open SDK; running it requires a licensed core.

Two ways to get a platform to build against

Authoring an extension needs the open SDK; running and integration-testing it needs the (proprietary) engine present. There are two supported paths:

You are granted access to the repository. You get the full engine + SDK + testkit in-tree and install everything editable:

pip install -e ".[dev]"

Everything works locally with no PyPI needed — the SDK facade resolves against the in-tree engine, the testkit fixtures run against a real Postgres, and your extension activates via its entry point (or, without an install step, via the $VECTIS_DEV_EXTENSIONS dev-link — see Discovery).

Repo access grants the proprietary core source

This path hands you the source of the commercial core. It is for trusted developers and partners under the appropriate agreement — not arms-length third parties.

For arms-length developers (no core source), the path is:

  1. Install the open surface from public PyPI: pip install vectis-sdk[engine] vectis-testkit[engine].
  2. Obtain a free dev tier / developer license that provisions a runnable core (the developer distribution) to author and integration-test against.

You author against the open, versioned SDK; the dev distribution supplies the engine that backs the [engine] extra at runtime, without ever handing you the core source. A paid tenant is what runs the extension in production.

Why the [engine] extra

The SDK is a lazy PEP-562 facade: import vectis_sdk.<mod> pulls zero core, so pip install vectis-sdk (no extra) resolves on public PyPI without the non-public engine and gives you the interface + typing + contract reference. Resolving a symbol (or models / catalog / services, which are eager) needs the engine — so any real build/run env installs vectis-sdk[engine]. See Extension SDK & Testkit.

Authoring a commercial extension

Mechanically, a commercial third-party extension is authored exactly like a first-party one — the SDK boundary and contribution surfaces are identical:

  1. Follow the authoring contract in Building Extensions: module-level manifest, register in on_activate, import vectis_sdk.* only, own your migrations with the ext_<name>_ table prefix.
  2. Carry your own pyproject.toml and build a standalone wheel per Extension Packaging & Extraction. An extension depends on the public vectis-sdk (the API surface it imports — pin it to the core version you target), not on the proprietary engine directly (it isn't publicly installable); the engine is provided by the licensed core at runtime.
  3. Prove compliance with the five contract suites from vectis_sdk.testing (assert_imports_clean, assert_manifest_valid, assert_lifecycle_clean, assert_strategy_satisfies / assert_webhook_vectors, assert_schema_composes).
  4. Declare your commercial status on the manifest (below).

The open (Apache-2.0) examples/ext_example in the enterprise-extensions repo is the canonical copy-paste starting point (a real extension like ext_twilio/ works too).

New manifest fields (marketplace foundation)

Three manifest fields carry the marketplace/commercial metadata. All are declarative — the engine reads them; none of them gate activation at runtime today.

Field Type Default Purpose
license 'commercial' | SPDX id (e.g. "MIT") | None None Your extension's license declaration; set "commercial" for a sellable extension.
trust_tier first_party | third_party | certified | signed third_party Provenance tier, reusing the csp_grants.TRUST_TIERS vocabulary. certified / signed are the forward slots for certification/signing.
event_topics[].consumer_group string vectis-ext-<name> Per-extension Redpanda consumer-group isolation, declared per topic inside an event_topics entry.
from vectis_sdk.extension import ExtensionManifest

manifest = ExtensionManifest(
    name="acme_pro",
    version="0.1.0",
    display_name="Acme Pro",
    description="Commercial value-add for Acme workflows",
    category="marketing",
    license="commercial",          # sellable
    trust_tier="third_party",       # default; certified/signed reserved for signing
    event_topics=[
        {
            "topic": "vectis.orders.placed.v1",
            "event_type": "order.placed",
            "mode": "consume",
            "consumer_group": "vectis-ext-acme_pro",  # per-ext isolation
        },
    ],
)

event_topics is declarative

Declaring a topic makes it discoverable to the platform (topic derivation, lock hashing, generated topic docs). You still wire the handler via event_bus.subscribe in on_activate and publish via publish_event — see Events & Messaging. The consumer_group is the per-extension isolation convention.

The entitlement model

license="commercial" pairs with an ExtensionEntitlement record — one row per extension (extension_name unique) recording the declared license, trust_tier, a free-text status (e.g. active / trial / expired), an advisory expires_at, and where it was recorded from.

Honor-system today — no runtime gate

The entitlement is metadata only. It does not gate activation: a commercial extension with no recorded entitlement still runs. The boot advisory (vectis.core.entitlements) merely warns when a license="commercial" extension runs without a recorded row. This is by design — while Vectis hosts every tenant, entitlement is provisioning-gated (the owner controls each tenant's plan + enabled extensions; the ExtensionEntitlement record is the source of truth). Real per-instance license/entitlement enforcement arrives with licensed self-host, superseding the earlier honor-system-only stance.

Where extensions live

Extensions live outside the core monorepo — the core's own backend/vectis/extensions/ directory contains only the namespace __init__.py. The first-party set ships from a dedicated repository:

Enterprise Extensions repo

github.com/vectiscommerce/enterprise-extensions is the home of all 48 first-party extensions (proprietary commercial license). It ships a README, a developer guide (DEVELOPING.md), and a CI template that builds each extension's wheel and runs the core-free import contract — the recommended starting point for building and shipping a sellable extension. In development, the core's docker-compose.yml dev-links a sibling checkout of this repo via $VECTIS_DEV_EXTENSIONS.

Not every extension in that repo is itself sellable — MIT/permissive connectors (e.g. ext_excise_engine, a connector to the paid external Excise Engine service) carry their own permissive license while shipping from the same repo. Your own extension lives wherever you keep it: any repo whose ext_<name>/ package follows the authoring contract can be dev-linked or installed as a wheel.