> This is the Markdown version of the page. For the complete Lattice Developers documentation index, fetch https://developer.anduril.com/llms.txt. Append .md to any page URL for its clean Markdown. # Connect to offline environments > Configure the Lattice SDK to connect at the edge By default, the Lattice SDK validates TLS certificates against your operating system's trusted Certificate Authority (CA) store. When your Lattice environment is offline or air-gapped, it's deployed on a local network without access to a public CA infrastructure. The environment presents a certificate signed by a private Anduril CA that isn't in your system's trust store, so your SDK client can't validate it and the connection fails. This guide describes two ways to connect: 1. **Use an Anduril-issued certificate**: This method lets the SDK trust your environment while keeping full TLS verification enabled. **This is the recommended approach** in production. 2. **Skip TLS verification with self-signed certificates**: This method lets you connect when an Anduril representative can't issue a certificate. Use this approach only in development environments. ## Before you begin * Complete the steps in [Set up](/guides/getting-started/set-up) to configure your environment variables: `LATTICE_ENDPOINT` and `ENVIRONMENT_TOKEN`. > **gRPC authentication** > > These gRPC examples authenticate with a static environment token attached as request metadata. > If you are using OAuth 2.0 client credentials instead, [set up the token refresh module](/guides/getting-started/authenticate#client-credentials) before running the examples on this page. ## Use an Anduril-issued certificate This is the recommended approach because it keeps full TLS verification enabled. You obtain a CA certificate from Anduril, add it to your trust store, and configure the SDK to trust it as a root CA. The SDK still validates the certificate chain, hostname, and expiry date. To obtain the certificate, add it to your trust store, and configure the SDK to trust it, follow these steps: #### Get the Anduril-issued certificate Contact your Anduril representative to obtain the CA certificate for your Lattice environment. The certificate is delivered as a PEM-encoded file. Because delivery mechanisms vary, follow the instructions your Anduril representative provides to retrieve it. #### Add the certificate to the system's trust store Add the certificate to your operating system's trust store so that clients on your machine trust it: **`macOS`** ```bash title="macOS" sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain /path/to/anduril-ca.pem ``` **`Linux`** ```bash title="Linux" sudo cp /path/to/anduril-ca.pem /usr/local/share/ca-certificates/anduril-ca.crt sudo update-ca-certificates ``` **`PowerShell`** ```ps title="PowerShell" Import-Certificate -FilePath "C:\path\to\anduril-ca.pem" -CertStoreLocation Cert:\LocalMachine\Root ``` #### Set an environment variable with the certificate path Set a `LATTICE_CA_CERT_PATH` environment variable that points to the certificate file. The examples in the following steps load the CA certificate from `LATTICE_CA_CERT_PATH` and configure the Lattice client to trust it as a root CA: **`Bash`** ```bash title="Bash" export LATTICE_CA_CERT_PATH=/path/to/anduril-ca.pem ``` **`PowerShell`** ```ps title="PowerShell" setx LATTICE_CA_CERT_PATH "C:\path\to\anduril-ca.pem" ``` #### Initialize the client Import your environment variables and initialize the client. `LATTICE_CA_CERT_PATH` is the file path to the PEM certificate you received from your Anduril representative. You can achieve the same result using a local `.env` file: ```go title={"Go (REST)"} startLine={16} maxLines={18} package main import ( "context" "crypto/tls" "crypto/x509" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, caCertPath, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, caCertPath string, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Load the Anduril-issued CA certificate and add it to the trust pool certPool, err := x509.SystemCertPool() if err != nil { certPool = x509.NewCertPool() } if caCertPath != "" { pemBytes, err := os.ReadFile(caCertPath) if err != nil { return nil, fmt.Errorf("reading CA certificate: %w", err) } if !certPool.AppendCertsFromPEM(pemBytes) { return nil, fmt.Errorf("failed to append CA certificate from %s", caCertPath) } } httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certPool, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={22} maxLines={18} package org.example; import java.io.FileInputStream; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating custom CA certificate configuration * for air-gapped or offline Lattice environments. */ public class CustomCaCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Path to the Anduril-issued CA certificate in PEM format String caCertPath = System.getenv("LATTICE_CA_CERT_PATH"); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, caCertPath ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, String caCertPath ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on the CA certificate setting OkHttpClient httpClient; if (caCertPath != null && !caCertPath.isEmpty()) { try { // Load the Anduril-issued CA certificate from a PEM file CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate caCert; try (FileInputStream fis = new FileInputStream(caCertPath)) { caCert = (X509Certificate) cf.generateCertificate(fis); } // Insert the certificate into a new trust store KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("anduril-ca", caCert); // Build a TrustManagerFactory backed by the trust store TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); tmf.init(keyStore); // Create an SSL context that trusts the Anduril CA SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // Create OkHttpClient that verifies against the Anduril CA httpClient = new OkHttpClient.Builder() .sslSocketFactory( sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0] ) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context from CA certificate", e); } } else { // Use the JVM's default trust store httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (REST)"} startLine={5} maxLines={8} from anduril import Lattice import os import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv('LATTICE_CA_CERT_PATH') def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, ca_cert_path: str | None = None, ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Configure httpx to verify TLS using the Anduril-issued CA certificate. # httpx accepts a certificate file path, an ssl.SSLContext, or True for # the system trust store. Passing the path trusts the Anduril CA. verify: str | bool = ca_cert_path if ca_cert_path else True http_client = httpx.Client(verify=verify) # Create and return Lattice client with configured httpx_client return Lattice( base_url=endpoint, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, ca_cert_path ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={5} maxLines={12} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; import fs from "fs"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; // Path to the Anduril-issued CA certificate in PEM format const caCertPath = process.env.LATTICE_CA_CERT_PATH; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, caCertPath: string | undefined ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure an HTTPS agent that trusts the Anduril-issued CA certificate const httpsAgent = new https.Agent({ ca: caCertPath ? fs.readFileSync(caCertPath) : undefined }); const fetchFunction = async (input: RequestInfo | URL, init?: RequestInit): Promise => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, caCertPath ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={7} maxLines={12} use anduril_lattice_sdk::prelude::*; use std::env; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format. let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client that trusts the supplied CA certificate. // When `LATTICE_CA_CERT_PATH` is set we add it to the client's root store; // otherwise we leave `reqwest_client` as `None` so the SDK builds its own // client against the system trust store. let reqwest_client = match &ca_cert_path { Some(path) => { let pem = fs::read(path).unwrap_or_else(|error| { panic!("Failed to read CA certificate at '{}': {}", path, error) }); let certificate = reqwest::Certificate::from_pem(&pem)?; println!("Trusting custom CA certificate from '{}'.", path); Some( reqwest::Client::builder() .add_root_certificate(certificate) .build()?, ) } None => { println!("LATTICE_CA_CERT_PATH not set; using the system trust store."); None } }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={20} maxLines={25} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "fmt" "log" "os" "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection using the Anduril-issued CA certificate conn, err := createGRPCConnection(latticeEndpoint, auth, caCertPath) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, caCertPath string, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials using the Anduril-issued CA certificate if caCertPath != "" { creds, err := credentials.NewClientTLSFromFile(caCertPath, "") if err != nil { return nil, fmt.Errorf("loading CA certificate: %w", err) } opts = append(opts, grpc.WithTransportCredentials(creds)) } else { // Fall back to the system certificate pool opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```py title={"Python (gRPC)"} startLine={33} maxLines={26} # This Python example is compatible with artifacts generated using the following # Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py # and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py import os import sys from typing import Optional from bearer_auth import create_client from anduril.entitymanager.v1.entity_manager_api_pub_pb import GetEntityRequest from anduril.entitymanager.v1.entity_manager_api_pub_connect import EntityManagerAPIClientSync def load_ca_cert(ca_cert_path: Optional[str]) -> Optional[bytes]: # Load the PEM-encoded, Anduril-issued CA certificate to use as the trusted # root. When no path is provided, fall back to the system certificate pool. if not ca_cert_path: return None with open(ca_cert_path, "rb") as f: return f.read() def main(): # Load environment variables environment_token = os.getenv("ENVIRONMENT_TOKEN") lattice_endpoint = os.getenv("LATTICE_ENDPOINT") sandboxes_token = os.getenv("SANDBOXES_TOKEN") # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv("LATTICE_CA_CERT_PATH") if not environment_token or not lattice_endpoint or not sandboxes_token: print("Missing required environment variables", file=sys.stderr) sys.exit(1) # Create the EntityManager API client, trusting the Anduril-issued CA certificate client = create_client( EntityManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token, ca_cert=load_ca_cert(ca_cert_path), ) print(f"Connection established: {lattice_endpoint}") # Get an entity try: request = GetEntityRequest(entity_id="") response = client.get_entity(request) if response.entity: print(f"Successfully fetched entity: {response.entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}", file=sys.stderr) if __name__ == "__main__": main() ``` ```rs title={"Rust (gRPC)"} startLine={16} maxLines={24} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use tonic::metadata::MetadataValue; use tonic::transport::{Certificate, Channel, ClientTlsConfig}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel using the Anduril-issued CA certificate let channel = create_grpc_channel(&lattice_endpoint, ca_cert_path.as_deref()).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When ca_cert_path is provided, the channel // trusts the Anduril-issued CA certificate. When it is None, the channel falls // back to the system's native root certificates. async fn create_grpc_channel( endpoint: &str, ca_cert_path: Option<&str>, ) -> Result> { let tls_config = if let Some(path) = ca_cert_path { // Load the PEM-encoded CA certificate and use it as the trusted root let pem = std::fs::read(path)?; let cert = Certificate::from_pem(pem); ClientTlsConfig::new().ca_certificate(cert) } else { // Fall back to the system certificate pool ClientTlsConfig::new().with_native_roots() }; let channel = Channel::from_shared(format!("https://{}", endpoint))? .tls_config(tls_config)? .connect() .await?; Ok(channel) } ``` #### Load the CA certificate Configure the HTTP or gRPC client to trust the Anduril-issued certificate. The client loads the certificate from the PEM file and adds it to the trust pool, so the client verifies the server's certificate against the Anduril CA during every TLS handshake: ```go title={"Go (REST)"} startLine={52} maxLines={41} package main import ( "context" "crypto/tls" "crypto/x509" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, caCertPath, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, caCertPath string, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Load the Anduril-issued CA certificate and add it to the trust pool certPool, err := x509.SystemCertPool() if err != nil { certPool = x509.NewCertPool() } if caCertPath != "" { pemBytes, err := os.ReadFile(caCertPath) if err != nil { return nil, fmt.Errorf("reading CA certificate: %w", err) } if !certPool.AppendCertsFromPEM(pemBytes) { return nil, fmt.Errorf("failed to append CA certificate from %s", caCertPath) } } httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certPool, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={49} maxLines={62} package org.example; import java.io.FileInputStream; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating custom CA certificate configuration * for air-gapped or offline Lattice environments. */ public class CustomCaCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Path to the Anduril-issued CA certificate in PEM format String caCertPath = System.getenv("LATTICE_CA_CERT_PATH"); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, caCertPath ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, String caCertPath ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on the CA certificate setting OkHttpClient httpClient; if (caCertPath != null && !caCertPath.isEmpty()) { try { // Load the Anduril-issued CA certificate from a PEM file CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate caCert; try (FileInputStream fis = new FileInputStream(caCertPath)) { caCert = (X509Certificate) cf.generateCertificate(fis); } // Insert the certificate into a new trust store KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("anduril-ca", caCert); // Build a TrustManagerFactory backed by the trust store TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); tmf.init(keyStore); // Create an SSL context that trusts the Anduril CA SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // Create OkHttpClient that verifies against the Anduril CA httpClient = new OkHttpClient.Builder() .sslSocketFactory( sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0] ) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context from CA certificate", e); } } else { // Use the JVM's default trust store httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (REST)"} startLine={13} maxLines={25} from anduril import Lattice import os import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv('LATTICE_CA_CERT_PATH') def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, ca_cert_path: str | None = None, ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Configure httpx to verify TLS using the Anduril-issued CA certificate. # httpx accepts a certificate file path, an ssl.SSLContext, or True for # the system trust store. Passing the path trusts the Anduril CA. verify: str | bool = ca_cert_path if ca_cert_path else True http_client = httpx.Client(verify=verify) # Create and return Lattice client with configured httpx_client return Lattice( base_url=endpoint, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, ca_cert_path ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={18} maxLines={36} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; import fs from "fs"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; // Path to the Anduril-issued CA certificate in PEM format const caCertPath = process.env.LATTICE_CA_CERT_PATH; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, caCertPath: string | undefined ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure an HTTPS agent that trusts the Anduril-issued CA certificate const httpsAgent = new https.Agent({ ca: caCertPath ? fs.readFileSync(caCertPath) : undefined }); const fetchFunction = async (input: RequestInfo | URL, init?: RequestInit): Promise => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, caCertPath ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={30} maxLines={36} use anduril_lattice_sdk::prelude::*; use std::env; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format. let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client that trusts the supplied CA certificate. // When `LATTICE_CA_CERT_PATH` is set we add it to the client's root store; // otherwise we leave `reqwest_client` as `None` so the SDK builds its own // client against the system trust store. let reqwest_client = match &ca_cert_path { Some(path) => { let pem = fs::read(path).unwrap_or_else(|error| { panic!("Failed to read CA certificate at '{}': {}", path, error) }); let certificate = reqwest::Certificate::from_pem(&pem)?; println!("Trusting custom CA certificate from '{}'.", path); Some( reqwest::Client::builder() .add_root_certificate(certificate) .build()?, ) } None => { println!("LATTICE_CA_CERT_PATH not set; using the system trust store."); None } }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={60} maxLines={25} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "fmt" "log" "os" "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection using the Anduril-issued CA certificate conn, err := createGRPCConnection(latticeEndpoint, auth, caCertPath) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, caCertPath string, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials using the Anduril-issued CA certificate if caCertPath != "" { creds, err := credentials.NewClientTLSFromFile(caCertPath, "") if err != nil { return nil, fmt.Errorf("loading CA certificate: %w", err) } opts = append(opts, grpc.WithTransportCredentials(creds)) } else { // Fall back to the system certificate pool opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```py title={"Python (gRPC)"} startLine={13} maxLines={19} # This Python example is compatible with artifacts generated using the following # Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py # and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py import os import sys from typing import Optional from bearer_auth import create_client from anduril.entitymanager.v1.entity_manager_api_pub_pb import GetEntityRequest from anduril.entitymanager.v1.entity_manager_api_pub_connect import EntityManagerAPIClientSync def load_ca_cert(ca_cert_path: Optional[str]) -> Optional[bytes]: # Load the PEM-encoded, Anduril-issued CA certificate to use as the trusted # root. When no path is provided, fall back to the system certificate pool. if not ca_cert_path: return None with open(ca_cert_path, "rb") as f: return f.read() def main(): # Load environment variables environment_token = os.getenv("ENVIRONMENT_TOKEN") lattice_endpoint = os.getenv("LATTICE_ENDPOINT") sandboxes_token = os.getenv("SANDBOXES_TOKEN") # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv("LATTICE_CA_CERT_PATH") if not environment_token or not lattice_endpoint or not sandboxes_token: print("Missing required environment variables", file=sys.stderr) sys.exit(1) # Create the EntityManager API client, trusting the Anduril-issued CA certificate client = create_client( EntityManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token, ca_cert=load_ca_cert(ca_cert_path), ) print(f"Connection established: {lattice_endpoint}") # Get an entity try: request = GetEntityRequest(entity_id="") response = client.get_entity(request) if response.entity: print(f"Successfully fetched entity: {response.entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}", file=sys.stderr) if __name__ == "__main__": main() ``` ```rs title={"Rust (gRPC)"} startLine={72} maxLines={22} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use tonic::metadata::MetadataValue; use tonic::transport::{Certificate, Channel, ClientTlsConfig}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel using the Anduril-issued CA certificate let channel = create_grpc_channel(&lattice_endpoint, ca_cert_path.as_deref()).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When ca_cert_path is provided, the channel // trusts the Anduril-issued CA certificate. When it is None, the channel falls // back to the system's native root certificates. async fn create_grpc_channel( endpoint: &str, ca_cert_path: Option<&str>, ) -> Result> { let tls_config = if let Some(path) = ca_cert_path { // Load the PEM-encoded CA certificate and use it as the trusted root let pem = std::fs::read(path)?; let cert = Certificate::from_pem(pem); ClientTlsConfig::new().ca_certificate(cert) } else { // Fall back to the system certificate pool ClientTlsConfig::new().with_native_roots() }; let channel = Channel::from_shared(format!("https://{}", endpoint))? .tls_config(tls_config)? .connect() .await?; Ok(channel) } ``` #### Use the client Use the client to call the Lattice API. This example uses [`GetEntity`](/reference/rest/entities/get-entity)\ to fetch an entity by its `entityId`: ```go title={"Go (REST)"} startLine={34} maxLines={16} package main import ( "context" "crypto/tls" "crypto/x509" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, caCertPath, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, caCertPath string, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Load the Anduril-issued CA certificate and add it to the trust pool certPool, err := x509.SystemCertPool() if err != nil { certPool = x509.NewCertPool() } if caCertPath != "" { pemBytes, err := os.ReadFile(caCertPath) if err != nil { return nil, fmt.Errorf("reading CA certificate: %w", err) } if !certPool.AppendCertsFromPEM(pemBytes) { return nil, fmt.Errorf("failed to append CA certificate from %s", caCertPath) } } httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certPool, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={40} maxLines={8} package org.example; import java.io.FileInputStream; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating custom CA certificate configuration * for air-gapped or offline Lattice environments. */ public class CustomCaCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Path to the Anduril-issued CA certificate in PEM format String caCertPath = System.getenv("LATTICE_CA_CERT_PATH"); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, caCertPath ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, String caCertPath ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on the CA certificate setting OkHttpClient httpClient; if (caCertPath != null && !caCertPath.isEmpty()) { try { // Load the Anduril-issued CA certificate from a PEM file CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate caCert; try (FileInputStream fis = new FileInputStream(caCertPath)) { caCert = (X509Certificate) cf.generateCertificate(fis); } // Insert the certificate into a new trust store KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("anduril-ca", caCert); // Build a TrustManagerFactory backed by the trust store TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); tmf.init(keyStore); // Create an SSL context that trusts the Anduril CA SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // Create OkHttpClient that verifies against the Anduril CA httpClient = new OkHttpClient.Builder() .sslSocketFactory( sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0] ) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context from CA certificate", e); } } else { // Use the JVM's default trust store httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (REST)"} startLine={51} maxLines={9} from anduril import Lattice import os import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv('LATTICE_CA_CERT_PATH') def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, ca_cert_path: str | None = None, ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Configure httpx to verify TLS using the Anduril-issued CA certificate. # httpx accepts a certificate file path, an ssl.SSLContext, or True for # the system trust store. Passing the path trusts the Anduril CA. verify: str | bool = ca_cert_path if ca_cert_path else True http_client = httpx.Client(verify=verify) # Create and return Lattice client with configured httpx_client return Lattice( base_url=endpoint, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, ca_cert_path ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={63} maxLines={12} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; import fs from "fs"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; // Path to the Anduril-issued CA certificate in PEM format const caCertPath = process.env.LATTICE_CA_CERT_PATH; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, caCertPath: string | undefined ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure an HTTPS agent that trusts the Anduril-issued CA certificate const httpsAgent = new https.Agent({ ca: caCertPath ? fs.readFileSync(caCertPath) : undefined }); const fetchFunction = async (input: RequestInfo | URL, init?: RequestInit): Promise => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, caCertPath ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={67} maxLines={12} use anduril_lattice_sdk::prelude::*; use std::env; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format. let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client that trusts the supplied CA certificate. // When `LATTICE_CA_CERT_PATH` is set we add it to the client's root store; // otherwise we leave `reqwest_client` as `None` so the SDK builds its own // client against the system trust store. let reqwest_client = match &ca_cert_path { Some(path) => { let pem = fs::read(path).unwrap_or_else(|error| { panic!("Failed to read CA certificate at '{}': {}", path, error) }); let certificate = reqwest::Certificate::from_pem(&pem)?; println!("Trusting custom CA certificate from '{}'.", path); Some( reqwest::Client::builder() .add_root_certificate(certificate) .build()?, ) } None => { println!("LATTICE_CA_CERT_PATH not set; using the system trust store."); None } }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={45} maxLines={14} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "fmt" "log" "os" "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Path to the Anduril-issued CA certificate in PEM format caCertPath := os.Getenv("LATTICE_CA_CERT_PATH") if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection using the Anduril-issued CA certificate conn, err := createGRPCConnection(latticeEndpoint, auth, caCertPath) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, caCertPath string, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials using the Anduril-issued CA certificate if caCertPath != "" { creds, err := credentials.NewClientTLSFromFile(caCertPath, "") if err != nil { return nil, fmt.Errorf("loading CA certificate: %w", err) } opts = append(opts, grpc.WithTransportCredentials(creds)) } else { // Fall back to the system certificate pool opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```py title={"Python (gRPC)"} startLine={55} maxLines={12} # This Python example is compatible with artifacts generated using the following # Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py # and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py import os import sys from typing import Optional from bearer_auth import create_client from anduril.entitymanager.v1.entity_manager_api_pub_pb import GetEntityRequest from anduril.entitymanager.v1.entity_manager_api_pub_connect import EntityManagerAPIClientSync def load_ca_cert(ca_cert_path: Optional[str]) -> Optional[bytes]: # Load the PEM-encoded, Anduril-issued CA certificate to use as the trusted # root. When no path is provided, fall back to the system certificate pool. if not ca_cert_path: return None with open(ca_cert_path, "rb") as f: return f.read() def main(): # Load environment variables environment_token = os.getenv("ENVIRONMENT_TOKEN") lattice_endpoint = os.getenv("LATTICE_ENDPOINT") sandboxes_token = os.getenv("SANDBOXES_TOKEN") # Path to the Anduril-issued CA certificate in PEM format ca_cert_path = os.getenv("LATTICE_CA_CERT_PATH") if not environment_token or not lattice_endpoint or not sandboxes_token: print("Missing required environment variables", file=sys.stderr) sys.exit(1) # Create the EntityManager API client, trusting the Anduril-issued CA certificate client = create_client( EntityManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token, ca_cert=load_ca_cert(ca_cert_path), ) print(f"Connection established: {lattice_endpoint}") # Get an entity try: request = GetEntityRequest(entity_id="") response = client.get_entity(request) if response.entity: print(f"Successfully fetched entity: {response.entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}", file=sys.stderr) if __name__ == "__main__": main() ``` ```rs title={"Rust (gRPC)"} startLine={54} maxLines={15} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use tonic::metadata::MetadataValue; use tonic::transport::{Certificate, Channel, ClientTlsConfig}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Path to the Anduril-issued CA certificate in PEM format let ca_cert_path = env::var("LATTICE_CA_CERT_PATH").ok(); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel using the Anduril-issued CA certificate let channel = create_grpc_channel(&lattice_endpoint, ca_cert_path.as_deref()).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When ca_cert_path is provided, the channel // trusts the Anduril-issued CA certificate. When it is None, the channel falls // back to the system's native root certificates. async fn create_grpc_channel( endpoint: &str, ca_cert_path: Option<&str>, ) -> Result> { let tls_config = if let Some(path) = ca_cert_path { // Load the PEM-encoded CA certificate and use it as the trusted root let pem = std::fs::read(path)?; let cert = Certificate::from_pem(pem); ClientTlsConfig::new().ca_certificate(cert) } else { // Fall back to the system certificate pool ClientTlsConfig::new().with_native_roots() }; let channel = Channel::from_shared(format!("https://{}", endpoint))? .tls_config(tls_config)? .connect() .await?; Ok(channel) } ``` After you add the CA certificate to your trust store and load it in your client, your integration can connect to an offline environment with full TLS verification enabled. ## Skip TLS verification with self-signed certificates If you can't obtain an Anduril-issued certificate for a development environment, you can configure the SDK to skip TLS verification. The SDK then connects without validating the server's certificate. > **Warning** > > Skipping TLS verification disables certificate validation, which exposes your connection to interception and > tampering. Use this approach only in development environments where an Anduril representative can't issue an > Anduril-issued certificate. In production, use an [Anduril-issued certificate](#use-an-anduril-issued-certificate) > instead. To skip TLS verification and configure your client, follow these steps: #### Set an environment variable Set a `SKIP_TLS_VERIFY` environment variable so the client knows to skip certificate validation. The examples in the following steps read `SKIP_TLS_VERIFY` and configure a custom Lattice client that skips TLS verification: **`Bash`** ```bash title="Bash" export SKIP_TLS_VERIFY=true ``` **`PowerShell`** ```ps title="PowerShell" setx SKIP_TLS_VERIFY true ``` #### Initialize the client Import your environment variables and initialize the client. `SKIP_TLS_VERIFY` controls whether the client validates the server's certificate. You can achieve the same result using a local `.env` file: ```go title={"Go (REST)"} startLine={14} maxLines={25} package main import ( "context" "crypto/tls" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, skipTLSVerify, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, skipTLSVerify bool, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Configure the InsecureSkipVerify option httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: skipTLSVerify, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={18} maxLines={25} package org.example; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating TLS configuration for air-gapped environments. */ public class SelfSignedCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Load the SKIP_TLS_VERIFY variable boolean skipTlsVerify = Boolean.parseBoolean(System.getenv("SKIP_TLS_VERIFY")); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, skipTlsVerify ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, boolean skipTlsVerify ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on TLS verification setting OkHttpClient httpClient; if (skipTlsVerify) { try { // Create a trust manager that accepts all certificates TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Create SSL context with trust-all manager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); // Create OkHttpClient with TLS verification disabled httpClient = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context", e); } } else { // Create OkHttpClient with default TLS verification httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (REST)"} startLine={6} maxLines={25} from anduril import Lattice import os import ssl import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Load the SKIP_TLS_VERIFY variable skip_tls_verify = os.getenv('SKIP_TLS_VERIFY', '').lower() == 'true' def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, skip_tls_verify: bool = False ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Base configuration base_url = endpoint # Configure HTTP client based on TLS verification setting if skip_tls_verify: # Create SSL context that doesn't verify certificates ssl_context = ssl.create_default_context() ssl_context.check_hostname = False ssl_context.verify_mode = ssl.CERT_NONE # Create httpx client with TLS verification disabled http_client = httpx.Client(verify=ssl_context) else: # Create httpx client with default TLS verification http_client = httpx.Client() # Create and return Lattice client with configured httpx_client return Lattice( base_url=base_url, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, skip_tls_verify ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={5} maxLines={25} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } // Load the SKIP_TLS_VERIFY variable const skipTlsVerify = process.env.SKIP_TLS_VERIFY === "true"; function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, skipTlsVerify: boolean ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure fetch function based on TLS verification setting let fetchFunction: ((input: RequestInfo | URL, init?: RequestInit) => Promise) | undefined; if (skipTlsVerify) { // Create HTTPS agent with TLS verification disabled const httpsAgent = new https.Agent({ rejectUnauthorized: false }); fetchFunction = async (input: RequestInfo | URL, init?: RequestInit) => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; } else { // Use default fetch with standard TLS verification fetchFunction = fetch; } // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, skipTlsVerify ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={7} maxLines={25} use anduril_lattice_sdk::prelude::*; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable. let skip_tls_verify = env::var("SKIP_TLS_VERIFY") .map(|v| v.to_lowercase() == "true") .unwrap_or(false); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client with certificate verification disabled when // requested; otherwise leave `reqwest_client` as `None` so the SDK builds // its own (verifying) client. let reqwest_client = if skip_tls_verify { println!( "SKIP_TLS_VERIFY=true: disabling TLS certificate verification. \ Never do this in production." ); Some( reqwest::Client::builder() .danger_accept_invalid_certs(true) .build()?, ) } else { None }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={18} maxLines={25} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "crypto/tls" "fmt" "log" "os" "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection with optional TLS verification conn, err := createGRPCConnection(latticeEndpoint, auth, skipTLSVerify) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, skipTLSVerify bool, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials based on TLS verification setting if skipTLSVerify { opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ InsecureSkipVerify: true, }))) } else { // Use default secure TLS configuration opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```rs title={"Rust (gRPC)"} startLine={16} maxLines={25} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{DigitallySignedStruct, SignatureScheme}; use std::sync::Arc; use tonic::metadata::MetadataValue; use tonic::transport::{Channel, ClientTlsConfig, Endpoint}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable let skip_tls_verify = env::var("SKIP_TLS_VERIFY").ok().as_deref() == Some("true"); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel with optional TLS verification let channel = create_grpc_channel(&lattice_endpoint, skip_tls_verify).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When skip_tls_verify is true the channel // accepts any server certificate (including self-signed ones) by installing a // rustls verifier that does nothing. This matches the Go example's // `InsecureSkipVerify: true` and MUST only be used in development. async fn create_grpc_channel( endpoint: &str, skip_tls_verify: bool, ) -> Result> { let uri: Uri = format!("https://{}", endpoint).parse()?; let tonic_endpoint = Endpoint::from(uri); if skip_tls_verify { // Install the ring-based default crypto provider so that rustls knows // which algorithms to use when building the connector. let _ = rustls::crypto::ring::default_provider().install_default(); let rustls_config = rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(Arc::new(NoCertVerifier)) .with_no_client_auth(); let mut http = HttpConnector::new(); http.enforce_http(false); let https = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(rustls_config) .https_or_http() .enable_http2() .wrap_connector(http); Ok(tonic_endpoint.connect_with_connector(https).await?) } else { // Use the default secure TLS configuration. let tls_config = ClientTlsConfig::new().with_native_roots(); Ok(tonic_endpoint.tls_config(tls_config)?.connect().await?) } } // A rustls server certificate verifier that accepts every certificate without // checking it. This is equivalent to Go's `tls.Config{InsecureSkipVerify: true}` // and should only be enabled for local development against self-signed Lattice // deployments. #[derive(Debug)] struct NoCertVerifier; impl ServerCertVerifier for NoCertVerifier { fn verify_server_cert( &self, _end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>, _ocsp_response: &[u8], _now: UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } fn verify_tls12_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn verify_tls13_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn supported_verify_schemes(&self) -> Vec { vec![ SignatureScheme::RSA_PKCS1_SHA256, SignatureScheme::RSA_PKCS1_SHA384, SignatureScheme::RSA_PKCS1_SHA512, SignatureScheme::ECDSA_NISTP256_SHA256, SignatureScheme::ECDSA_NISTP384_SHA384, SignatureScheme::ECDSA_NISTP521_SHA512, SignatureScheme::RSA_PSS_SHA256, SignatureScheme::RSA_PSS_SHA384, SignatureScheme::RSA_PSS_SHA512, SignatureScheme::ED25519, ] } } ``` #### Create a custom client Configure a custom client that skips TLS verification. In Go, this uses the `InsecureSkipVerify` option; the other languages provide an equivalent: ```go title={"Go (REST)"} startLine={52} maxLines={20} package main import ( "context" "crypto/tls" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, skipTLSVerify, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, skipTLSVerify bool, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Configure the InsecureSkipVerify option httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: skipTLSVerify, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={47} maxLines={20} package org.example; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating TLS configuration for air-gapped environments. */ public class SelfSignedCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Load the SKIP_TLS_VERIFY variable boolean skipTlsVerify = Boolean.parseBoolean(System.getenv("SKIP_TLS_VERIFY")); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, skipTlsVerify ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, boolean skipTlsVerify ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on TLS verification setting OkHttpClient httpClient; if (skipTlsVerify) { try { // Create a trust manager that accepts all certificates TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Create SSL context with trust-all manager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); // Create OkHttpClient with TLS verification disabled httpClient = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context", e); } } else { // Create OkHttpClient with default TLS verification httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (REST)"} startLine={15} maxLines={20} from anduril import Lattice import os import ssl import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Load the SKIP_TLS_VERIFY variable skip_tls_verify = os.getenv('SKIP_TLS_VERIFY', '').lower() == 'true' def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, skip_tls_verify: bool = False ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Base configuration base_url = endpoint # Configure HTTP client based on TLS verification setting if skip_tls_verify: # Create SSL context that doesn't verify certificates ssl_context = ssl.create_default_context() ssl_context.check_hostname = False ssl_context.verify_mode = ssl.CERT_NONE # Create httpx client with TLS verification disabled http_client = httpx.Client(verify=ssl_context) else: # Create httpx client with default TLS verification http_client = httpx.Client() # Create and return Lattice client with configured httpx_client return Lattice( base_url=base_url, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, skip_tls_verify ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={18} maxLines={20} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } // Load the SKIP_TLS_VERIFY variable const skipTlsVerify = process.env.SKIP_TLS_VERIFY === "true"; function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, skipTlsVerify: boolean ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure fetch function based on TLS verification setting let fetchFunction: ((input: RequestInfo | URL, init?: RequestInit) => Promise) | undefined; if (skipTlsVerify) { // Create HTTPS agent with TLS verification disabled const httpsAgent = new https.Agent({ rejectUnauthorized: false }); fetchFunction = async (input: RequestInfo | URL, init?: RequestInit) => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; } else { // Use default fetch with standard TLS verification fetchFunction = fetch; } // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, skipTlsVerify ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={32} maxLines={20} use anduril_lattice_sdk::prelude::*; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable. let skip_tls_verify = env::var("SKIP_TLS_VERIFY") .map(|v| v.to_lowercase() == "true") .unwrap_or(false); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client with certificate verification disabled when // requested; otherwise leave `reqwest_client` as `None` so the SDK builds // its own (verifying) client. let reqwest_client = if skip_tls_verify { println!( "SKIP_TLS_VERIFY=true: disabling TLS certificate verification. \ Never do this in production." ); Some( reqwest::Client::builder() .danger_accept_invalid_certs(true) .build()?, ) } else { None }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={63} maxLines={20} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "crypto/tls" "fmt" "log" "os" "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection with optional TLS verification conn, err := createGRPCConnection(latticeEndpoint, auth, skipTLSVerify) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, skipTLSVerify bool, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials based on TLS verification setting if skipTLSVerify { opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ InsecureSkipVerify: true, }))) } else { // Use default secure TLS configuration opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```rs title={"Rust (gRPC)"} startLine={80} maxLines={20} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{DigitallySignedStruct, SignatureScheme}; use std::sync::Arc; use tonic::metadata::MetadataValue; use tonic::transport::{Channel, ClientTlsConfig, Endpoint}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable let skip_tls_verify = env::var("SKIP_TLS_VERIFY").ok().as_deref() == Some("true"); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel with optional TLS verification let channel = create_grpc_channel(&lattice_endpoint, skip_tls_verify).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When skip_tls_verify is true the channel // accepts any server certificate (including self-signed ones) by installing a // rustls verifier that does nothing. This matches the Go example's // `InsecureSkipVerify: true` and MUST only be used in development. async fn create_grpc_channel( endpoint: &str, skip_tls_verify: bool, ) -> Result> { let uri: Uri = format!("https://{}", endpoint).parse()?; let tonic_endpoint = Endpoint::from(uri); if skip_tls_verify { // Install the ring-based default crypto provider so that rustls knows // which algorithms to use when building the connector. let _ = rustls::crypto::ring::default_provider().install_default(); let rustls_config = rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(Arc::new(NoCertVerifier)) .with_no_client_auth(); let mut http = HttpConnector::new(); http.enforce_http(false); let https = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(rustls_config) .https_or_http() .enable_http2() .wrap_connector(http); Ok(tonic_endpoint.connect_with_connector(https).await?) } else { // Use the default secure TLS configuration. let tls_config = ClientTlsConfig::new().with_native_roots(); Ok(tonic_endpoint.tls_config(tls_config)?.connect().await?) } } // A rustls server certificate verifier that accepts every certificate without // checking it. This is equivalent to Go's `tls.Config{InsecureSkipVerify: true}` // and should only be enabled for local development against self-signed Lattice // deployments. #[derive(Debug)] struct NoCertVerifier; impl ServerCertVerifier for NoCertVerifier { fn verify_server_cert( &self, _end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>, _ocsp_response: &[u8], _now: UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } fn verify_tls12_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn verify_tls13_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn supported_verify_schemes(&self) -> Vec { vec![ SignatureScheme::RSA_PKCS1_SHA256, SignatureScheme::RSA_PKCS1_SHA384, SignatureScheme::RSA_PKCS1_SHA512, SignatureScheme::ECDSA_NISTP256_SHA256, SignatureScheme::ECDSA_NISTP384_SHA384, SignatureScheme::ECDSA_NISTP521_SHA512, SignatureScheme::RSA_PSS_SHA256, SignatureScheme::RSA_PSS_SHA384, SignatureScheme::RSA_PSS_SHA512, SignatureScheme::ED25519, ] } } ``` #### Use the client Use the client to call the Lattice API. This example uses [`GetEntity`](/reference/rest/entities/get-entity) to fetch an entity by its `entityId`: ```go title={"Go (REST)"} startLine={33} maxLines={20} package main import ( "context" "crypto/tls" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, skipTLSVerify, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, skipTLSVerify bool, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Configure the InsecureSkipVerify option httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: skipTLSVerify, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ```java title={"Java (REST)"} startLine={36} maxLines={20} package org.example; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import com.anduril.Lattice; import okhttp3.OkHttpClient; /** * Example application demonstrating TLS configuration for air-gapped environments. */ public class SelfSignedCertificate { public static void main(String[] args) { // Configuration String endpoint = System.getenv("LATTICE_ENDPOINT"); String environmentToken = System.getenv("ENVIRONMENT_TOKEN"); String sandboxesToken = System.getenv("SANDBOXES_TOKEN"); // Load the SKIP_TLS_VERIFY variable boolean skipTlsVerify = Boolean.parseBoolean(System.getenv("SKIP_TLS_VERIFY")); // Create Lattice client Lattice client = createLatticeClient( endpoint, environmentToken, sandboxesToken, skipTlsVerify ); System.out.println("Lattice client initialized: https://" + endpoint); // Use the client try { com.anduril.types.Entity entity = client.entities().getEntity(""); System.out.println("Successfully fetched entity: " + entity.getEntityId().get()); } catch (Exception e) { System.err.println("Error fetching entity: " + e.getMessage()); } } private static Lattice createLatticeClient( String endpoint, String environmentToken, String sandboxesToken, boolean skipTlsVerify ) { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } // Configure HTTP client based on TLS verification setting OkHttpClient httpClient; if (skipTlsVerify) { try { // Create a trust manager that accepts all certificates TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Create SSL context with trust-all manager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); // Create OkHttpClient with TLS verification disabled httpClient = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) .build(); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context", e); } } else { // Create OkHttpClient with default TLS verification httpClient = new OkHttpClient.Builder() .build(); } // Create and return Lattice client with configured httpClient return Lattice.builder() .url(endpoint) .token(environmentToken) .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken) .httpClient(httpClient) .build(); } } ``` ```py title={"Python (gRPC)"} startLine={52} maxLines={16} from anduril import Lattice import os import ssl import httpx # Configuration lattice_endpoint = os.getenv('LATTICE_ENDPOINT') environment_token = os.getenv('ENVIRONMENT_TOKEN') sandboxes_token = os.getenv('SANDBOXES_TOKEN') # Load the SKIP_TLS_VERIFY variable skip_tls_verify = os.getenv('SKIP_TLS_VERIFY', '').lower() == 'true' def create_lattice_client( endpoint: str, environment_token: str, sandboxes_token: str, skip_tls_verify: bool = False ) -> Lattice: if not endpoint.startswith("https://"): endpoint = "https://" + endpoint # Base configuration base_url = endpoint # Configure HTTP client based on TLS verification setting if skip_tls_verify: # Create SSL context that doesn't verify certificates ssl_context = ssl.create_default_context() ssl_context.check_hostname = False ssl_context.verify_mode = ssl.CERT_NONE # Create httpx client with TLS verification disabled http_client = httpx.Client(verify=ssl_context) else: # Create httpx client with default TLS verification http_client = httpx.Client() # Create and return Lattice client with configured httpx_client return Lattice( base_url=base_url, token=lambda: str(environment_token), httpx_client=http_client, headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" } ) if not lattice_endpoint or not environment_token or not sandboxes_token: print("Missing required environment variables.") raise SystemExit(1) # Create Lattice client client = create_lattice_client( lattice_endpoint, environment_token, sandboxes_token, skip_tls_verify ) print(f"Lattice client initialized: {lattice_endpoint}") # Use the client try: # Replace with the ID of an entity in your environment. entity = client.entities.get_entity( entity_id="" ) print(f"Successfully fetched entity: {entity.entity_id}") except Exception as error: print(f"Error fetching entity: {error}") ``` ```ts title={"TypeScript (REST)"} startLine={70} maxLines={20} import { LatticeClient } from "@anduril-industries/lattice-sdk"; import https from "https"; const latticeEndpoint = process.env.LATTICE_ENDPOINT; const environmentToken = process.env.ENVIRONMENT_TOKEN; // Remove sandboxesToken from the following statement if you are not developing on Sandboxes. const sandboxesToken = process.env.SANDBOXES_TOKEN; if (!latticeEndpoint || !environmentToken || !sandboxesToken) { console.log('Missing required environment variables.'); process.exit(1); } // Load the SKIP_TLS_VERIFY variable const skipTlsVerify = process.env.SKIP_TLS_VERIFY === "true"; function createLatticeClient( endpoint: string, environmentToken: string, sandboxesToken: string, skipTlsVerify: boolean ): LatticeClient { if (!endpoint.startsWith("https://")) { endpoint = "https://" + endpoint; } const baseUrl = endpoint; // Configure fetch function based on TLS verification setting let fetchFunction: ((input: RequestInfo | URL, init?: RequestInit) => Promise) | undefined; if (skipTlsVerify) { // Create HTTPS agent with TLS verification disabled const httpsAgent = new https.Agent({ rejectUnauthorized: false }); fetchFunction = async (input: RequestInfo | URL, init?: RequestInit) => { const response = await fetch(input, { ...init, agent: httpsAgent } as RequestInit); return response; }; } else { // Use default fetch with standard TLS verification fetchFunction = fetch; } // Create and return Lattice client with configured fetch function return new LatticeClient({ baseUrl: baseUrl, token: environmentToken, fetch: fetchFunction, // Remove the following if you are not developing on Sandboxes. headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` } }); } // Create Lattice client const client = createLatticeClient( latticeEndpoint, environmentToken, sandboxesToken, skipTlsVerify ); console.log(`Lattice client initialized: ${latticeEndpoint}`); // Use the client (async () => { try { const entity = await client.entities.getEntity({ // Replace with the ID of an entity in your environment. entityId: "" }); console.log(`Successfully fetched entity: ${entity.entityId}`); } catch (error) { console.log(`Error fetching entity: ${error}`); } })(); ``` ```rs title={"Rust (REST)"} startLine={63} maxLines={20} use anduril_lattice_sdk::prelude::*; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Configuration let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable. let skip_tls_verify = env::var("SKIP_TLS_VERIFY") .map(|v| v.to_lowercase() == "true") .unwrap_or(false); if lattice_endpoint.is_empty() || environment_token.is_empty() || sandboxes_token.is_empty() { eprintln!("Missing required environment variables."); std::process::exit(1); } // Ensure the base URL carries the https:// scheme. let base_url = if lattice_endpoint.starts_with("https://") { lattice_endpoint.clone() } else { format!("https://{}", lattice_endpoint) }; // Build a custom reqwest client with certificate verification disabled when // requested; otherwise leave `reqwest_client` as `None` so the SDK builds // its own (verifying) client. let reqwest_client = if skip_tls_verify { println!( "SKIP_TLS_VERIFY=true: disabling TLS certificate verification. \ Never do this in production." ); Some( reqwest::Client::builder() .danger_accept_invalid_certs(true) .build()?, ) } else { None }; let config = ClientConfig { base_url, token: Some(environment_token), custom_headers: HashMap::from([( "anduril-sandbox-authorization".to_string(), format!("Bearer {}", sandboxes_token), )]), reqwest_client, ..Default::default() }; let client = Lattice::new(config)?; println!("Lattice client initialized: {}", lattice_endpoint); // Use the client. Replace with the ID of an entity in your // environment. match client.entities.get_entity("", None).await { Ok(entity) => println!("Successfully fetched entity: {:?}", entity.entity_id), Err(error) => println!("Error fetching entity: {}", error), } Ok(()) } ``` ```go title={"Go (gRPC)"} startLine={45} maxLines={20} // This Go example is compatible with artifacts generated using // the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go package main import ( "context" "crypto/tls" "fmt" "log" "os" "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc" entitymanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ctx := context.Background() // Configuration environmentToken := os.Getenv("ENVIRONMENT_TOKEN") latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" { log.Fatalf("Missing required environment variables") } auth := &BearerTokenAuth{ Token: environmentToken, SandboxesToken: sandboxesToken, } // Create gRPC connection with optional TLS verification conn, err := createGRPCConnection(latticeEndpoint, auth, skipTLSVerify) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() fmt.Printf("gRPC connection established: %s\n", latticeEndpoint) client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn) // Get an entity entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{ EntityId: "", }) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Successfully fetched entity: %s\n", entity.Entity.EntityId) } } func createGRPCConnection( endpoint string, auth *BearerTokenAuth, skipTLSVerify bool, ) (*grpc.ClientConn, error) { // Base dial options opts := []grpc.DialOption{ grpc.WithPerRPCCredentials(auth), } // Configure transport credentials based on TLS verification setting if skipTLSVerify { opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ InsecureSkipVerify: true, }))) } else { // Use default secure TLS configuration opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Establish connection return grpc.NewClient(endpoint, opts...) } ``` ```rs title={"Rust (gRPC)"} startLine={56} maxLines={20} // This Rust example is compatible with artifacts generated using // the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic mod bearer_auth; use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::GetEntityRequest; use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient; use bearer_auth::EnvironmentTokenAuth; use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{DigitallySignedStruct, SignatureScheme}; use std::sync::Arc; use tonic::metadata::MetadataValue; use tonic::transport::{Channel, ClientTlsConfig, Endpoint}; use tonic::Request; use std::env; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let environment_token = env::var("ENVIRONMENT_TOKEN") .expect("ENVIRONMENT_TOKEN environment variable not set"); let lattice_endpoint = env::var("LATTICE_ENDPOINT") .expect("LATTICE_ENDPOINT environment variable not set"); let sandboxes_token = env::var("SANDBOXES_TOKEN") .expect("SANDBOXES_TOKEN environment variable not set"); // Load the SKIP_TLS_VERIFY variable let skip_tls_verify = env::var("SKIP_TLS_VERIFY").ok().as_deref() == Some("true"); // Set up authentication let auth = EnvironmentTokenAuth::new( environment_token, sandboxes_token, ); // Create gRPC channel with optional TLS verification let channel = create_grpc_channel(&lattice_endpoint, skip_tls_verify).await?; println!("gRPC connection established: {}", lattice_endpoint); // Fetch a fresh access token and build metadata values let access_token = auth.get_token().await?; let sandboxes_token = auth.sandboxes_token(); let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?; let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?; let mut client = EntityManagerApiClient::with_interceptor( channel, move |mut req: Request<()>| { req.metadata_mut() .insert("authorization", auth_header.clone()); req.metadata_mut() .insert("anduril-sandbox-authorization", sandbox_header.clone()); Ok(req) }, ); // Get an entity let request = Request::new(GetEntityRequest { entity_id: "".to_string(), }); match client.get_entity(request).await { Ok(resp) => { if let Some(entity) = resp.into_inner().entity { println!("Successfully fetched entity: {}", entity.entity_id); } } Err(e) => println!("Error fetching entity: {}", e), } Ok(()) } // Build a TLS-enabled gRPC channel. When skip_tls_verify is true the channel // accepts any server certificate (including self-signed ones) by installing a // rustls verifier that does nothing. This matches the Go example's // `InsecureSkipVerify: true` and MUST only be used in development. async fn create_grpc_channel( endpoint: &str, skip_tls_verify: bool, ) -> Result> { let uri: Uri = format!("https://{}", endpoint).parse()?; let tonic_endpoint = Endpoint::from(uri); if skip_tls_verify { // Install the ring-based default crypto provider so that rustls knows // which algorithms to use when building the connector. let _ = rustls::crypto::ring::default_provider().install_default(); let rustls_config = rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(Arc::new(NoCertVerifier)) .with_no_client_auth(); let mut http = HttpConnector::new(); http.enforce_http(false); let https = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(rustls_config) .https_or_http() .enable_http2() .wrap_connector(http); Ok(tonic_endpoint.connect_with_connector(https).await?) } else { // Use the default secure TLS configuration. let tls_config = ClientTlsConfig::new().with_native_roots(); Ok(tonic_endpoint.tls_config(tls_config)?.connect().await?) } } // A rustls server certificate verifier that accepts every certificate without // checking it. This is equivalent to Go's `tls.Config{InsecureSkipVerify: true}` // and should only be enabled for local development against self-signed Lattice // deployments. #[derive(Debug)] struct NoCertVerifier; impl ServerCertVerifier for NoCertVerifier { fn verify_server_cert( &self, _end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>, _ocsp_response: &[u8], _now: UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } fn verify_tls12_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn verify_tls13_signature( &self, _message: &[u8], _cert: &CertificateDer<'_>, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn supported_verify_schemes(&self) -> Vec { vec![ SignatureScheme::RSA_PKCS1_SHA256, SignatureScheme::RSA_PKCS1_SHA384, SignatureScheme::RSA_PKCS1_SHA512, SignatureScheme::ECDSA_NISTP256_SHA256, SignatureScheme::ECDSA_NISTP384_SHA384, SignatureScheme::ECDSA_NISTP521_SHA512, SignatureScheme::RSA_PSS_SHA256, SignatureScheme::RSA_PSS_SHA384, SignatureScheme::RSA_PSS_SHA512, SignatureScheme::ED25519, ] } } ``` ```go title={"Go (REST)"} startLine={34} maxLines={20} package main import ( "context" "crypto/tls" "fmt" "net/http" "os" Lattice "github.com/anduril/lattice-sdk-go/v5" "github.com/anduril/lattice-sdk-go/v5/client" "github.com/anduril/lattice-sdk-go/v5/option" ) func main() { latticeEndpoint := os.Getenv("LATTICE_ENDPOINT") environmentToken := os.Getenv("ENVIRONMENT_TOKEN") sandboxesToken := os.Getenv("SANDBOXES_TOKEN") // Load the SKIP_TLS_VERIFY variable skipTLSVerify := os.Getenv("SKIP_TLS_VERIFY") == "true" // Initialize the client LatticeClient, err := createLatticeClient(latticeEndpoint, environmentToken, sandboxesToken, skipTLSVerify, ) if err != nil { fmt.Printf("Error creating Lattice client: %v\n", err) os.Exit(1) } // Use the client ctx := context.Background() entity, err := LatticeClient.Entities.GetEntity( ctx, &Lattice.GetEntityRequest{ EntityID: "Demo-Sim-Asset1", }, ) if err != nil { fmt.Printf("Error fetching entity: %v\n", err) } else { fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName()) fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(), *entity.GetLocation().GetPosition().GetLongitudeDegrees()) } } func createLatticeClient(endpoint string, environmentToken string, sandboxesToken string, skipTLSVerify bool, ) (*client.Client, error) { headers := http.Header{} if sandboxesToken != "" { headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken)) } // Configure the InsecureSkipVerify option httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: skipTLSVerify, }, }, } latticeClient := client.NewClient( option.WithToken(environmentToken), option.WithBaseURL(fmt.Sprintf("https://%s", endpoint)), option.WithHTTPHeader(headers), option.WithHTTPClient(httpClient), ) return latticeClient, nil } ``` ## What's next * Learn how to [publish entities](/guides/entities/publish) to Lattice. * Explore [tasking](/guides/tasks/overview) to learn how you can task an agent in Lattice. * Learn more about the [Lattice OAuth 2.0](/reference/rest/oauth/get-token) endpoint. > Configure the Lattice SDK to connect at the edge