Connector models
A connector model is a reusable blueprint which holds applications. Define it once at the provider level; attach it to any connector to automatically materialize those applications on that connector.
The server keeps the longer ConnectorModel / connector-models naming on its API surface. In the docs we call them models for brevity.
Why use a model
- Provision once, apply many times. A provider defines the shape of a "standard device" (which applications it offers, on which ports) once. Every connector instantiated from that model gets the same applications automatically.
- Centralized updates. PATCHing a model's
applicationslist propagates the change to every connector currently attached to it: applications added to the model are materialized on each connector; removed applications are detached. - Re-shape a fleet in place. A connector can be switched from one model to another, or detached entirely, without being torn down and recreated — it keeps its identity and enrollment throughout, so nothing has to be re-enrolled. See Attaching a model.
- One definition of what a connector hosts. Attaching a model clears whatever the connector was hosting and materializes the model's set, so an attached connector's applications are exactly the model's — never a mixture with whatever was added directly beforehand.
Fields
| Field | Description | Example |
|---|---|---|
id | Unique ID assigned at create time | 9a8b7c6d-5e4f-4321-9876-543210fedcba |
providerId | Parent provider — set on create, immutable | 8b14d4f9-3a52-4b91-8e7c-d3e44b5c1f2a |
name | Human-readable name, unique within the provider (1–255 chars) | edge-device |
description | Free-form description (up to 1000 chars) | Standard edge device blueprint |
type | A ConnectorType — sets the type of every connector that attaches this model. Set on create, immutable (silently ignored if supplied on update) | DEVICE |
applications | List of application templates. Each gets a server-assigned key (UUID) that connector overrides bind to; name is a default a connector may override, while type and protocol are fixed by the model. Each address also gets a server-assigned key (UUID); runtime address fields (listenAddress, listenPort, and the corresponding targetAddress / targetPort or allowedAddresses / allowedPorts) may be left null on the model and filled in per-connector via overrides. The forwardAddress / forwardPort flags default to false on create when omitted, so the model commits to a forwarding direction up-front. | |
Lifecycle of an inherited application
A connector that attaches a model receives a materialized Application for every entry in the model. These rows are marked with connectorModelId = <model uuid> so Customer
Connect can distinguish inherited applications from explicitly created ones.
- Inherited applications are read-only. PATCH/PUT/DELETE on them returns
409 Conflict. There is no way to convert one into a standalone application. - Deleting the connector removes its inherited applications.
- Any change of the connector's attachment — attach, switch, or detach — clears every application on it, those created directly on the connector included. An attached connector hosts exactly its model's set; a detached one hosts none. See Attaching a model.
Per-connector overrides
A Model defines the shape of each application; a connector can fill in or replace runtime values via applicationOverrides
on the connector itself. Overrides reference apps by their stable server-assigned key, and addresses by their stable key. A connector override may replace an application's
name
(its appName); type and protocol are fixed by the model and are not overridable.
If a model leaves a runtime field (e.g. targetAddress) null, every connector attaching that model must supply it via override before the application can be materialized.
Response-only override metadata
GET responses for a model decorate each application and address with two read-only lists so callers (typically a UI) don't have to derive the override surface from the schema:
| Field | Where it appears | Meaning |
|---|---|---|
overridableFields | on each application and each address | Names of the fields a connector's applicationOverrides may set for this entry. Static — mirrors the override schema's mutable surface. |
requiredFields | on each application and each address | Names of the fields the connector must supply via override before this entry can be materialized. Computed per-instance from the model's current values (it tracks what the model author left null). |
requiredFields mirrors the runtime materialization rules: listenAddress / listenPort are required if missing; if
forwardAddress is true the allowedAddresses is required, if false targetAddress is required instead.
forwardPort / targetPort / allowedPorts follow the same pattern. These fields are emitted only on responses. Clients sending them on create/update are ignored, and the values
are never persisted into the model itself.
Template variables in model applications
Instead of a concrete value, an application's templated fields can hold a {{variable}} placeholder: the default name, and each address's runtime value fields (listenAddress,
listenPort, targetAddress, targetPort, allowedAddresses, allowedPorts). Placeholders resolve against the attaching connector's context at materialization — never at model
create/update, where no connector exists yet — drawing on four namespaces:
| Namespace | Variables | Notes |
|---|---|---|
provider.* | provider.id, provider.name, provider.networkId, provider.networkGroupId, provider.organizationId, provider.custom.* | The connector's provider. |
customer.* | customer.id, customer.name, customer.custom.* | The location's customer, if any — unresolved at a location directly under the provider. |
location.* | location.id, location.name, location.description, location.address, location.custom.* | The location the connector attaches to. |
connector.* | connector.id, connector.name, connector.type | The connector itself. Resolvable only at materialization; there is no connector.custom.*. |
Both the built-in variables (like {{customer.name}}) and any custom variables defined on those resources (like {{provider.custom.edgeDomain}})
are available. The connector.* namespace is a deferred projection: it is accepted on the model but left unresolved until a connector materializes it, so a model can name its
own applications and hosts after the connector that will host them. The rendered values are then validated as real addresses and ports.
Host and address fields (listenAddress, targetAddress, allowedAddresses) are URL-safe: after a variable resolves, any character outside the RFC 3986 unreserved set (plus
: and /) is stripped from the resolved value before it is substituted, so a {{connector.name}} of "east gw" yields eastgw in a hostname. Ports, name, and description
are substituted verbatim.
Generator tokens
A field can also carry a {{type:count}} generator token that produces a fresh pseudo-random string instead of referencing the catalog: {{number:N}} (digits), {{char:N}}
(lowercase letters), {{alpha:N}} (lowercase alphanumerics), where N is the character count (1–255). Generated characters are URL-safe by construction. The value is
deterministic per connector, field, and occurrence: generated once at materialization and reproduced byte-for-byte on every later re-sync, so it never churns the Ziti
projection, yet two connectors — or two fields on one connector — get different values. Use it to give each materialized connector a unique but stable host label, e.g.
{{alpha:8}}.{{location.name}}.ziti.
A templated field counts as required in the response-only requiredFields list: it resolves automatically where the variable has a value, and needs a connector override where it
does not (for example {{customer.*}} at a location without a customer). An unresolvable placeholder (a typo, or a custom variable not defined on the attaching connector's
resources) fails the connector create with a 400 rather than materializing a blank value.
Attach rules
A model can be attached to a connector at create time, and attached, switched, or detached later by sending connectorModelId to PUT/PATCH /connectors/{id} — a uuid
to attach or switch, null to detach (see Attaching a model). Every path enforces the same rules:
- The model exists and belongs to the same provider as the connector's location.
- The model's
typemust match the connector's — on create, when the connector also specifies atype; afterwards, always. A connector's type is fixed once it exists, so a model of a different type can never be attached to it. - On create only, the model must not define an application whose name collides (case-insensitively) with an ad-hoc application supplied in the same request. Attaching later needs no such rule: the connector is cleared first, so there is nothing left for the incoming model's names to collide with.
Violations return 400 Bad Request before anything is written. Changing the attachment on a live connector additionally requires the provider's network to be available, and is
rejected with 422 otherwise.
Resource counts
Every model response includes a counts object with the number of inherited applications and source/destination access policies scoped to the model (sourceAccessPolicies and
destinationAccessPolicies). Applications are aggregated across every connector that inherits from the model; this is the live deployment footprint, distinct from the template
list on the model itself. Source and destination access policy counts are independent: an access policy with the same model on both sides is counted in each. It's populated on
every list and get response, so dashboards and navigation UIs can render counts without paging through the underlying collections.