Authoring a connector
A connector deploys a renewed credential to a target — NGINX, Apache, IIS, HAProxy, F5 BIG-IP, AWS ACM, Azure Key Vault, GCP Certificate Manager, and so on. The connector SDK extracts everything those share, so a new connector is a small, focused change: implement one seam and declare the capabilities it needs.
The Connector interface
type Connector interface {
Name() string
Capabilities() pluginhost.Grant
Deploy(ctx context.Context, sb connector.Sandbox, dep connector.Deployment) error
}
Nameidentifies the connector.Capabilitiesdeclares the least set of privileges the connector needs, using the same grant model as WASM plugins (pluginhost.Grant): for exampleCapFSWriteconstrained to a path,CapNetDialto reach a network target, orCapExecto run a reload command. The connector cannot exceed what it declares.Deployinstallsdep(the certificate and key, as PEM) using only theSandboxit is handed — never ambient I/O.
The Sandbox
Deploy performs all of its side effects through the Sandbox, and every
operation is checked against the connector's declared Capabilities. Anything
outside the grant returns ErrDenied.
WriteFile— write a file at a granted path (for file-based servers like NGINX/Apache/HAProxy).Send/ request primitives — call a network management API (for appliances and cloud targets like F5, ACM, Key Vault).Exec— run a granted command, typically a reload or restart.
Idempotency
Deploy must be idempotent on dep.Fingerprint: replaying a deploy leaves
the target in the same state. Delivery to connectors is at-least-once (the
orchestrator dispatches through an outbox), and idempotency is what makes the
effect exactly-once. Practically: check whether the target already holds the
credential with that fingerprint before writing, and make the reload safe to
repeat.
Delivery (AN-6)
Deployment is outbox-driven. The orchestrator enqueues a connector.deploy
message (EncodeDeploy) in the same transaction as the lifecycle state change,
and a worker hands it to the connector via a Registry:
reg := connector.NewRegistry(opsFor) // opsFor supplies each connector's real Ops
reg.Register(myconnector.New(...))
outbox.HandlerFunc(func(ctx, m) error { return reg.Handle(ctx, m.Payload) })
At-least-once delivery plus an idempotent Deploy make the effect exactly-once.
A minimal connector
The connector SDK ships a complete example file-plus-reload connector
(the NGINX/Apache shape): it declares CapFSWrite for its config path and
CapExec for its reload, writes the PEM with WriteFile, and runs the reload
with Exec. Start by copying it.
Conformance
The SDK ships a conformance suite (connector.Conformance) that exercises a
connector — driven by the in-memory target harness (connector.MemoryOps) —
against the host contract. A connector can only ever do what its grant permits,
the same sandbox discipline the plugin host enforces for WASM plugins, so
conformance is also the least-privilege check: it verifies the connector names
itself, declares at least one capability, deploys a credential, stays idempotent
over persistent target state, and has every operation outside its declared grant
denied. Wire your connector into it and keep it green; that is what lets forks
and downstream users trust a third-party connector.
func TestMyConnectorConformance(t *testing.T) {
report := connector.Conformance(context.Background(), myconnector.New(...))
if !report.OK() {
t.Fatal(report)
}
}
See the connector SDK reference for the full contract and the list of in-tree connectors to model yours on.