Build Channels with the SDK

Author Channel protocols and Auth Providers that normalize communication without claiming platform authority.

Use authProvider() to verify ingress and channel() to translate a protocol into canonical events.

Verify the caller

ts
import { authProvider } from "@constal/sdk";

export default authProvider({
  id: "signed-webhook", version: "1.0.0",
  needs: [{ binding: "verifier", kind: "service", ops: ["verify"] }],
  async authenticate({ request }, context) {
    const proof = await context.invoke<{ valid: boolean; subject?: string }>(
      context.resources.verifier!, "verify", { headers: request.headers, bodyBase64: request.bodyBase64 },
    );
    return proof.valid && proof.subject
      ? { authenticated: true, subject: proof.subject }
      : { authenticated: false, reason: "invalid signature" };
  },
});

Normalize the protocol

ts
import { channel } from "@constal/sdk";

export default channel({
  id: "webhook", version: "1.0.0", public: true,
  authProvider: { kind: "local", resourceKind: "auth-provider", id: "signed-webhook" },
  protocol: {
    id: "json-webhook", version: "1",
    receive(request) {
      const body = JSON.parse(atob(request.bodyBase64 ?? ""));
      return { id: body.id, type: "message", session: body.session, data: body.message };
    },
    respond(result) {
      return { status: result.status === "failed" ? 500 : 200,
        headers: { "content-type": "application/json" }, bodyBase64: btoa(JSON.stringify(result)) };
    },
  },
});

Auth Provider output is evidence, not platform authority. Central authentication assigns tenant and customer identity before receive. Both contexts expose declared Resources through governed invoke() and never expose Credential bytes. See Deploy a Channel and Deploy an Auth Provider.

The local Auth Provider reference is package identity, not runtime authority. Direct deployment and catalog installation resolve it inside the authenticated tenant and namespace, verify its kind, and record the exact selected Auth Provider on the Channel revision.

Routing is not embedded in Channel code. constal.channel.json owns an Agent target Resource selector; Agent manifests own labels. If one Agent matches, receive may omit its event target. If several match, the protocol may return a target Agent CRN, but the platform rejects it unless that Agent matches the pinned selector.

Keep event and delivery ids stable so retries resolve to recorded outcomes. An optional send handler uses the same declared Channel context for outbound delivery. Register analytics definitions on the Channel before emitting them.