For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Logo
ContactLearn More
GuidesReferenceSamplesLicenseChangelog
GuidesReferenceSamplesLicenseChangelog
  • Concepts
    • Overview
    • Principles
  • Getting started
    • Set up
    • Authenticate
    • Quickstart
  • Best practices
    • Choose a protocol
    • Connect offline
    • Retry connections
  • Developer tools
    • Sandboxes
  • Entities
    • Overview
    • Watch
    • Publish
  • Tasks
    • Overview
    • Operate
    • Listen
    • Update
  • Objects
    • Overview
    • Upload
    • Download
    • Manage
ContactLearn More
On this page
  • Before you begin
  • Get the Lattice endpoint
  • Get your credentials
  • Client credentials
  • Using REST
  • Using gRPC
  • Environment token
  • Using REST
  • Using gRPC
  • What’s next?
Getting started

Authenticate

Configuring authentication for the Lattice SDK
Previous

Quickstart

Publishing a surface vessel to Lattice
Next

Lattice supports two authentication methods: OAuth 2.0 client credentials using short-lived access token, environment token authentication, using long-lived, static access tokens.

Client credentials
Environment token

OAuth 2.0 client credentials exchange a client ID and secret for a short-lived token. REST SDKs manage the client credential lifecycle automatically, while gRPC integrations require that you refresh the token:

The following steps show how to authenticate using both client credentials and long-lived environment tokens with both REST and gRPC SDKs.

Before you begin

  • Set up the Lattice SDK.
  • If you are using Lattice Sandboxes, see the Sandboxes set up guide. If you are using a Lattice deployment other than Sandboxes, obtain your Lattice endpoint and access credentials from your Anduril representative.

Get the Lattice endpoint

To store the Lattice endpoint as a system environment variable, do the following:

1

Add a system variable for your Lattice environment endpoint (hostname only):

$export LATTICE_ENDPOINT=<YOUR_LATTICE_ENDPOINT>
2

To verify, run the following command to print the system environment variables to your terminal:

$echo $LATTICE_ENDPOINT
$<environment_id>.env.sandboxes.developer.anduril.com

Get your credentials

To store your credentials as system environment variables, do the following:

1

Client credentials

If you’re using client credentials:

$export LATTICE_CLIENT_ID=<YOUR_LATTICE_CLIENT_ID>
$export LATTICE_CLIENT_SECRET=<YOUR_LATTICE_CLIENT_SECRET>
2

Environment token

If you’re using a long-lived environment token:

$export ENVIRONMENT_TOKEN=<YOUR_ENVIRONMENT_TOKEN>
3

Sandboxes token

If you’re connecting to Sandboxes:

$export SANDBOXES_TOKEN=<YOUR_SANDBOXES_TOKEN>

This is required for Sandboxes whether you use client credentials, or an environment token. If you do not have a Sandboxes token, create a new token.

Client credentials

OAuth 2.0 client credentials is the recommended authentication method for production integrations. When you use OAuth, Lattice exchanges your credentials for a short-lived access token using the Lattice OAuth REST endpoint:

Using REST

The REST SDK handles fetching a new access token automatically. Pass your client ID and secret when you initialize the client:

1package main
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "os"
8 "time"
9
10 Lattice "github.com/anduril/lattice-sdk-go/v4"
11 "github.com/anduril/lattice-sdk-go/v4/client"
12 "github.com/anduril/lattice-sdk-go/v4/option"
13)
14
15func main() {
16 // Get environment variables
17 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
18 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
19 clientId := os.Getenv("LATTICE_CLIENT_ID")
20
21 // Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
22 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
23
24 // Check required environment variables
25 if latticeEndpoint == "" || clientId == "" || clientSecret == "" || sandboxesToken == "" {
26 fmt.Println("Missing required environment variables")
27 os.Exit(1)
28 }
29
30 // Initialize headers for sandbox authorization
31 headers := http.Header{}
32 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
33
34 // Create the client
35 LatticeClient := client.NewClient(
36 option.WithClientCredentials(clientId, clientSecret),
37 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
38 option.WithHTTPHeader(headers),
39 )
40
41 // Continuously get the entity
42 for {
43 // Create context for the request
44 ctx := context.Background()
45
46 entity, err := LatticeClient.Entities.GetEntity(
47 ctx,
48 &Lattice.GetEntityRequest{
49 EntityID: "<ENTITY_ID>",
50 },
51 )
52
53 if err != nil {
54 fmt.Printf("Error fetching entity: %v\n", err)
55 } else {
56 fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName())
57 fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(),
58 *entity.GetLocation().GetPosition().GetLongitudeDegrees())
59 }
60
61 // Wait before next request
62 time.Sleep(5 * time.Second)
63 }
64}

