Skip to content
Esc
navigateopen⌘Jpreview
On this page

SDK

The github.com/ethndotsh/switchboard/sdk package: request accessors, action constructors, builders, and Chain.

import "github.com/ethndotsh/switchboard/sdk"

A rule exports one function with this signature; switchboard build generates the Wasm wrapper:

func Handle(req sdk.Request) sdk.Action

sdk.Request

Read accessors. Inside the proxy these are lazy host calls; a rule pays only for the fields it reads.

Method Returns Notes
Method() string HTTP method.
Path() string URL path. Reflects earlier rewrites in a chain.
Host() string Request host.
RawQuery() string Raw query string, without ?.
Query(name) string One decoded query parameter.
Scheme() string http or https.
Protocol() string e.g. HTTP/2.0.
RemoteAddr() string Peer address as connected.
ClientIP() string Adapter-resolved client IP; honors Caddy trusted_proxies.
TLS() bool Whether the request arrived over TLS.
Header(name) string First value, case-insensitive. Empty string when absent.
HeaderValues(name) []string All values for a header.
Cookie(name) string One cookie value.

Action constructors

Constructor Decision Default status
sdk.Next() continue to the next handler / upstream
sdk.Deny(status) reject the request 403 when status is 0
sdk.Redirect(status, location) redirect the client 302 when status is 0
sdk.Rewrite(path) rewrite the path, then continue
sdk.Respond(status, body) serve a synthetic response 200 when status is 0

Redirect statuses must be 300–399; all statuses must be 100–599 (validated on emit).

Builders

All builders return the modified Action, so they chain. They can be attached to any constructor.

Request patch: mutate what the upstream sees

Method Effect
SetRequestHeader(name, value) Set (replace) a request header.
AddRequestHeader(name, value) Append a request header value.
DeleteRequestHeader(name) Remove a request header.
RewriteHost(host) Change the request host.
RewritePath(path) Change the request path (must start with /).
RewriteQuery(query) Replace the raw query string.

Response: mutate what the client sees

Method Effect
SetResponseHeader(name, value) Set (replace) a response header.
AddResponseHeader(name, value) Append a response header value.
DeleteResponseHeader(name) Remove a response header.

Annotations

Method Effect
WithReason(reason) Label the decision. Surfaces in access logs, test output, eval, and replay diffs.
SetMetadata(name, value) Expose a key/value to the surrounding proxy as Caddy request variables, usable for routing.

Header operations count against max_header_ops, and all emitted strings count against max_action_bytes; see execution limits.

sdk.Chain

func Handle(req sdk.Request) sdk.Action {
	return sdk.Chain(req,
		BlockInternalPaths,
		RewriteLegacyPaths,
		AddRuleHeader,
	)
}

Runs each func(sdk.Request) sdk.Action in order. Next() results accumulate their request patch, response headers, metadata, and reason; later rules observe the request as rewritten so far. The first Deny, Redirect, or Respond short-circuits with all accumulated state. See Writing rules.

Data files

Read-only files bundled under data/ (see Bundle data files). Each accessor parses and caches its file on first use; files are named relative to the data directory.

Accessor File shape Returns
sdk.DataBytes(name) any raw []byte, nil if absent
sdk.DataString(name) any contents as a string
sdk.DataSet(name) newline-delimited (blank and # lines ignored) sdk.Set; Set.Contains(s) bool (unordered, deduped)
sdk.DataLines(name) newline-delimited (blank and # lines ignored) []string, ordered, duplicates kept
sdk.DataMap(name) JSON object of strings map[string]string, nil if absent or invalid
sdk.DataJSON(name, &v) JSON value unmarshals into v; error on invalid JSON
sdk.DataJSONL(name, &v) JSON Lines appends each line into *[]T
sdk.DataTOML(name, &v) TOML unmarshals into v; error on invalid TOML
sdk.DataCSV(name) CSV [][]string rows

Each format’s parser is linked into the guest only when its accessor is called, so rules pay for a format only if they use it. YAML is not supported (yaml.v3 fails under TinyGo). sdk.LoadTestData(map[string][]byte) injects data for host-side rule tests; keys may include or omit the data/ prefix. See Bundle data files.

Host-side testing

The SDK doubles as a plain Go library for unit tests: sdk.NewRequest(data) builds a populated request from an sdk.RequestData literal, so rule functions can be tested with go test before ever compiling to Wasm:

func TestHandle(t *testing.T) {
	req := sdk.NewRequest(sdk.RequestData{Method: "GET", Path: "/blocked"})
	action := Handle(req)
	if action.Decision != sdk.DecisionDeny {
		t.Fatalf("want deny, got %s", action.Decision)
	}
}

Decision constants: DecisionNext, DecisionDeny, DecisionRedirect, DecisionRewrite, DecisionRespond.

ABI

The SDK targets the switchboard/v4 host-function ABI, recorded in each bundle’s descriptor. v4 adds the data-file host functions; a host requires an exact ABI match, so bundles built for an earlier ABI must be rebuilt. Request-header and response-header operations are explicitly separate; the v2 API where one operation meant different things per action is gone.

Last updated on July 20, 2026

Was this page helpful?