Skip to main content

Connection and Encryption in the TypeScript SDK

Temporal Workers and Clients connect with your Temporal Cluster via gRPC, and must be configured securely for production. There are three main features to know:

  • Namespaces help isolate code from each other
  • TLS Encryption helps encrypt code in transit
  • Data Converter helps encrypt code at rest (available soon)

Temporal Server internally has other Security features, particularly Authorization.

An important part of Temporal's security model is that Temporal Server only manages state and time - it never actually sees or runs your Workflow/Activity code. Code is hosted by Temporal Workers that you run, and Temporal Server only sees inbound/outbound gRPC messages. This eliminates a whole class of problems particularly when providing Temporal to multiple teams in your company, or when working with Temporal Cloud as a customer.

Namespaces

A Namespace is a unit of isolation within the Temporal Platform.

A single Namespace is still multi-tenant. You can use Namespaces to match the development lifecycle; for example, having separate dev and prod Namespaces. Or you could use them to ensure Workflow Executions between different teams never communicate; such as ensuring that the teamA Namespace never impacts the teamB Namespace.

  • If no other Namespace is specified, the Temporal Cluster uses the Namespace "default" for all Temporal SDKs and tctl.
    • If you are deploying through Docker Compose or using the auto-setup image in a custom Docker Compose application, the Namespace "default" is created, through the auto-setup script.
    • If you are deploying through the Temporal Helm charts, you can create the "default" Namespace by using tctl; for example, tctl namespace default. We recommend using the default Namespace if you aren’t using multiple Namespaces.
  • Case Insensitive: Because of DNS, Namespaces are case insensitive on the network and routing side. We recommend using lowercase for namespace names to avoid potential issues.
  • Membership: Task Queue names and Workflow Ids must all correspond to a specific Namespace. For example, when a Workflow Execution is spawned, it does so within a specific Namespace.
  • Uniqueness: Temporal guarantees a unique Workflow Id within a Namespace. Workflow Executions may have the same Workflow Id if they are in different Namespaces.
  • Namespace Configuration: Various configuration options like the retention period and the Archival destination are configured per Namespace through a special CRUD API or through tctl.

All SDK connections (whether Workers or Clients) are to a specific namespace. If not specified in WorkflowClientOptions, this defaults to the default namespace.

const connection = await Connection.connect();

const client = new WorkflowClient({
connection,
namespace: 'your-custom-namespace', // defaults to 'default'
});

Encryption in transit with mTLS

There are two classes in the SDK that connect to the Temporal server, the Worker and the client Connection. When instantiating either of them, you may choose whether to connect securely or not.

A full example for Clients looks like this:

import { Connection, WorkflowClient } from '@temporalio/client';

const connection = await Connection.connect({
// defaults port to 7233 if not specified
address: 'foo.bar.tmprl.cloud',
tls: {
// set to true if TLS without mTLS
// See docs for other TLS options
clientCertPair: {
crt: clientCert,
key: clientKey,
},
},
});
const client = new WorkflowClient({
connection,
namespace: 'foo.bar', // as explained in Namespaces section
});

A full example for Workers looks like this:

import { Worker, NativeConnection } from '@temporalio/worker';
import * as activities from './activities';

async function run() {
const connection = await NativeConnection.connect({
address: 'foo.bar.tmprl.cloud', // defaults port to 7233 if not specified
tls: {
// set to true if TLS without mTLS
// See docs for other TLS options
clientCertPair: {
crt: clientCert,
key: clientKey,
},
},
});

const worker = await Worker.create({
connection,
namespace: 'foo.bar', // as explained in Namespaces section
// ...
});
await worker.run();
}

run().catch((err) => {
console.error(err);
process.exit(1);
});

If you are using mTLS, is completely up to you how to get the clientCert and clientKey pair into your code, whether it is reading from filesystem, secrets manager, or both. Just keep in mind that they are whitespace sensitive and some environment variable systems have been known to cause frustration because they modify whitespace.

Example code that works for local dev and for certs hosted on AWS S3
let serverRootCACertificate: Buffer | undefined;
let clientCertificate: Buffer | undefined;
let clientKey: Buffer | undefined;
if (certificateS3Bucket) {
const s3 = new S3client({ region: certificateS3BucketRegion });
serverRootCACertificate = await s3.getObject({
bucket: certificateS3Bucket,
key: serverRootCACertificatePath,
});
clientCertificate = await s3.getObject({
bucket: certificateS3Bucket,
key: clientCertPath,
});
clientKey = await s3.getObject({
bucket: certificateS3Bucket,
key: clientKeyPath,
});
} else {
serverRootCACertificate = fs.readFileSync(serverRootCACertificatePath);
clientCertificate = fs.readFileSync(clientCertPath);
clientKey = fs.readFileSync(clientKeyPath);
}

Thanks to our Design Partner [Mina Abadir](https://twitter.com/abadir) for sharing this._

Connecting to Temporal Cloud (with mTLS)

The Hello World mTLS sample shows how to connect to a Temporal Cloud account. After signing up for Temporal Cloud, you should have a namespace, a server address, and a client certificate and key. Use the following environment variables to set up the sample:

  • TEMPORAL_ADDRESS: looks like foo.bar.tmprl.cloud (NOT web.foo.bar.tmprl.cloud)
  • TEMPORAL_NAMESPACE: looks like foo.bar
  • TEMPORAL_CLIENT_CERT_PATH: e.g. /tls/ca.pem (file contents start with -----BEGIN CERTIFICATE-----)
  • TEMPORAL_CLIENT_KEY_PATH: e.g. /tls/ca.key (file contents start with -----BEGIN PRIVATE KEY-----)

You can leave the remaining vars, like TEMPORAL_SERVER_NAME_OVERRIDE and TEMPORAL_SERVER_ROOT_CA_CERT_PATH blank. There is another var, TEMPORAL_TASK_QUEUE, which the example defaults to 'hello-world-mtls' but you can customize as needed.

Example environment settings
export function getEnv(): Env {
return {
// NOT web.foo.bar.tmprl.cloud
address: 'foo.bar.tmprl.cloud',
namespace: 'foo.bar',
// in project root
clientCertPath: 'foobar.pem',
clientKeyPath: 'foobar.key',
// just to ensure task queue is same on client and worker, totally optional
taskQueue: process.env.TEMPORAL_TASK_QUEUE || 'hello-world-mtls',
// // not usually needed
// serverNameOverride: process.env.TEMPORAL_SERVER_NAME_OVERRIDE,
// serverRootCACertificatePath: process.env.TEMPORAL_SERVER_ROOT_CA_CERT_PATH,
};
}

If you have misconfigured your connection somehow, you will get an opaque [TransportError: transport error] error. Read through your settings carefully and contact us if you are sure you have checked everything.

Note the difference between the gRPC and Temporal Web endpoints:

  • The gRPC endpoint has a DNS address of <Namespace_ID>.tmprl.cloud, for example: accounting-production.f45a2.tmprl.cloud.
  • The Temporal Web endpoint is web.<Namespace_ID>.tmprl.cloud, for example: https://web.accounting-production.f45a2.tmprl.cloud.

Local mTLS sample tutorial

Follow this tutorial for setting up mTLS (Mutual TLS authentication) with Temporal Server, Client, and Worker locally. For Temporal Cloud customers, there is a separate tutorial above.

  1. Set up Temporal Server with mTLS encryption locally
    • Clone the server samples repo and change to the tls/tls-simple directory
    • Follow these instructions to set up a local server with mTLS
    • The sample does not register the default Namespace on startup, register it with: docker exec -it tls-simple_temporal-admin-tools_1 tctl n re --retention 1 default
  2. Configure your Temporal Client and Worker to connect with mTLS
    • Scaffold a new Temporal project with npx @temporalio/create@latest using the hello-world-mtls template, or copy the relevant configuration from the snippets below into an existing project.
    • Export the required environment variables:
      export TEMPORAL_ADDRESS=localhost
      export TEMPORAL_NAMESPACE=default
      export TEMPORAL_CLIENT_CERT_PATH=/path/to/samples-server/tls/tls-simple/certs/client.pem
      export TEMPORAL_CLIENT_KEY_PATH=/path/to/samples-server/tls/tls-simple/certs/client.key
      # just for the local mTLS sample
      export TEMPORAL_SERVER_ROOT_CA_CERT_PATH=/path/to/samples-server/tls/tls-simple/certs/ca.cert
      export TEMPORAL_SERVER_NAME_OVERRIDE=tls-sample
  3. Test the connection with npm run start.watch and npm run workflow. You should see everything working as per the regular Hello World tutorial.

Temporal has no opinions on production deployment strategy other than the connections and architecture displayed here.

Encryption at rest with Payload Codec