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 authoring contract itself (manifests, contribution surfaces, lifecycle, migrations): Building Extensions.
- The open import surface + contract suites: Extension SDK & Testkit.
- Building + shipping an extension wheel: Extension Packaging & Extraction.
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:
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:
- Install the open surface from public PyPI:
pip install vectis-sdk[engine] vectis-testkit[engine]. - 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:
- Follow the authoring contract in Building Extensions:
module-level
manifest, register inon_activate, importvectis_sdk.*only, own your migrations with theext_<name>_table prefix. - Carry your own
pyproject.tomland build a standalone wheel per Extension Packaging & Extraction. An extension depends on the publicvectis-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. - 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). - 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.