Skip to content

Conversation

@karthikscale3
Copy link
Contributor

AWS World: Reference implementation for serverless workflow execution

Summary

This PR introduces @workflow/world-aws as a reference implementation for running Workflow DevKit on AWS infrastructure. It demonstrates how to build a serverless world implementation using DynamoDB, SQS, S3, and Lambda, providing a blueprint for custom AWS-based workflow backends.

Features

AWS Infrastructure Integration

  • DynamoDB: Durable state persistence for workflow runs, steps, events, hooks, and stream chunks
  • SQS: Reliable message queuing for workflow orchestration and step execution
  • S3: Large payload storage for streams exceeding 256KB
  • Lambda: Serverless execution environment with automatic scaling

Developer Experience

  • CLI Tool: aws-workflow provides commands for bootstrapping infrastructure, deploying workflows, and managing resources
  • One-command setup: npx aws-workflow bootstrap -y creates all required AWS resources
  • Zero-config deployment: Automatically packages and deploys workflows to Lambda

Implementation Details

Package Structure

  • Aligned with @workflow/world-postgres structure for consistency across reference implementations
  • Extends shared TypeScript configuration from @workflow/tsconfig
  • Includes Turbo configuration for efficient monorepo builds

Documentation

  • Comprehensive README with setup instructions, configuration options, and examples
  • Official documentation page at /docs/deploying/world/aws-world
  • CLI help text with usage examples

Status

⚠️ EXPERIMENTAL & BETA: This is a reference implementation for demonstration purposes. It is not recommended for production use without additional hardening and testing.

Use Cases

This reference implementation can be used as:

  • A template for building custom AWS-based world implementations
  • A learning resource for understanding the World interface
  • A starting point for production implementations with additional security and reliability features

Cost Estimate

Free tier eligible. Typical cost: $5-20/month for moderate usage.

@changeset-bot
Copy link

changeset-bot bot commented Oct 31, 2025

⚠️ No Changeset found

Latest commit: 176a870

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Contributor

vercel bot commented Oct 31, 2025

@karthikscale3 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Comment on lines +42 to +45
workflow: process.env.WORKFLOW_AWS_WORKFLOW_QUEUE_URL!,
step: process.env.WORKFLOW_AWS_STEP_QUEUE_URL!,
},
streamBucket: process.env.WORKFLOW_AWS_STREAM_BUCKET!,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical runtime error: Non-null assertions on environment variables that may not be defined will cause the application to crash when AWS resources aren't properly configured.

View Details
📝 Patch Details
diff --git a/packages/world-aws/world/config.ts b/packages/world-aws/world/config.ts
index 8c6278b..dcc8e47 100644
--- a/packages/world-aws/world/config.ts
+++ b/packages/world-aws/world/config.ts
@@ -27,6 +27,25 @@ export function getDefaultConfig(): AWSWorldConfig {
     region: process.env.AWS_REGION || process.env.WORKFLOW_AWS_REGION,
   });
 
