> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticating to an Instance Endpoint

> Declare an ingress at creation time and call it with a bearer token or a client certificate.

Instance endpoints are authenticated by default. Reaching one is not a matter of fetching a URL: an HTTP ingress expects a bearer token in a Namespace-specific header, and a TCP ingress expects a client certificate.

Declaring `ingresses` on the create request targets a port the *instance* listens on, which is why it works on macOS where container `exportPorts` does not.

<Info>
  `ingresses` on `CreateInstanceRequest` is not exposed by the TypeScript SDK at version 1.0.0, which only carries the `CreateIngress` form, so this step is shown in Go only.
</Info>

## HTTP ingress

<Steps titleSize="h3">
  <Step title="Declare an HTTP ingress">
    `mode: HTTP` makes Namespace terminate TLS and forward cleartext HTTP to the port your workload listens on.

    <CodeGroup>
      ```go Go theme={null}
      resp, err := computeClient.Compute.CreateInstance(ctx, &computepb.CreateInstanceRequest{
      	Shape: &computepb.InstanceShape{
      		VirtualCpu:      6,
      		MemoryMegabytes: 14 * 1024,
      		Os:              "macos",
      		MachineArch:     "arm64",
      	},
      	DocumentedPurpose: "macoshttps example",
      	Deadline:          timestamppb.New(time.Now().Add(30 * time.Minute)),
      	Applications: []*computepb.ApplicationRequest{{
      		Name:     "httpserver",
      		ImageRef: imageRef,
      		Command:  "./entrypoint",
      	}},
      	Ingresses: []*computepb.Ingress{{
      		Name: "web",
      		Mode: computepb.Ingress_HTTP,
      		Port: 8080,
      	}},
      })
      ```
    </CodeGroup>
  </Step>

  <Step title="Read the allocated hostname">
    After the instance is ready, `ListIngresses` returns the allocated ingresses with their fully qualified names.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const ingresses = await computeClient.compute.listIngresses({ instanceId });

      for (const ingress of ingresses.allocatedIngresses) {
        if (ingress.name === "web") {
          console.log(`https://${ingress.fqdn}`);
        }
      }
      ```

      ```go Go theme={null}
      ingresses, err := computeClient.Compute.ListIngresses(ctx, &computepb.ListIngressesRequest{
      	InstanceId: resp.Metadata.InstanceId,
      })
      if err != nil {
      	return "", err
      }

      for _, ingress := range ingresses.GetAllocatedIngresses() {
      	if ingress.GetName() == "web" {
      		endpoint := "https://" + ingress.GetFqdn()
      		fmt.Fprintf(debugLog, "[namespace] HTTPS endpoint: %s\n", endpoint)
      		return endpoint, nil
      	}
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Call it with a bearer token">
    The token goes in `x-nsc-ingress-auth`, not in `Authorization`. This is the single most common mistake when calling an instance endpoint: an `Authorization` header is ignored and the request is rejected as unauthenticated.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const ingressToken = await tokenSource.issueToken(5 * 60 * 1000);

      const res = await fetch(endpoint, {
        headers: { "x-nsc-ingress-auth": `Bearer ${ingressToken}` },
      });

      if (!res.ok) {
        throw new Error(`get ${endpoint}: ${res.status}`);
      }

      console.log(await res.text());
      ```

      ```go Go theme={null}
      token, err := tokenSource.IssueToken(ctx, 5*time.Minute, false)
      if err != nil {
      	return fmt.Errorf("issue ingress token: %w", err)
      }

      req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
      if err != nil {
      	return err
      }
      req.Header.Set("x-nsc-ingress-auth", "Bearer "+token)

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
      	return fmt.Errorf("get %s: %w", endpoint, err)
      }
      defer resp.Body.Close()
      ```
    </CodeGroup>

    To make a route reachable without a credential, set `doesNotRequireAuth` on an `httpMatchRule` entry with a method and path `match`. Everything not matched by such a rule still requires authentication.
  </Step>
</Steps>

## TCP ingress with mTLS

`mode: TCP` makes Namespace terminate TLS and proxy the raw stream. Client authentication defaults to requiring mTLS, so a client needs a short-lived Namespace client certificate rather than a bearer token.

<Steps titleSize="h3">
  <Step title="Declare a TCP ingress">
    ```go Go theme={null}
    Ingresses: []*computepb.Ingress{{
    	Name:                 "echo",
    	Mode:                 computepb.Ingress_TCP,
    	Port:                 15000,
    	ClientAuthentication: computepb.IngressClientAuthentication_INGRESS_CLIENT_AUTHENTICATION_MTLS,
    }},
    ```
  </Step>

  <Step title="Read the server name">
    A TCP endpoint is not addressed by an ingress FQDN. The name to dial comes from `extendedMetadata.tlsBackedPort[].serverName`, which requires a `DescribeInstance` call after the instance is ready.

    ```go Go theme={null}
    instance, err := computeClient.Compute.DescribeInstance(ctx, &computepb.DescribeInstanceRequest{
    	InstanceId: resp.Metadata.InstanceId,
    })
    if err != nil {
    	return "", err
    }

    var endpoint string
    for _, ingress := range instance.GetExtendedMetadata().GetTlsBackedPort() {
    	if ingress.GetName() == "echo" {
    		endpoint = ingress.GetServerName()
    		break
    	}
    }
    ```
  </Step>

  <Step title="Dial with a client certificate">
    `auth.TenantCertificateSource` issues the short-lived certificates, and `nstls.ClientConfig` builds a `tls.Config` that presents them.

    ```go Go theme={null}
    import (
    	"crypto/tls"

    	"namespacelabs.dev/integrations/auth"
    	"namespacelabs.dev/integrations/auth/nstls"
    )

    certificates := auth.TenantCertificateSource(token)

    dialer := tls.Dialer{Config: nstls.ClientConfig(ctx, certificates)}
    conn, err := dialer.DialContext(ctx, "tcp", endpoint)
    if err != nil {
    	return fmt.Errorf("dial %s: %w", endpoint, err)
    }
    defer conn.Close()
    ```

    <Info>
      This section is Go only. `nstls.ClientConfig` and `auth.TenantCertificateSource` have no TypeScript equivalent in the published SDK. The underlying RPCs, `IssueTenantClientCertificate` and `ExchangeTenantTokenForClientCert`, are available to any client willing to assemble the TLS configuration itself.
    </Info>
  </Step>
</Steps>

## Source

`go/macoshttps` and `go/macosmtls` in [github.com/namespacelabs/examples](https://github.com/namespacelabs/examples).
See [ingress](/docs/platform/networking/ingress) for the ingress model and [access controls](/docs/platform/networking/ingress#access-controls) for the full authentication matrix.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.