Utilities & cross-slice
What sits at the SDK's top level, beside Backbone and Deed rather than inside either.
Signatures
// Outbound HTTP, egress-gated.
HTTPRequest(method, url string, headers map[string]string, body []byte) (*HTTPResponse, error)
HTTPRequestWithTimeout(method, url string, headers map[string]string, body []byte, timeout time.Duration) (*HTTPResponse, error)
type HTTPResponse struct { Status int; Body []byte } // no headers, status and body only
// drift.Slice(name): a slice you linked with `drift slice link add`.
// An unlinked name errors before any network I/O.
Get(path string) (*HTTPResponse, error)
Post(path string, body any) (*HTTPResponse, error) // JSON-encodes body
Request(method, path string, headers map[string]string, body []byte) (*HTTPResponse, error)
CallerSlice(req Request) string · Env(key string) string · Log(msg string)Outbound HTTP
The default timeout is 30 seconds; HTTPRequestWithTimeout takes your own. Private address space is unreachable whatever your Driftfile says: RFC-1918, link-local (including the cloud metadata endpoint) and CGNAT are excepted from the slice's outbound rule.
A blocked or failed request does not surface the same way in every language. Go, Python, Node.js and Ruby treat it as a failure: Go returns an error, and the other three raise or throw. PHP and Rust instead answer with a status of 0 and an empty or explanatory body, no exception involved, so a caller there has to check the status before trusting the rest of the response.
Go tells an egress refusal apart from a broken remote.
HTTPRequest wraps an allowlist refusal in *EgressDeniedError, matchable with errors.As, so a handler can answer one differently from a remote that is merely down. The match is best-effort, on the same connection refused that a refused dial and a genuinely dead host both produce, so treat it as a debugging aid rather than a proof.var ee *drift.EgressDeniedError
if errors.As(err, &ee) {
return 502, "external API not allowlisted", map[string]string{"host": ee.Host}
}Rust: outbound HTTPS needs the tls feature.
ureq with default-features = false, so it is pure Rust and cross-compiles with rustup alone. In that build an https:// URL returns (0, "drift-sdk: outbound HTTPS needs the \"tls\" feature …") without a request ever leaving the process, so check the status and don't assume the call happened. Enabling features = ["tls"] pulls ring (C and assembly), and deploys then need a C cross-toolchain such as zig.Cross-slice
All three Slice methods inject the X-Drift-Slice identity header, which is what the far end reads back with CallerSlice. Your own headers override it only by setting the same key.
Cross-slice calling is deliberately not under Backbone, because it is inter-slice networking, the seed of a different, hypothetical future pillar. Not to be confused with Deed.Link, which enrolls a device for one identity.
Environment
Env exists in all six SDKs and returns an empty string for a missing key. Reaching past it for the language's own accessor changes that: Python's os.environ[key] raises KeyError rather than returning "". For secrets specifically, use Secret.Get rather than either, because it resolves the value whichever way the runtime delivered it.