Custom gateways lets you connect any network of devices to Span. You run the gateways yourself on premises (or another place in the cloud). Whenever the gateway or devices change in Span the gateway will get a notification of the changes.

Gateway Overview

This is a blog post with deeper discussion for LoRaWAN

Connecting to the gateway endpoint

The gateways use a gRPC interface and connects to

Sample code

Connecting to the gateway endpoint is relatively straightforward; use the certificates from Span (a client certificate, a certificate chain and a private key for the client certificate) to set up the TLS credentials, then use that to connect to the endpoint as usual.

The full gateway sample (and library) for Go is available in the spangw repository at GitHub

import (
    // ....

const (
    certFile = "clientcert.crt"
    chainFile = "chain.crt"
    keyFile = "private.key"

func ConnectToSpan() (gateway.UserGatewayServiceClient, error) {
    	certs, err := os.ReadFile(chainFile)
	if err != nil {
		return nil, err
	certPool := x509.NewCertPool()

	cCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cCert},
		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
			return &cCert, nil
		RootCAs: certPool,

	cc, err := grpc.Dial("", grpc.WithTransportCredentials(creds))
	if err != nil {
		return nil, err

	gw := gateway.NewUserGatewayServiceClient(cc)
	stream, err := demoGW.ControlStream(context.Background())
	if err != nil {
		return bil, err
    return gw, nil