Skip to main content

Context IDs

Context IDs provide request and runtime scoping across the TWIN stack. They are used to carry identity and tenancy metadata through async execution, enforce route-level context rules, and derive storage partition keys.

AsyncLocalStorage

The context package stores request context in Node.js AsyncLocalStorage through ContextIdStore.

At request entry, the API server creates a mutable contextIds object and runs route processing inside ContextIdStore.run(contextIds, ...). This means all downstream service code can read the same active context with ContextIdStore.getContextIds() without passing context through every function signature.

Engine lifecycle execution also runs inside ContextIdStore.run(...) so startup and shutdown logic can use the configured context IDs consistently.

Context IDs: Async Propagation Through a Request

Incoming Request
API server: ContextIdStore.run(contextIds, handler) — establishes async scope
TenantProcessor
Resolves tenant from x-api-key (or query parameter fallback) and writes contextIds.tenant
AuthHeaderProcessor
Verifies JWT and writes contextIds.organization (from org claim) and contextIds.user (from sub claim)
Route → Component → Connector
Any code anywhere in the call stack calls ContextIdStore.getContextIds() to read the full active context — no parameter threading required

Default Keys and Value Derivation

The default key names come from ContextIdKeys:

  • node
  • tenant
  • organization
  • user

The table below shows where values are populated.

KeyPrimary SourceHow Value Is Derived
nodeNode runtime stateDuring node startup, configureContextIds reads state.nodeId and sets engine.addContextId(ContextIdKeys.Node, state.nodeId) when node identity is enabled.
tenantNode runtime state and tenant processorStartup sets from state.nodeTenantId when tenant mode is enabled. In tenant-processor mode, request pre-processing resolves tenant from x-api-key (or query fallback) and writes contextIds[ContextIdKeys.Tenant].
organizationJWT claimAuthHeaderProcessor verifies token and maps JWT org claim to contextIds[ContextIdKeys.Organization].
userJWT claimAuthHeaderProcessor verifies token and maps JWT sub claim to contextIds[ContextIdKeys.User].

Notes:

  • Tenant-aware auth also validates that request tenant context and JWT tid match.
  • The runtime key is organization in code; this corresponds to organisation scope in platform documentation.

Context ID Handlers and Short Forms

Context IDs can be transformed by handlers before partitioning.

  • DidContextIdHandler validates DID format and returns the DID id segment as the short value (hex ids are compacted to base64url).
  • TenantIdContextIdHandler validates tenant id format and returns a compact base64url short form.

Engine context-key registration associates each key with required handler features, then binds the matching handler implementation into ContextIdHandlerFactory.

Handlers are registered only for keys intended for partitioning. Keys that are typically populated only after authentication, such as organization and user, are intentionally left without default handlers to avoid coupling storage partitioning to identity values that are not available during unauthenticated or pre-auth flows.

Full and Short Form Examples

The table below shows illustrative full values and their short forms as used for partitioning.

KeyExample Full FormExample Short FormHow Short Form Is Produced
nodedid:iota:0x8c3f4a5e8d9a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0ajD9KXo2ae2xdTj8qGwydjn9qW0w9Lh8KDidContextIdHandler takes the DID id segment and compacts hex to base64url.
tenant7f3a9c1e0b4d6f8a2c5e7b9d1a3c4e6ffzqcHgtNb4osXnudGjxObwTenantIdContextIdHandler converts 32-hex tenant id to base64url.
organizationdid:iota:0x1234567890abcdef1234567890abcdef1234567890abcdefNot producedNo default context-id handler is registered for this key.
userdid:iota:0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefNot producedNo default context-id handler is registered for this key.

When multiple keys are used for partitioning, ContextIdHelper.combinedContextKey(...) joins the short forms with /. For example:

  • Keys: node, tenant
  • Combined partition key: jD9KXo2ae2xdTj8qGwydjn9qW0w9Lh8K/fzqcHgtNb4osXnudGjxObw

Data Partitioning

Many connectors support context-based partitioning through optional partitionContextIds settings. When configured, the connector:

  1. reads current context IDs using ContextIdStore.getContextIds(),
  2. derives a combined partition key via ContextIdHelper.combinedContextKey(...),
  3. uses that key to isolate stored data.

Examples:

  • Entity storage connectors persist an internal partitionId and include it in get, set, remove, and query filtering.
  • Blob storage connectors compose keys as <partitionKey>/<id>, with a root fallback where relevant.

Partition Key Derivation

node (full form)
did:iota:0x8c3f4a5e...1f0a
DidContextIdHandler
extracts id segment, compacts hex to base64url
node (short form)
jD9KXo2ae2x...
tenant (full form)
7f3a9c1e0b4d6f8a...4e6f
TenantIdContextIdHandler
converts 32-hex tenant id to base64url
tenant (short form)
fzqcHgtNb4...
ContextIdHelper.combinedContextKey(...) joins short forms with /
Combined Partition Key
jD9KXo2ae2x.../fzqcHgtNb4...
Used by connectors to isolate stored data by partition — entity storage includes it as a filter, blob storage prefixes keys with it

Optional Partition IDs

Partition IDs are optional by design:

  • If partitionContextIds is not configured, no context partition key is produced.
  • If no partition key is produced, storage operates in a non-partitioned scope.
  • If partition keys are configured but required context IDs are missing, context guards fail early rather than silently crossing partitions.

This model supports both single-scope deployments and strict multi-scope isolation without changing application service contracts.

For details on how partitioning behaves across different storage backends, multi-tenant deployment models, and tenant management, see Data Partitioning and Multi-Tenant Architecture.