Using gRPC

Since gRPC does not provide built-in OAuth token management, implement a ClientCredentialsAuth module that fetches an access token, caches it, and refreshes it before it expires:

1// This Go example is compatible with artifacts generated using
2// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
3package main
4
5import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "io"
10 "net/http"
11 "net/url"
12 "strings"
13 "time"
14)
15
16type ClientCredentialsAuth struct {
17 ClientID string
18 ClientSecret string
19 SandboxesToken string
20 Endpoint string
21}
22
23type TokenResponse struct {
24 AccessToken string `json:"access_token"`
25 ExpiresIn int `json:"expires_in"`
26 TokenType string `json:"token_type"`
27}
28
29var (
30 tokenCache TokenResponse
31 tokenExpiryTime time.Time
32)
33
34func GetToken(credentials *ClientCredentialsAuth) (*TokenResponse, error) {
35 // If the token is initialized and doesn't expire within the next 5 minutes, return it.
36 if tokenCache.AccessToken != "" && time.Now().Add(time.Minute*5).Before(tokenExpiryTime) {
37 return &tokenCache, nil
38 }
39
40 // Otherwise, refresh the token
41
42 formData := url.Values{}
43 formData.Add("grant_type", "client_credentials")
44 formData.Add("client_id", credentials.ClientID)
45 formData.Add("client_secret", credentials.ClientSecret)
46
47 req, err := http.NewRequest("POST", credentials.Endpoint, strings.NewReader(formData.Encode()))
48 if err != nil {
49 return nil, fmt.Errorf("failed to create token request: %w", err)
50 }
51 req.Header.Add("Anduril-Sandbox-Authorization", "Bearer "+credentials.SandboxesToken)
52 req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
53
54 client := &http.Client{
55 Timeout: 10 * time.Second, // Set a timeout for the HTTP request
56 }
57
58 resp, err := client.Do(req)
59 if err != nil {
60 return nil, fmt.Errorf("failed to make token request: %w", err)
61 }
62 defer resp.Body.Close()
63
64 if resp.StatusCode != http.StatusOK {
65 bodyBytes, _ := io.ReadAll(resp.Body)
66 return nil, fmt.Errorf("token request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
67 }
68
69 var tokenResp TokenResponse
70 if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
71 return nil, fmt.Errorf("failed to decode token response: %w", err)
72 }
73
74 // Cache the token and set its expiry time
75 tokenCache = tokenResp
76 tokenExpiryTime = time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)
77
78 return &tokenCache, nil
79}
80
81func (a *ClientCredentialsAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
82 _, err := GetToken(a)
83 if err != nil {
84 fmt.Printf("Token refresh failed: %v\n", err)
85 return nil, err
86 }
87 headers := map[string]string{
88 "authorization": "Bearer " + tokenCache.AccessToken,
89 "anduril-sandbox-authorization": "Bearer " + a.SandboxesToken,
90 }
91 return headers, nil
92}
93
94func (b *ClientCredentialsAuth) RequireTransportSecurity() bool {
95 return true
96}

Then, use this module to handle fetching new tokens when you interact with Lattice:

1// This Go example is compatible with artifacts generated using
2// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
3package main
4
5import (
6 "context"
7 "fmt"
8 "log"
9 "os"
10 "time"
11
12 "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc"
13 entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1"
14 "google.golang.org/grpc"
15 "google.golang.org/grpc/credentials"
16)
17
18func main() {
19 ctx := context.Background()
20
21 clientID := os.Getenv("LATTICE_CLIENT_ID")
22 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
23 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
24 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
25
26 if latticeEndpoint == "" || clientSecret == "" || clientID == "" || sandboxesToken == "" {
27 log.Fatalf("Missing required environment variables")
28 }
29 auth := &ClientCredentialsAuth{
30 ClientID: clientID,
31 ClientSecret: clientSecret,
32 SandboxesToken: sandboxesToken,
33 Endpoint: fmt.Sprintf("https://%s/api/v1/oauth/token", latticeEndpoint),
34 }
35
36 opts := []grpc.DialOption{
37 grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
38 grpc.WithPerRPCCredentials(auth),
39 }
40 conn, err := grpc.NewClient(latticeEndpoint, opts...)
41
42 if err != nil {
43 log.Fatalf("Did not connect: %v", err)
44 }
45 defer conn.Close()
46
47 client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn)
48
49 // Periodically get the latest entity state
50 for {
51 entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{
52 EntityId: "Demo-Sim-Asset1",
53 })
54 if err != nil {
55 log.Fatalf("Error getting entity: %v", err)
56 }
57
58 // Process each entity.
59 log.Printf("Fetching entity at location: %f %f", entity.GetEntity().GetLocation().GetPosition().LatitudeDegrees, entity.GetEntity().GetLocation().GetPosition().LongitudeDegrees)
60
61 time.Sleep(5 * time.Second)
62 }
63}