+  // Validate required environment variables
+  const requiredEnvVars: Record<string, string | undefined> = {
+    WORKFLOW_AWS_WORKFLOW_QUEUE_URL: process.env.WORKFLOW_AWS_WORKFLOW_QUEUE_URL,
+    WORKFLOW_AWS_STEP_QUEUE_URL: process.env.WORKFLOW_AWS_STEP_QUEUE_URL,
+    WORKFLOW_AWS_STREAM_BUCKET: process.env.WORKFLOW_AWS_STREAM_BUCKET,
+  };
+
+  const missingVars = Object.entries(requiredEnvVars)
+    .filter(([, value]) => !value)
+    .map(([key]) => key);
+
+  if (missingVars.length > 0) {
+    throw new Error(
+      `Missing required AWS World environment variables: ${missingVars.join(', ')}. ` +
+      `Please ensure these are set before using AWS World. ` +
+      `See https://github.com/vercel/workflow-devkit for setup instructions.`
+    );
+  }
+
   const config = {
     region:
       process.env.AWS_REGION || process.env.WORKFLOW_AWS_REGION || 'us-east-1',
@@ -39,10 +58,10 @@ export function getDefaultConfig(): AWSWorldConfig {
         process.env.WORKFLOW_AWS_STREAMS_TABLE || 'workflow_stream_chunks',
     },
     queues: {
-      workflow: process.env.WORKFLOW_AWS_WORKFLOW_QUEUE_URL!,
-      step: process.env.WORKFLOW_AWS_STEP_QUEUE_URL!,
+      workflow: requiredEnvVars.WORKFLOW_AWS_WORKFLOW_QUEUE_URL as string,
+      step: requiredEnvVars.WORKFLOW_AWS_STEP_QUEUE_URL as string,
     },
-    streamBucket: process.env.WORKFLOW_AWS_STREAM_BUCKET!,
+    streamBucket: requiredEnvVars.WORKFLOW_AWS_STREAM_BUCKET as string,
     // CRITICAL: In Lambda, AWS credentials are auto-provided BUT we should NOT pass them explicitly
     // The AWS SDK will automatically use the Lambda execution role if we don't provide credentials
     credentials: undefined, // Always use Lambda's IAM role, never explicit credentials

Analysis

Missing environment variable validation in getDefaultConfig()

What fails: getDefaultConfig() in packages/world-aws/world/config.ts uses TypeScript non-null assertions (!) on three required environment variables (WORKFLOW_AWS_WORKFLOW_QUEUE_URL, WORKFLOW_AWS_STEP_QUEUE_URL, WORKFLOW_AWS_STREAM_BUCKET) without runtime validation. When these variables are missing, the function returns a config object with undefined values that later cause failures when passed to AWS SDK methods.

How to reproduce:

# In an environment where the required env vars are not set
unset WORKFLOW_AWS_WORKFLOW_QUEUE_URL
unset WORKFLOW_AWS_STEP_QUEUE_URL  
unset WORKFLOW_AWS_STREAM_BUCKET

# Import and call getDefaultConfig
node -e "const {getDefaultConfig} = require('./packages/world-aws/dist/world/config.js'); getDefaultConfig();"

Result before fix: Returns config object with undefined values for queue URLs and bucket. When these are passed to SQS operations (e.g., in packages/world-aws/world/queue.ts line 27-28), the AWS SDK throws "Missing required parameter QueueUrl".

Expected: Should immediately throw a clear error listing which required environment variables are missing, preventing silent failures.

Fix implemented: Added validation in getDefaultConfig() that:

  1. Checks for presence of required environment variables at function start
  2. Throws a clear error with list of missing variables if any are not set
  3. Replaced non-null assertions with type-safe variable access after validation

This ensures misconfiguration is caught immediately with a helpful error message instead of failing later in AWS SDK calls.

const streams = new dynamodb.Table(this, 'WorkflowStreamChunks', {
tableName: 'workflow_stream_chunks',
partitionKey: { name: 'streamId', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'chunkIndex', type: dynamodb.AttributeType.NUMBER },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sortKey: { name: 'chunkIndex', type: dynamodb.AttributeType.NUMBER },
sortKey: { name: 'chunkId', type: dynamodb.AttributeType.STRING },

DynamoDB schema mismatch: The stream chunks table is defined with a NUMBER sort key (chunkIndex), but the code writes STRING values (chunkId) to it, causing DynamoDB operations to fail.

View Details

Analysis

DynamoDB Stream Chunks Table Schema Mismatch

What fails: WorkflowInfrastructure.createTables() defines the stream chunks DynamoDB table with a sort key named chunkIndex (NUMBER type), but createStreamer() writes items with a chunkId (STRING type) sort key, causing DynamoDB PutItem/UpdateItem operations to fail at runtime.

How to reproduce:

// In packages/world-aws/src/cdk/index.ts line 144
const streams = new dynamodb.Table(this, 'WorkflowStreamChunks', {
  tableName: 'workflow_stream_chunks',
  partitionKey: { name: 'streamId', type: dynamodb.AttributeType.STRING },
  sortKey: { name: 'chunkIndex', type: dynamodb.AttributeType.NUMBER },  // ❌ Wrong
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
});

// In packages/world-aws/world/streamer.ts line 38-67
const streamChunk: StreamChunk = {
  streamId: name,
  chunkId,  // ❌ STRING value like "chnk_01ARYZ5ADG..."
  s3Key,
  eof: false,
  createdAt: new Date().toISOString(),
};

await dynamoClient.send(
  new PutCommand({
    TableName: tableName,
    Item: streamChunk,  // Writes to table expecting NUMBER sort key
  })
);

Result: DynamoDB rejects the PutItem operation with validation error because the table schema expects a NUMBER attribute chunkIndex but receives a STRING attribute chunkId.

Expected: The sort key should be named chunkId (STRING type) to match what createStreamer() writes. This is correctly defined in packages/world-aws/lib/schema-utils.ts line 147 and packages/world-aws/lib/workflow-stack.ts line 192-194, where both use chunkId (STRING). Since WorkflowInfrastructure is exported in package.json exports.cdk, users deploying this infrastructure will encounter failures when attempting to write stream data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant