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.
Usage
Namespaces are created on the Temporal Cluster, and provide a range of controls to achieve isolation on Workflow Executions.
- Namespaces are a mechanism for resource isolation. Heavy traffic from one Namespace will not impact other Namespaces running on the same Cluster.
For example, you can use Namespaces to match the development lifecycle by having separate
dev
andprod
Namespaces. - If no other Namespace is specified, the Temporal Cluster uses the Namespace "default" for all Temporal SDKs and tctl. See the Registration section for details.
- Namespaces created on self-hosted Temporal Clusters are case-sensitive. For example,
foo
andFoo
are two different Namespaces. On Temporal Cloud, Namespaces are case-insensitive, and 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
.
Registration
Registering a Namespace creates the Namespace on the Temporal Cluster. When you register your Namespace, you must also set the Retention Period for the Namespace.
On Temporal Cloud, use the Temporal Cloud UI or tcld commands to create and manage Namespaces.
On self-hosted Temporal Cluster, you can register your Namespaces using tctl (recommended) or programmatically using APIs. Note that these APIs and tctl commands will not work with Temporal Cloud.
All SDKs require a Namespace on the Temporal Cluster (or Temporal Cloud) for their Client calls. If not set using Client options, the Workflow Client API looks for the default
Namespace. If there is no default Namespace registered with your Temporal Cluster (or Temporal Cloud), all calls will throw errors.
You must register your Namespace with the Temporal Cluster (or Temporal Cloud) before setting it in your Client.
On self-hosted Temporal Clusters, you can register your Namespaces in the following ways:
In your Cluster setup, create your Namespaces, including the default, in your setup script. For example:
- If 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 deploying through the Temporal Helm charts, you can create the "default" Namespace by using tctl; for example,
tctl --namespace default namespace register
.
Use the
tctl namespace register
command with the--retention
modfiier to register your Namespaces, one at a time, and set the Retention Period on each.In your Client program, register your Namespace using
RegisterNamespaceRequest
API available in all the SDKs.
Note that registering a Namespace takes up to 15 seconds to complete. Ensure that you are waiting for this process to complete before making calls to the Namespace.
Manage Namespaces
Use a custom Authorizer on your Frontend Service in the Temporal Cluster to set restrictions on who can create, update, or deprecate Namespaces.
On Temporal Cloud, use the Temporal Cloud UI or tcld commands to manage Namespaces.
On self-hosted Temporal Cluster, you can manage your registered Namespaces using tctl (recommended) or programmatically using APIs. Note that these APIs and tctl commands will not work with Temporal Cloud.
Update information and configuration for a registered Namespace on your Temporal Cluster:
- With tctl:
tctl namespace update
- Use the [
UpdateNamespace
API]9(/application-development/features#namespaces) to update configuration on a Namespace.
- With tctl:
Get details for a registered Namespace on your Temporal Cluster:
- With tctl:
tctl namespace describe
- Use the
DescribeNamespace
API to return information and configuration details for a registered Namespace.
- With tctl:
Get details for all registered Namespaces on your Temporal Cluster:
- With tctl:
tctl namespace list
- Use the
ListNamespace
API to return information and configuration details for all registered Namespaces on your Temporal Cluster.
- With tctl:
Deprecate a Namespace: The
DeprecateNamespace
API updates the state of a registered Namespace to "DEPRECATED". Once a Namespace is deprecated, you cannot start new Workflow Executions on it. All existing and running Workflow Executions on a deprecated Namespace will continue to run.
Setting
Set Namespaces in your SDK Client to isolate your Workflow Executions to the Namespace. If you do not set a Namespace, all Workflow Executions started using the Client will be associated with the "default" Namespace. This means, you must have a default Namespace called "default" registered with your Temporal Cluster. See Registration for details.
- How to set the Namespace for a Temporal Client
- How to list Namespaces in a Cluster using tctl
- How to view (describe) Namespace metadata and details using tctl
All SDK connections (whether Workers or Clients) are to a specific namespace.
If not specified in WorkflowClientOptions, this defaults to the default
namespace.
- TypeScript
- JavaScript
const connection = await Connection.connect();
const client = new WorkflowClient({
connection,
namespace: 'your-custom-namespace', // defaults to 'default'
});
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.
- In order to connect to the server using TLS, set a truthy value (
true
or TLSConfig for custom options) in thetls
configuration option. - Use
ServerOptions.tls
when creating a new Worker andConnectionOptions.tls
for theConnection
constructor. - The client connection also accepts gRPC credentials at
ConnectionOptions.credentials
as long astls
is not also specified.
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 { NativeConnection, Worker } 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
- TypeScript
- JavaScript
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);
}
let serverRootCACertificate;
let clientCertificate;
let clientKey;
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
- TypeScript
- JavaScript
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,
};
}
export function getEnv() {
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.
- 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
- Clone the server samples repo and change to the
- Configure your Temporal Client and Worker to connect with mTLS
- Scaffold a new Temporal project with
npx @temporalio/create@latest
using thehello-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
- Scaffold a new Temporal project with
- Test the connection with
npm run start.watch
andnpm 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.