These examples refresh the access token before it expires (with a buffer of approximately five minutes). Each gRPC call invokes the helper function, which checks the cache and refreshes the token if needed.

Environment token

Bearer token authentication uses a long-lived, static token to authenticate requests. Use this method when your deployment provides a static access token rather than OAuth 2.0 client credentials.

Using REST

Pass the token directly to the client constructor using the token parameter:

1package main
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "os"
8 "time"
9
10 Lattice "github.com/anduril/lattice-sdk-go/v4"
11 "github.com/anduril/lattice-sdk-go/v4/client"
12 "github.com/anduril/lattice-sdk-go/v4/option"
13)
14
15func main() {
16 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
17 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
18 // Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
19 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
20
21 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
22 fmt.Println("Missing required environment variables")
23 os.Exit(1)
24 }
25
26 headers := http.Header{}
27 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
28
29 LatticeClient := client.NewClient(
30 option.WithToken(environmentToken),
31 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
32 option.WithHTTPHeader(headers),
33 )
34
35 for {
36 ctx := context.Background()
37 entity, err := LatticeClient.Entities.GetEntity(ctx, &Lattice.GetEntityRequest{EntityID: "<ENTITY_ID>"})
38 if err != nil {
39 fmt.Printf("Error fetching entity: %v\n", err)
40 } else {
41 fmt.Printf("Asset name | %s\n", *entity.GetAliases().GetName())
42 fmt.Printf("Asset location | %f, %f\n", *entity.GetLocation().GetPosition().GetLatitudeDegrees(),
43 *entity.GetLocation().GetPosition().GetLongitudeDegrees())
44 }
45 time.Sleep(5 * time.Second)
46 }
47}

Using gRPC

For gRPC, attach the bearer token as metadata on every outgoing request. Most gRPC client libraries provide a credentials or interceptor mechanism for this purpose:

1// This Go example is compatible with artifacts generated using
2// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
3package main
4
5import (
6 "context"
7 "log"
8 "os"
9 "time"
10
11 "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc"
12 entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1"
13 "google.golang.org/grpc"
14 "google.golang.org/grpc/credentials"
15)
16
17type BearerTokenAuth struct {
18 Token string
19 SandboxesToken string
20}
21
22func (b *BearerTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
23 return map[string]string{
24 "authorization": "Bearer " + b.Token,
25 "anduril-sandbox-authorization": "Bearer " + b.SandboxesToken,
26 }, nil
27}
28
29func (b *BearerTokenAuth) RequireTransportSecurity() bool {
30 return true
31}
32
33func main() {
34 ctx := context.Background()
35
36 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
37 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
38 // Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
39 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
40
41 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
42 log.Fatalf("Missing required environment variables")
43 }
44 auth := &BearerTokenAuth{
45 Token: environmentToken,
46 SandboxesToken: sandboxesToken,
47 }
48
49 opts := []grpc.DialOption{
50 grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
51 grpc.WithPerRPCCredentials(auth),
52 }
53 conn, err := grpc.NewClient(latticeEndpoint, opts...)
54
55 if err != nil {
56 log.Fatalf("Did not connect: %v", err)
57 }
58 defer conn.Close()
59
60 client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn)
61
62 // Periodically get the latest entity state
63 for {
64 entity, err := client.GetEntity(ctx, &entitymanagerv1.GetEntityRequest{
65 EntityId: "Demo-Sim-Asset1",
66 })
67 if err != nil {
68 log.Fatalf("Error getting entity: %v", err)
69 }
70
71 // Process each entity.
72 log.Printf("Fetching entity at location: %f %f", entity.GetEntity().GetLocation().GetPosition().LatitudeDegrees, entity.GetEntity().GetLocation().GetPosition().LongitudeDegrees)
73
74 time.Sleep(5 * time.Second)
75 }
76}

What’s next?

  • See Connect to offline environments to configure self-signed certificates.
  • Learn how to publish entities to Lattice.
  • Explore tasking to learn how to task an agent in Lattice.