> 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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response> => {
        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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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="<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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response> => {
        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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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="<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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response> => {
        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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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="<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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response>) | 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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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<ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
        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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response>) | 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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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<ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
        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("<ENTITY_ID>");
            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 <ENTITY_ID> with the ID of an entity in your environment.
    entity = client.entities.get_entity(
        entity_id="<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<Response>) | 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 <ENTITY_ID> with the ID of an entity in your environment.
            entityId: "<ENTITY_ID>"
        });
        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<dyn std::error::Error>> {
    // 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 <ENTITY_ID> with the ID of an entity in your
    // environment.
    match client.entities.get_entity("<ENTITY_ID>", 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: "<ENTITY_ID>",
	})

	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<dyn std::error::Error>> {
    // 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: "<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<Channel, Box<dyn std::error::Error>> {
    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<ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
        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.