diff --git a/docs/guides/c4/c4-api.mdx b/docs/guides/c4/c4-api.mdx
new file mode 100644
index 000000000..7641c3235
--- /dev/null
+++ b/docs/guides/c4/c4-api.mdx
@@ -0,0 +1,151 @@
+---
+description: 'C4 Api'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'API' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to create and manage APIs within their application.
+ - App code interacts with the **API resource** through defined endpoints.
+ - Developers define API specifications and implement backend logic to handle requests.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS API Gateway resources.
+ - **AWS API Gateway v2** serves as the HTTP API management service.
+ - **AWS Lambda** functions are deployed to handle API requests.
+ - **AWS IAM** (implicitly assumed) provides roles and policies for secure interaction between API Gateway and Lambda functions.
+ - **AWS ACM** manages SSL/TLS certificates for custom domain names.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ APIGateway["AWS API Gateway v2
(HTTP API)"]
+ Lambda["AWS Lambda Functions"]
+ IAM["AWS IAM"]
+ ACM["AWS ACM
(Certificates)"]
+
+ Developer -->|Define API| App
+ Operations -->|Terraform| App
+ App -->|Create API Gateway| APIGateway
+ App -->|Deploy Lambda Functions| Lambda
+ App -->|Configure Permissions| IAM
+ APIGateway -->|Invoke| Lambda
+ ACM -->|Provide Certificates| APIGateway
+ IAM -->|Manage Access| APIGateway
+ App -->ACM
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **API** is managed through AWS API Gateway v2 and interacts with backend Lambda functions to process HTTP requests.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(API 1)
+ Nitric -->C(API 2)
+ Nitric -->D(...)
+ B(API 1) -->E(HTTP GET)
+ B(API 1) -->F(HTTP POST)
+ B(API 1) -->G(...)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+### API Module
+
+- **aws_apigatewayv2_api.api_gateway**
+ - Creates an AWS API Gateway v2 HTTP API.
+ - Configures the API name, protocol type, API specification (`body`), and tags for identification and management.
+- **aws_apigatewayv2_stage.stage**
+ - Creates a stage for the API Gateway.
+ - Sets the stage name to `$default` and enables automatic deployment of changes.
+- **aws_lambda_permission.apigw_lambda**
+ - Grants API Gateway permission to invoke the specified Lambda functions.
+ - Iterates over `var.target_lambda_functions` to set permissions for each target function.
+- **data.aws_acm_certificate.cert**
+ - Looks up existing ACM certificates for the specified domains.
+ - Iterates over `var.domains` to retrieve certificate details for each domain.
+- **aws_apigatewayv2_domain_name.domain**
+
+ - Creates custom domain names for the API Gateway using the retrieved ACM certificates.
+ - Configures domain name settings, including the certificate ARN, endpoint type, and security policy.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'api' resource from the SDK, configures the api, and implements HTTP routes, implement middleware, etc.
+
+```typescript
+import { api } from '@nitric/sdk'
+
+const publicApi = api('public')
+
+api('public').get('/customers', (ctx) => {
+ // construct response for the GET: /customers request...
+ const responseBody = {}
+ ctx.res.json(responseBody)
+})
+
+const authMiddleware = async (ctx, next) => {
+ // Perform auth validation.
+ return await next(ctx)
+}
+
+const privateApi = api('private', { middleware: authMiddleware })
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS API Gateway as defined.
+
+```hcl
+resource "aws_apigatewayv2_api" "api_gateway" {
+ name = var.name
+ protocol_type = "HTTP"
+ body = var.spec
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.name,
+ "x-nitric-${var.stack_id}-type" = "api",
+ }
+}
+
+resource "aws_apigatewayv2_stage" "stage" {
+ api_id = aws_apigatewayv2_api.api_gateway.id
+ name = "$default"
+ auto_deploy = true
+}
+
+# deploy lambda permissions for execution
+resource "aws_lambda_permission" "apigw_lambda" {
+ for_each = var.target_lambda_functions
+ action = "lambda:InvokeFunction"
+ function_name = each.value
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_apigatewayv2_api.api_gateway.execution_arn}/*/*/*"
+}
+
+# look up existing certificate for domains
+data "aws_acm_certificate" "cert" {
+ for_each = var.domains
+ domain = each.value
+}
+
+# deploy custom domain names
+resource "aws_apigatewayv2_domain_name" "domain" {
+ for_each = var.domains
+ domain_name = each.value
+ domain_name_configuration {
+ certificate_arn = data.aws_acm_certificate.cert[each.key].arn
+ endpoint_type = "REGIONAL"
+ security_policy = "TLS_1_2"
+ }
+}
+```
diff --git a/docs/guides/c4/c4-big-picture.mdx b/docs/guides/c4/c4-big-picture.mdx
new file mode 100644
index 000000000..b1e4947fc
--- /dev/null
+++ b/docs/guides/c4/c4-big-picture.mdx
@@ -0,0 +1,92 @@
+---
+description: 'C4 Nitric High-Level Architecture'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric High-Level Architecture
+
+Nitric allows your team to work together to build an application:
+
+- **Developer**: Writes application code with built-in support for APIs, file storage (bucket), secrets, key‑value store, and RDS, leveraging the Nitric SDK.
+- **Operations**: Customize, extend or use Nitric's generated IaC (Terraform or Pulumi) to provision and manage the resources that the developer needs for their application.
+- **SRE**: Configure environment/region/policy specific details, they also are heavily involved in overseeing that the Terraform modules themselves adhere to governance standards.
+
+The roles above may overlap depending on your organization structure, for example, it is not abnormal Developers to assume all roles, or for Operations and SRE responsibilities to be handled by the same team.
+
+```mermaid
+flowchart TD
+ Developer[Developer]
+ Operations[Operations]
+ SRE[Site Reliablility Engineer]
+ App[Nitric App - 'nitric up']
+ Repo[Code Repository]
+
+ API[API Gateway]
+ Bucket[Bucket - AWS S3]
+ Secrets[Secrets - AWS Secrets Manager\n]
+ KVStore[Key-Value Store - AWS DynamoDB]
+ RDS[Relational Database - AWS RDS/Aurora]
+ Other[Other resources]
+
+ SRE -->|Deployment Config| Repo
+ Developer -->|Code| Repo
+ Operations -->|Extend/Customize Terraform| Repo
+ Repo-->App
+
+ App -->|Exposes REST/HTTP Routes| API
+ App -->|Stores/Retrieves Files| Bucket
+ App -->|Manages Sensitive Data| Secrets
+ App -->|Reads/Writes Data| KVStore
+ App -->|Executes SQL Queries| RDS
+ App -->|1..n|Other
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+Nitric applications can have any number of APIs, Secrets, Buckets etc. Providers can also be extended to further support new resources, many which will work across all cloud providers and some that are cloud specific.
+
+## Example: Handling HTTP requests.
+
+Interaction with services that have been exposed as HTTP routes within an API gateway, Scheduled tasks, Event subscriptions, WebSocket handlers and more.
+
+```mermaid
+flowchart TD
+ %% Actors
+ Browser[Client Browser]
+
+ %% Nitric Application Containers
+ API[HTTP API - API Gateway]
+ Service[GET Route]
+ Service2[POST Route]
+ Service3[...]
+
+ %% Backend Services / Resources
+ Bucket[AWS S3 Bucket]
+ Secrets[AWS Secrets Manager]
+ KVStore[AWS DynamoDB - Key-Value Store]
+ RDS[AWS RDS/Aurora]
+
+ %% Interactions
+ Browser -->|Sends HTTP Request| API
+ API -->|Triggers Service| Service
+ API -->|Triggers Service| Service2
+ API -->|Triggers Service| Service3
+ Service -->|Manage/Uploads/Downloads files| Bucket
+ Service -->|Retrieves credentials/config data| Secrets
+ Service -->|Reads/Writes key data| KVStore
+ Service -->|Queries/Updates relational data| RDS
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+- The **Client Browser** sends an HTTP request to the **API Gateway**.
+- The **API Gateway** acts as a proxy, forwarding the request to the appropriate **Services**.
+- The **Services** process the request by coordinating with different backend services, this is done through :
+ - They interact with one or more **AWS S3 Bucket** to manage files.
+ - They retrieve credentials or configuration from **AWS Secrets Manager**.
+ - They use **AWS DynamoDB** for fast key-value operations.
+ - They query or update structured data in **AWS RDS/Aurora**.
diff --git a/docs/guides/c4/c4-buckets.mdx b/docs/guides/c4/c4-buckets.mdx
new file mode 100644
index 000000000..12ecb050a
--- /dev/null
+++ b/docs/guides/c4/c4-buckets.mdx
@@ -0,0 +1,135 @@
+---
+description: 'C4 Buckets'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Bucket' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage S3 buckets within their application.
+ - App code imports the **Bucket resource** from the Nitric SDK.
+ - Developers configure buckets and implement application logic to securely access and manipulate bucket data.
+ - Developers can also implement hanlders for on events like on read, write or delete.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS S3 resources.
+ - **AWS S3** serves as the storage backend.
+ - **AWS Lambda** functions are used to process events triggered by S3.
+ - **AWS IAM** provides roles and policies for secure access to S3 buckets and Lambda functions.
+ - **Random ID** resource is used to generate unique bucket names.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ S3Bucket["AWS S3 Bucket"]
+ Lambda["AWS Lambda Functions"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create S3 Bucket| S3Bucket
+ App -->|Configure Notifications| S3Bucket
+ App -->|Allow Lambda Invocation| Lambda
+ S3Bucket -->|Store/Retrieve Data| App
+ Lambda -->|Process Events| App
+ App -->|Provide Access| IAM
+ IAM -->S3Bucket
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **Bucket** is managed through AWS S3 and accessed by the application through securely configured mechanisms provided by Nitric.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(Bucket 1)
+ Nitric -->C(Bucket 2)
+ Nitric -->D(...)
+ B(BUCKET 1) -->E(ON Read)
+ B(BUCKET 1) -->F(ON Write)
+ B(BUCKET 1) -->G(ON Delete)
+```
+
+## 3. Component (Level 3)
+
+### Bucket Module
+
+- **random_id.bucket_id**
+ - Generates a random ID for the S3 bucket to ensure unique naming.
+- **aws_s3_bucket.bucket**
+ - Creates an AWS S3 bucket with a unique name by appending the generated random ID.
+ - Configures tags for identification and management.
+- **aws_lambda_permission.allow_bucket**
+ - Grants AWS S3 permission to invoke the specified Lambda functions.
+ - Iterates over `var.notification_targets` to set permissions for each target.
+- **aws_s3_bucket_notification.bucket_notification**
+ - Configures S3 bucket notifications to trigger Lambda functions based on specified events.
+ - Uses dynamic blocks to handle multiple Lambda function notifications.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'bucket' resource from the SDK, configures the bucket, and implements the application logic to read, write and delete files.
+
+```typescript
+import { bucket } from '@nitric/sdk'
+
+const profiles = bucket('profiles').allow('read')
+
+const image = await profiles.file('users/bruce-wayne/profile.png').read()
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS S3 Buckets as defined.
+
+```hcl
+
+# Generate a random id for the bucket
+resource "random_id" "bucket_id" {
+ byte_length = 8
+
+ keepers = {
+ # Generate a new id each time we switch to a new AMI id
+ bucket_name = var.bucket_name
+ }
+}
+
+# AWS S3 bucket
+resource "aws_s3_bucket" "bucket" {
+ bucket = "${var.bucket_name}-${random_id.bucket_id.hex}"
+
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.bucket_name
+ "x-nitric-${var.stack_id}-type" = "bucket"
+ }
+}
+
+# Deploy bucket lambda invocation permissions
+resource "aws_lambda_permission" "allow_bucket" {
+ for_each = var.notification_targets
+ action = "lambda:InvokeFunction"
+ function_name = each.value.arn
+ principal = "s3.amazonaws.com"
+ source_arn = aws_s3_bucket.bucket.arn
+}
+
+# Deploy lambda notifications
+resource "aws_s3_bucket_notification" "bucket_notification" {
+ bucket = aws_s3_bucket.bucket.id
+
+ // make dynamic blocks for lambda function
+ dynamic "lambda_function" {
+ for_each = var.notification_targets
+ content {
+ lambda_function_arn = lambda_function.value.arn
+ events = lambda_function.value.events
+ filter_prefix = lambda_function.value.prefix
+ }
+ }
+}
+```
diff --git a/docs/guides/c4/c4-keyvalue.mdx b/docs/guides/c4/c4-keyvalue.mdx
new file mode 100644
index 000000000..2a6fc2b68
--- /dev/null
+++ b/docs/guides/c4/c4-keyvalue.mdx
@@ -0,0 +1,96 @@
+---
+description: 'C4 KV'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'KVStore' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage key-value stores within their application.
+ - App code imports the **KVStore resource** from the Nitric SDK.
+ - Developers configure key-value stores and implement application logic to securely access and manipulate data.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS DynamoDB resources.
+ - **AWS DynamoDB** serves as the key-value store backend.
+ - **AWS IAM** provides roles and policies for secure access to DynamoDB tables.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ DynamoDB["AWS DynamoDB
(Key-Value Store)"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create DynamoDB Table| DynamoDB
+ App -->|Access DynamoDB| IAM
+ DynamoDB -->|Store/Retrieve Data| App
+ IAM -->|Provide Access| App
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **KVStore** is managed through AWS DynamoDB and accessed by the application through securely configured mechanisms provided by Nitric.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(KVStore 1)
+ Nitric -->C(KVStore 2)
+ Nitric -->D(...)
+```
+
+## 3. Component (Level 3)
+
+### KVStore Module
+
+- **aws_dynamodb_table.table**
+ - Deploys an AWS DynamoDB table with specified attributes, hash key, range key, billing mode, and tags for identification and management.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'secret' resource from the SDK, configures the secret, and implements the application logic to access and manage secrets.
+
+```typescript
+import { kv } from '@nitric/sdk'
+
+const countries = kv('Countries').allow('delete', 'set')
+
+await countries.set('USA', {
+ name: 'United States of America',
+ population: 329500000,
+})
+
+await countries.delete('USA')
+```
+
+**Operations** will use the provided Terraform module to create and manage the KeyValue store as defined.
+
+```hcl
+# Deploy an aws dynamodb table
+resource "aws_dynamodb_table" "table" {
+ name = var.kvstore_name
+ attribute {
+ name = "_pk"
+ type = "S"
+ }
+ attribute {
+ name = "_sk"
+ type = "S"
+ }
+ hash_key = "_pk"
+ range_key = "_sk"
+ billing_mode = "PAY_PER_REQUEST"
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.kvstore_name
+ "x-nitric-${var.stack_id}-type" = "kvstore"
+ }
+}
+```
diff --git a/docs/guides/c4/c4-queues.mdx b/docs/guides/c4/c4-queues.mdx
new file mode 100644
index 000000000..442c58540
--- /dev/null
+++ b/docs/guides/c4/c4-queues.mdx
@@ -0,0 +1,82 @@
+---
+description: 'C4 Queues'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Queue' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage message queues within their application.
+ - App code imports the **Queue resource** from the Nitric SDK.
+ - Developers configure queues and implement application logic to send and receive messages.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS SQS resources.
+ - **AWS SQS** serves as the message queuing service.
+ - **AWS IAM** provides roles and policies for secure access to SQS queues.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ SQSQueue["AWS SQS Queue"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create SQS Queue| SQSQueue
+ App -->|Access SQS Queue| IAM
+ SQSQueue -->|Send/Receive Messages| App
+ IAM -->|Provide Access| App
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **Queue** is managed through AWS SQS and accessed by the application through securely configured mechanisms provided by Nitric.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(Queue 1)
+ Nitric -->C(Queue 2)
+ Nitric -->D(...)
+```
+
+## 3. Component (Level 3)
+
+### Queue Module
+
+- **aws_sqs_queue.queue**
+ - Deploys an AWS SQS queue with a specified name.
+ - Configures tags for identification and management.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'secret' resource from the SDK, configures the secret, and implements the application logic to access and manage secrets.
+
+```typescript
+import { queue } from '@nitric/sdk'
+
+const payload = {}
+const batchQueue = queue('batch').allow('dequeue')
+
+const messages = await batchQueue.enqueue()
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS SQS queues as defined.
+
+```hcl
+# Deploy an SQS queue
+resource "aws_sqs_queue" "queue" {
+ name = var.queue_name
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.queue_name
+ "x-nitric-${var.stack_id}-type" = "queue"
+ }
+}
+```
diff --git a/docs/guides/c4/c4-rds.mdx b/docs/guides/c4/c4-rds.mdx
new file mode 100644
index 000000000..fc8625654
--- /dev/null
+++ b/docs/guides/c4/c4-rds.mdx
@@ -0,0 +1,253 @@
+---
+description: 'C4'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'RDS' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage relational databases and automate database creation within their application.
+ - App code interacts with the **RDS Cluster** for data storage and retrieval.
+ - Developers utilize **CodeBuild** projects to automate database setup and migrations.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS resources.
+ - **AWS RDS** provides a scalable and managed relational database service.
+ - **AWS CodeBuild** automates the process of setting up and managing the database schema.
+ - **AWS IAM** manages roles and permissions for secure access to AWS resources.
+ - **AWS VPC** ensures network isolation and security for the database instances.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ RDSCluster["AWS RDS Cluster"]
+ CodeBuild["AWS CodeBuild"]
+ IAM["AWS IAM"]
+ VPC["AWS VPC"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Provision RDS Cluster| RDSCluster
+ App -->|Configure CodeBuild| CodeBuild
+ RDSCluster <-->|Store/Retrieve Data| App
+ CodeBuild -->|Automate Database Setup| RDSCluster
+ IAM -->|Manage Permissions| RDSCluster
+ VPC -->|Network Security| RDSCluster
+ App -->IAM
+ App -->VPC
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+The system comprises several AWS services orchestrated through Terraform to provide a secure and scalable relational database environment with automated deployment capabilities.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->A(RDS Cluster)
+ Nitric -->B(CodeBuild Project)
+ Nitric -->C(IAM Roles)
+ Nitric -->D(VPC Configuration)
+ Nitric -->E(Security Groups)
+```
+
+## 3. Component (Level 3)
+
+### RDS Module
+
+- **random_password.rds_password**
+ - Generates a secure random password for the RDS cluster.
+- **aws_db_subnet_group.rds_subnet_group**
+ - Creates a subnet group for the RDS instance to ensure it is deployed within the specified private subnets.
+- **aws_security_group.rds_security_group**
+ - Defines a security group for the RDS instance, allowing inbound traffic on port 5432 (PostgreSQL) from within the VPC and allowing all outbound traffic.
+- **aws_iam_role.codebuild_role**
+ - Creates an IAM role for AWS CodeBuild with a trust relationship allowing CodeBuild to assume the role.
+- **aws_iam_role_policy_attachment.codebuild_managed_policies**
+ - Attaches managed IAM policies to the CodeBuild role to grant necessary permissions for CodeBuild operations.
+- **aws_rds_cluster.rds_cluster**
+ - Creates an AWS RDS cluster with Aurora PostgreSQL engine, configuring it for serverless v2 with specified scaling parameters.
+- **aws_rds_cluster_instance.rds_cluster_instance**
+ - Creates an instance within the RDS cluster, specifying the instance class and engine details.
+- **aws_codebuild_project.create_database**
+ - Sets up an AWS CodeBuild project to automate the creation of the database within the RDS cluster. It includes environment variables, VPC configuration, and build specifications.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'rds' resource from the SDK, configures and connects to an RDS.
+
+```typescript
+import { api, sql } from '@nitric/sdk'
+import { PrismaClient } from '@prisma/client'
+
+const mainApi = api('main')
+const db = sql('my-data')
+
+let prisma
+const getClient = async () => {
+ // ensure we only create the client once
+ if (!prisma) {
+ const connectionString = await db.connectionString()
+
+ prisma = new PrismaClient({
+ datasourceUrl: connectionString,
+ })
+ }
+ return prisma
+}
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS RDS as defined.
+
+```hcl
+# Create a new random password for the RDS cluster
+resource "random_password" "rds_password" {
+ length = 16
+ special = false
+}
+
+# Create a subnet group for the RDS instance
+resource "aws_db_subnet_group" "rds_subnet_group" {
+ subnet_ids = var.private_subnet_ids
+}
+
+# Create a security group for the RDS instance
+resource "aws_security_group" "rds_security_group" {
+ vpc_id = var.vpc_id
+
+ ingress {
+ from_port = 5432
+ to_port = 5432
+ protocol = "tcp"
+ self = true
+ }
+
+ egress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+}
+
+# Create a role for the codebuild job
+resource "aws_iam_role" "codebuild_role" {
+ name = "nitric-codebuild-role"
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17",
+ Statement = [
+ {
+ Effect = "Allow",
+ Principal = {
+ Service = "codebuild.amazonaws.com"
+ },
+ Action = "sts:AssumeRole"
+ }
+ ]
+ })
+}
+
+# Attach managed policies to the codebuild role
+locals {
+ codebuildManagedPolicies = {
+ "codeBuildAdmin" = "arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess"
+ "rdsAdmin" = "arn:aws:iam::aws:policy/AmazonRDSFullAccess"
+ "ec2Admin" = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
+ "cloudWatchLogs" = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
+ "ecrReadonly" = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
+ }
+}
+
+resource "aws_iam_role_policy_attachment" "codebuild_managed_policies" {
+ for_each = local.codebuildManagedPolicies
+
+ role = aws_iam_role.codebuild_role.name
+ policy_arn = each.value
+}
+
+# Create an RDS cluster with serverless v2
+resource "aws_rds_cluster" "rds_cluster" {
+ cluster_identifier = "nitric-rds-cluster"
+ engine = "aurora-postgresql"
+ engine_mode = "provisioned"
+ engine_version = "13.14"
+ database_name = "nitric"
+ master_username = "nitric"
+ master_password = random_password.rds_password.result
+ db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.id
+ vpc_security_group_ids = [aws_security_group.rds_security_group.id]
+ skip_final_snapshot = true
+ deletion_protection = false
+ serverlessv2_scaling_configuration {
+ max_capacity = var.max_capacity
+ min_capacity = var.min_capacity
+ }
+}
+
+# Create a rds cluster instance
+resource "aws_rds_cluster_instance" "rds_cluster_instance" {
+ cluster_identifier = aws_rds_cluster.rds_cluster.id
+ instance_class = "db.serverless"
+ engine = aws_rds_cluster.rds_cluster.engine
+ engine_version = aws_rds_cluster.rds_cluster.engine_version
+ db_subnet_group_name = aws_rds_cluster.rds_cluster.db_subnet_group_name
+}
+
+
+
+# Create an AWS Codebuild job to create a database on the RDS cluster
+resource "aws_codebuild_project" "create_database" {
+ name = "nitric-create-database"
+ description = "Create the database on the RDS cluster"
+ build_timeout = 60
+ service_role = aws_iam_role.codebuild_role.arn
+
+ artifacts {
+ type = "NO_ARTIFACTS"
+ }
+
+
+ environment {
+ compute_type = "BUILD_GENERAL1_SMALL"
+ image = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
+ type = "LINUX_CONTAINER"
+
+ environment_variable {
+ name = "DB_PASSWORD"
+ value = random_password.rds_password.result
+ type = "PLAINTEXT"
+ }
+ }
+
+
+ vpc_config {
+ subnets = var.private_subnet_ids
+ security_group_ids = aws_rds_cluster.rds_cluster.vpc_security_group_ids
+ vpc_id = var.vpc_id
+ }
+
+ source {
+ type = "NO_SOURCE"
+ buildspec = jsonencode({
+ version = "0.2",
+ phases = {
+ build = {
+ commands = [
+ "echo 'Creating database $DB_NAME'",
+ # FIXME: Store the password in a secret manager
+ "export PGPASSWORD=$${DB_PASSWORD}",
+ # "CREATE DATABASE ${DB_NAME}" || echo "database ${DB_NAME} already exists"
+ "psql -h ${aws_rds_cluster.rds_cluster.endpoint} -U ${aws_rds_cluster.rds_cluster.master_username} -d ${aws_rds_cluster.rds_cluster.database_name} -c \"CREATE DATABASE $${DB_NAME}\" || echo \"database $${DB_NAME} already exists\""
+ ]
+ }
+ }
+ })
+ }
+}
+```
diff --git a/docs/guides/c4/c4-schedules.mdx b/docs/guides/c4/c4-schedules.mdx
new file mode 100644
index 000000000..5afd33c78
--- /dev/null
+++ b/docs/guides/c4/c4-schedules.mdx
@@ -0,0 +1,137 @@
+---
+description: 'C4 Schedules'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Schedule' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to write and deploy their application,
+ - App code will import the **Schedule resource** from the Nitric SDK.
+ - Developers configure the schedule with CRON or rate expressions like '7 days' and implement application logic to be executed.
+- **Operations** use default/extended or overridden Terraform modules to provision the neccessary resources to run the schedule.
+ - **Lambda function** is deployed as a packaged container image uploaded to an Amazon Elastic Container Registry (Amazon ECR).
+ - **AWS EventBridge** is used to schedule and trigger Lambda functions.
+ - **AWS IAM** provides the role/policies allowing EventBridge to invoke the Lambda functions securely.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ EventBridge["EventBridge
(AWS Scheduler)"]
+ Lambda["AWS Lambda
Deployed function(s)"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create AWS schedules| EventBridge
+ App -->|Create AWS IAM Roles / Policies| IAM
+ EventBridge -->|Invokes| Lambda
+ IAM -->|Attach Policy| Lambda
+ App -->|Deploy container app| Lambda
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **Schedule** is deployed as a Lambda function from a container image that has been packaged with either Docker or Podman and uploaded to an Amazon Elastic Container Registry (Amazon ECR) repository.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(API 1)
+ Nitric -->C(Schedule 1)
+ Nitric -->E(API 2)
+ Nitric -->F(Schedule 2)
+ Nitric -->G(...)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+Schedules Module
+
+- **aws_iam_role.role** creates the IAM role for `scheduler.amazonaws.com`.
+- **aws_iam_role_policy.role_policy** attaches the correct policy (i.e., `lambda:InvokeFunction`) to that role.
+- **aws_scheduler_schedule.schedule** configures EventBridge Scheduler with the Cron (or rate) expression, the time zone, and the role ARN.
+ - When triggered it passes an event payload (`"x-nitric-schedule": var.schedule_name`) in the request.
+
+## 4. Code (Level 4)
+
+**Developers** write application code like the following examples that import the 'schedule' resource from the SDK, configure the schedule and implement the application code that will execute.
+
+```typescript
+import { schedule } from '@nitric/sdk'
+
+// Run every 5 minutes
+schedule('process-transactions').every('5 minutes', async (ctx) => {
+ console.log(`processing at ${new Date().toLocaleString()}`)
+})
+
+// Run at 22:00 Monday through Friday.
+schedule('send-reminder').cron('0 22 * * 1-5', async (ctx) => {
+ console.log(`reminder at ${new Date().toLocaleString()}`)
+})
+```
+
+**Operations** will accept or override the default Nitric schedule Terraform provider.
+
+```hcl
+# Create role and policy to allow schedule to invoke lambda
+resource "aws_iam_role" "role" {
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17",
+ Statement = [
+ {
+ Effect = "Allow",
+ Principal = {
+ Service = "scheduler.amazonaws.com"
+ },
+ Action = "sts:AssumeRole"
+ }
+ ]
+ })
+}
+
+resource "aws_iam_role_policy" "role_policy" {
+ role = aws_iam_role.role.id
+ policy = jsonencode({
+ Version = "2012-10-17",
+ Statement = [
+ {
+ Effect = "Allow",
+ Action = "lambda:InvokeFunction",
+ Resource = var.target_lambda_arn
+ }
+ ]
+ })
+}
+
+# Create an AWS eventbridge schedule
+resource "aws_scheduler_schedule" "schedule" {
+ flexible_time_window {
+ mode = "OFF"
+ }
+
+ schedule_expression_timezone = var.schedule_timezone
+
+ schedule_expression = var.schedule_expression
+
+ target {
+ arn = var.target_lambda_arn
+ role_arn = aws_iam_role.role.arn
+
+ input = jsonencode({
+ "x-nitric-schedule": var.schedule_name
+ })
+ }
+}
+```
diff --git a/docs/guides/c4/c4-secrets.mdx b/docs/guides/c4/c4-secrets.mdx
new file mode 100644
index 000000000..91e4222c5
--- /dev/null
+++ b/docs/guides/c4/c4-secrets.mdx
@@ -0,0 +1,89 @@
+---
+description: 'C4 Secrets'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Secret' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage secrets within their application.
+ - App code imports the **Secret resource** from the Nitric SDK.
+ - Developers configure secrets and implement application logic to securely access and manage these secrets.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS Secrets Manager resources.
+ - **AWS Secrets Manager** stores and manages secrets.
+ - **AWS IAM** provides roles/policies for secure access to secrets.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ SecretsManager["AWS Secrets Manager"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create Secret| SecretsManager
+ App -->|Access Secret| IAM
+ SecretsManager -->|Store/Retrieve Secret| App
+ IAM -->|Provide Access| App
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **Secret** is managed through AWS Secrets Manager and is accessed by the application through securely configured mechanisms provided by Nitric.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->B(Secret 1)
+ Nitric -->C(Secret 2)
+ Nitric -->D(...)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+### Secrets Module
+
+- **aws_secretsmanager_secret.secret**
+ - Creates a new secret in AWS Secrets Manager.
+ - Configures the secret name and tags for identification and management.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'secret' resource from the SDK, configures the secret, and implements the application logic to access and manage secrets.
+
+```typescript
+import { secret } from '@nitric/sdk'
+
+// Define a secret with permission to put a new value
+const apiKey = secret('api-key').allow('put')
+
+// Store a new secret value
+const latestVersion = await apiKey.put('a new secret value')
+
+// Retrieve the version ID of the newly stored value
+console.log(`Secret version ID: ${latestVersion.version}`)
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS Secrets Manager secret as defined.
+
+```hcl
+# Create a new AWS Secrets Manager secret
+resource "aws_secretsmanager_secret" "secret" {
+ name = var.secret_name
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.secret_name
+ "x-nitric-${var.stack_id}-type" = "secret"
+ }
+}
+```
diff --git a/docs/guides/c4/c4-services.mdx b/docs/guides/c4/c4-services.mdx
new file mode 100644
index 000000000..693c62edb
--- /dev/null
+++ b/docs/guides/c4/c4-services.mdx
@@ -0,0 +1,234 @@
+---
+description: 'C4 Services'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Service Deployment' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to deploy and manage serverless functions within their application.
+ - App code interacts with the **Service resource** through defined functions.
+ - Developers build container images for their Lambda functions and push them to a container registry.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS resources.
+ - **AWS ECR (Elastic Container Registry)** stores container images for Lambda functions.
+ - **AWS Lambda** runs serverless functions based on the container images.
+ - **AWS IAM** manages roles and policies for secure access to AWS resources.
+ - **Docker** is used to build and tag container images before pushing them to ECR.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ ECR["AWS ECR
(Container Registry)"]
+ Lambda["AWS Lambda
(Containerized Functions)"]
+ IAM["AWS IAM"]
+ Docker["Docker
(Image Building)"]
+
+ Developer -->|Code| App
+ App -->|Build & Push Image| Docker
+ Docker -->|Push to| ECR
+ Operations -->|Terraform| App
+
+ App -->|Access ECR| IAM
+ ECR -->|Provide Image| Lambda
+ IAM -->|Manage Permissions| App
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **Service** is containerized, stored in AWS ECR, and deployed using AWS Lambda. The deployment process involves building, tagging, and pushing Docker images to ECR, followed by deploying them as Lambda functions.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->A(Function 1)
+ Nitric -->B(Function 2)
+ Nitric -->C(...)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+### Lambda Deployment Module
+
+- **terraform `{ required_providers { docker = { source = "kreuzwerker/docker" version = "3.0.2" } } }`**
+ - Configures Terraform to use the Docker provider for managing Docker images and containers.
+- **aws_ecr_repository.repo**
+ - Creates an AWS ECR repository to store container images for Lambda functions.
+- **data.aws_ecr_authorization_token.ecr_auth**
+ - Retrieves an authorization token for authenticating Docker with AWS ECR.
+- **docker_tag.tag**
+ - Tags the provided Docker image with the ECR repository URL.
+- **docker_registry_image.push**
+ - Pushes the tagged Docker image to the ECR repository.
+- **aws_iam_role.role**
+ - Creates an IAM role for the Lambda function with a trust relationship allowing Lambda to assume the role.
+- **aws_iam_role_policy.resource-list-access**
+ - Attaches a custom IAM policy to the Lambda role, granting permissions to list various AWS resources.
+- **aws_iam_role_policy_attachment.basic-execution**
+ - Attaches the AWSLambdaBasicExecutionRole managed policy to the Lambda role for basic Lambda execution permissions.
+- **aws_iam_role_policy_attachment.vpc-access**
+ - Conditionally attaches the AWSLambdaVPCAccessExecutionRole managed policy to the Lambda role if VPC subnets are provided.
+- **aws_lambda_function.function**
+ - Creates a Lambda function using the container image pushed to ECR. Configures environment variables, memory, timeout, and optionally VPC settings.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that implements handlers for the the 'api','bucket','websocket',"topic", "schedule" resources from the SDK.
+
+```typescript
+import { api } from '@nitric/sdk'
+
+const customerRoute = api('public').route(`/customers`)
+
+customerRoute.get((ctx) => {
+ // construct response for the GET: /customers request...
+ const responseBody = {}
+ ctx.res.json(responseBody)
+})
+```
+
+```typescript
+import { bucket } from '@nitric/sdk'
+
+const assets = bucket('assets')
+
+const accessibleAssets = bucket('assets').allow('delete')
+
+// The request will contain the name of the file `key` and the type of event `type`
+assets.on('delete', '*', (ctx) => {
+ console.log(`A file named ${ctx.req.key} was deleted`)
+})
+```
+
+**Operations** will use the provided Terraform module to create and manage the Service deployment as defined.
+
+```hcl
+terraform {
+ required_providers {
+ docker = {
+ source = "kreuzwerker/docker"
+ version = "3.0.2"
+ }
+ }
+}
+
+# Create an ECR repository
+resource "aws_ecr_repository" "repo" {
+ name = var.service_name
+ image_tag_mutability = "IMMUTABLE"
+}
+
+data "aws_ecr_authorization_token" "ecr_auth" {
+}
+
+# Tag the provided docker image with the ECR repository url
+resource "docker_tag" "tag" {
+ source_image = var.image
+ target_image = aws_ecr_repository.repo.repository_url
+}
+
+# Push the tagged image to the ECR repository
+resource "docker_registry_image" "push" {
+ name = aws_ecr_repository.repo.repository_url
+ triggers = {
+ source_image_id = docker_tag.tag.source_image_id
+ }
+}
+
+# Create a role for the lambda function
+resource "aws_iam_role" "role" {
+ name = var.service_name
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Effect = "Allow"
+ Principal = {
+ Service = "lambda.amazonaws.com"
+ }
+ Action = "sts:AssumeRole"
+ }
+ ]
+ })
+}
+
+# TODO Make a common policy and attach separately
+# as a base common compute policy
+resource "aws_iam_role_policy" "resource-list-access" {
+ name = "resource-list-access"
+ role = aws_iam_role.role.name
+
+ policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Effect = "Allow"
+ Action = [
+ "sns:ListTopics",
+ "sqs:ListQueues",
+ "dynamodb:ListTables",
+ "s3:ListAllMyBuckets",
+ "tag:GetResources",
+ "apigateway:GET",
+ ]
+ Resource = "*"
+ }
+ ]
+ })
+}
+
+
+resource "aws_iam_role_policy_attachment" "basic-execution" {
+ role = aws_iam_role.role.name
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+}
+
+# Attach vpc access execution role if subnets are provided
+resource "aws_iam_role_policy_attachment" "vpc-access" {
+ count = length(var.subnet_ids) > 0 ? 1 : 0
+ role = aws_iam_role.role.name
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
+}
+
+# Create a lambda function using the pushed image
+resource "aws_lambda_function" "function" {
+ function_name = "${var.service_name}-${var.stack_id}"
+ role = aws_iam_role.role.arn
+ image_uri = "${aws_ecr_repository.repo.repository_url}@${docker_registry_image.push.sha256_digest}"
+ package_type = "Image"
+ timeout = var.timeout
+ memory_size = var.memory
+ ephemeral_storage {
+ size = var.ephemeral_storage
+ }
+ environment {
+ variables = var.environment
+ }
+
+
+ dynamic "vpc_config" {
+ for_each = length(var.subnet_ids) > 0 ? ["1"] : []
+ content {
+ subnet_ids = var.subnet_ids
+ security_group_ids = var.security_group_ids
+ }
+ }
+
+ depends_on = [docker_registry_image.push]
+
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.service_name,
+ "x-nitric-${var.stack_id}-type" = "service",
+ }
+}
+```
diff --git a/docs/guides/c4/c4-topics.mdx b/docs/guides/c4/c4-topics.mdx
new file mode 100644
index 000000000..bd0bdb3c8
--- /dev/null
+++ b/docs/guides/c4/c4-topics.mdx
@@ -0,0 +1,201 @@
+---
+description: 'C4 Topics'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'Topic' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to manage messaging and event-driven communication within their application.
+ - App code interacts with the **SNS Topic resource** through defined topics and subscriptions.
+ - Developers configure SNS topics and implement application logic to publish and consume messages.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS SNS resources.
+ - **AWS SNS (Simple Notification Service)** serves as the messaging and event notification service.
+ - **AWS Lambda** functions are subscribed to SNS topics to process incoming messages.
+ - **AWS IAM** manages roles and policies for secure access to SNS topics and Lambda functions.
+ - **AWS Step Functions** orchestrate workflows that interact with SNS topics.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ SNSTopic["AWS SNS Topic"]
+ Lambda["AWS Lambda Functions"]
+ IAM["AWS IAM"]
+ StepFunctions["AWS Step Functions"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create SNS Topic| SNSTopic
+ App -->|Configure Subscriptions| SNSTopic
+ App -->|Publish Messages| StepFunctions
+ SNSTopic -->|Invoke Lambda| Lambda
+ App -->|Manage Permissions| IAM
+ StepFunctions -->|Orchestrate Workflows| SNSTopic
+ Lambda -->|Process Messages| App
+ IAM -->SNSTopic
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 2. Container (Level 2)
+
+Each **SNS Topic** is managed through AWS SNS and interacts with subscribed AWS Lambda functions and AWS Step Functions to handle event-driven workflows.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->A(SNS Topic 1)
+ Nitric -->B(SNS Topic 2)
+ Nitric -->C(...)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+### SNS Topic Module
+
+- **random_id.topic_id**
+ - Generates a unique random ID for each SNS topic to ensure unique naming.
+- **aws_sns_topic.topic**
+ - Creates an AWS SNS topic with a unique name by appending the generated random ID.
+ - Configures tags for identification and management.
+- **aws_sns_topic_subscription.subscription**
+ - Subscribes Lambda functions to the SNS topic.
+ - Iterates over `var.lambda_subscribers` to create subscriptions for each Lambda endpoint.
+- **aws_lambda_permission.sns**
+ - Grants SNS permission to invoke the specified Lambda functions.
+ - Iterates over `var.lambda_subscribers` to set permissions for each target function.
+- **aws_iam_role.sns_publish_role**
+ - Creates an IAM role for AWS Step Functions to publish messages to the SNS topic.
+ - Defines a trust relationship allowing Step Functions to assume the role.
+- **aws_iam_role_policy.publish_policy**
+ - Attaches an inline policy to the SNS publish role, granting permissions to publish messages to the SNS topic.
+- **aws_sfn_state_machine.publish_to_topic**
+ - Creates an AWS Step Functions state machine that publishes messages to the SNS topic.
+ - Defines the workflow with a wait state followed by a publish task.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'topic' resource from the SDK, and implements the application logic to publish and subscript to topics.
+
+```typescript
+import { topic } from '@nitric/sdk'
+
+const updates = topic('updates').allow('publish')
+
+await updates.publish({
+ something: 'amazing happened',
+})
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS Secrets Manager secret as defined.
+
+```hcl
+# Generate a random id for the topic
+resource "random_id" "topic_id" {
+ byte_length = 8
+
+ keepers = {
+ # Generate a new id each time we switch to a new name
+ topic_name = var.topic_name
+ }
+}
+
+
+# AWS SNS Topic
+resource "aws_sns_topic" "topic" {
+ name = "${var.topic_name}-${random_id.topic_id.hex}"
+
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.topic_name
+ "x-nitric-${var.stack_id}-type" = "topic"
+ }
+}
+
+# Loop over the subsribers and deploy subscriptions and permissions
+resource "aws_sns_topic_subscription" "subscription" {
+ for_each = var.lambda_subscribers
+
+ topic_arn = aws_sns_topic.topic.arn
+ protocol = "lambda"
+ endpoint = each.value
+}
+
+resource "aws_lambda_permission" "sns" {
+ for_each = var.lambda_subscribers
+
+ action = "lambda:InvokeFunction"
+ function_name = each.value
+ principal = "sns.amazonaws.com"
+ source_arn = aws_sns_topic.topic.arn
+}
+
+resource "aws_iam_role" "sns_publish_role" {
+ name = "${var.topic_name}-sns-publish-role"
+
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Effect = "Allow"
+ Principal = {
+ Service = "states.amazonaws.com"
+ }
+ Action = "sts:AssumeRole"
+ }
+ ]
+ })
+}
+
+# Attach the policy to the role inline
+resource "aws_iam_role_policy" "publish_policy" {
+ role = aws_iam_role.sns_publish_role.id
+
+ # Terraform's "jsonencode" function converts a
+ # Terraform expression result to valid JSON syntax.
+ policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Effect = "Allow"
+ Action = "sns:Publish"
+ Resource = aws_sns_topic.topic.arn
+ }
+ ]
+ })
+}
+
+# Create a step function that publishes to the topic
+resource "aws_sfn_state_machine" "publish_to_topic" {
+ name = "${var.topic_name}-publish-to-topic"
+ role_arn = aws_iam_role.sns_publish_role.arn
+ definition = jsonencode({
+ Comment = "",
+ StartAt = "Wait",
+ States = {
+ Wait = {
+ Type = "Wait",
+ SecondsPath : "$.seconds",
+ Next = "Publish"
+ }
+ Publish = {
+ Type = "Task",
+ Resource = "arn:aws:states:::sns:publish",
+ Parameters = {
+ TopicArn = aws_sns_topic.topic.arn,
+ "Message.$" : "$.message",
+ },
+ End = true
+ }
+ }
+ })
+}
+```
diff --git a/docs/guides/c4/c4-websockets.mdx b/docs/guides/c4/c4-websockets.mdx
new file mode 100644
index 000000000..f1015c28a
--- /dev/null
+++ b/docs/guides/c4/c4-websockets.mdx
@@ -0,0 +1,207 @@
+---
+description: 'C4'
+tags:
+ - C4
+published_at: 2024-07-25
+---
+
+# Nitric 'WebSocket API Gateway' Architecture
+
+## 1. System Context (Level 1)
+
+- A **Developer** uses Nitric to create and manage WebSocket APIs within their application.
+ - App code interacts with the **WebSocket API resource** through defined routes and integrations.
+ - Developers implement backend logic to handle WebSocket connections, messages, and disconnections.
+- **Operations** use default or overridden Terraform modules to provision the necessary AWS WebSocket API resources.
+ - **AWS API Gateway v2** manages WebSocket API endpoints and routes.
+ - **AWS Lambda** functions handle WebSocket events such as connection, message reception, and disconnection.
+ - **AWS IAM** manages roles and policies for secure access between API Gateway and Lambda functions.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ Operations["Operations"]
+ App["nitric up"]
+ APIGateway["AWS API Gateway v2
(WebSocket API)"]
+ Lambda["AWS Lambda Functions"]
+ IAM["AWS IAM"]
+
+ Developer -->|Code| App
+ Operations -->|Terraform| App
+ App -->|Create WebSocket API| APIGateway
+ App -->|Configure Integrations| APIGateway
+ App -->|Deploy Lambda Functions| Lambda
+ APIGateway -->|Invoke Lambda| Lambda
+ App -->|Manage Permissions| IAM
+ IAM -->Lambda
+ IAM -->APIGateway
+ Lambda -->|Handle Events| App
+```
+
+## 2. Container (Level 2)
+
+The system comprises AWS API Gateway v2 managing WebSocket APIs, which interact with AWS Lambda functions to handle connection, message, and disconnection events.
+
+```mermaid
+flowchart TD
+ Nitric["Nitric Application"]
+ Nitric -->A(WebSocket API 1)
+ Nitric -->B(WebSocket API 2)
+ Nitric -->C(...)
+ A(WebSocket API 1) -->D(onConnect)
+ A(WebSocket API 1) -->E(onDisconnect)
+ A(WebSocket API 1) -->F(onMessage)
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+## 3. Component (Level 3)
+
+### WebSocket API Module
+
+- **random_id.topic_id**
+ - Generates a unique random ID for each SNS topic to ensure unique naming.
+- **aws_apigatewayv2_api.websocket**
+ - Creates an AWS API Gateway v2 WebSocket API.
+ - Configures the API name, protocol type, route selection expression, and tags for identification and management.
+- **aws_apigatewayv2_integration.default**
+ - Creates an integration for the `$default` route, linking it to a Lambda function.
+- **aws_apigatewayv2_integration.connect**
+ - Creates an integration for the `$connect` route, linking it to a Lambda function.
+- **aws_apigatewayv2_integration.disconnect**
+ - Creates an integration for the `$disconnect` route, linking it to a Lambda function.
+- **aws_apigatewayv2_route.default**
+ - Creates the default route for the WebSocket API, targeting the default integration.
+- **aws_apigatewayv2_route.connect**
+ - Creates the `$connect` route for the WebSocket API, targeting the connect integration.
+ - Depends on the default route to prevent concurrent edit conflicts.
+- **aws_apigatewayv2_route.disconnect**
+ - Creates the `$disconnect` route for the WebSocket API, targeting the disconnect integration.
+ - Depends on the connect route to prevent concurrent edit conflicts.
+- **aws_lambda_permission.websocket-message**
+ - Grants API Gateway permission to invoke the Lambda function handling messages.
+- **aws_lambda_permission.websocket-connect**
+ - Grants API Gateway permission to invoke the Lambda function handling connections.
+- **aws_lambda_permission.websocket-disconnect**
+ - Grants API Gateway permission to invoke the Lambda function handling disconnections.
+- **aws_apigatewayv2_stage.stage**
+ - Creates a stage for the WebSocket API, enabling automatic deployment of changes.
+ - Configures tags for identification and management.
+
+## 4. Code (Level 4)
+
+**Developers** write application code that imports the 'websocket' resource from the SDK and implement backend logic to handle WebSocket connections, messages, and disconnections.
+
+```typescript
+import { websocket } from '@nitric/sdk'
+
+const socket = websocket('socket')
+
+socket.on('connect', async (ctx) => {
+ // handle connections
+})
+
+socket.on('disconnect', async (ctx) => {
+ // handle disconnections
+})
+
+socket.on('message', async (ctx) => {
+ // handle messages
+})
+```
+
+**Operations** will use the provided Terraform module to create and manage the AWS Secrets Manager secret as defined.
+
+```hcl
+# Deploy a websocket API gateway
+
+resource "aws_apigatewayv2_api" "websocket" {
+ name = var.websocket_name
+ protocol_type = "WEBSOCKET"
+ route_selection_expression = "$request.body.action"
+ tags = {
+ "x-nitric-${var.stack_id}-name" = var.websocket_name
+ "x-nitric-${var.stack_id}-type" = "websocket"
+ }
+}
+
+resource "aws_apigatewayv2_integration" "default" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ integration_type = "AWS_PROXY"
+ integration_uri = var.lambda_message_target
+}
+
+# Create an integration for the connect route
+resource "aws_apigatewayv2_integration" "disconnect" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ integration_type = "AWS_PROXY"
+ integration_uri = var.lambda_disconnect_target
+}
+
+# Create an integration for the connect route
+resource "aws_apigatewayv2_integration" "connect" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ integration_type = "AWS_PROXY"
+ integration_uri = var.lambda_connect_target
+}
+
+# Create the default route for the websocket
+resource "aws_apigatewayv2_route" "default" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ route_key = "$default"
+ target = "integrations/${aws_apigatewayv2_integration.default.id}"
+}
+
+# Create the connect route for the websocket
+resource "aws_apigatewayv2_route" "connect" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ route_key = "$connect"
+ target = "integrations/${aws_apigatewayv2_integration.connect.id}"
+ # Chain routes to prevent Concurrent edit conflict exceptions
+ depends_on = [ aws_apigatewayv2_route.default ]
+}
+
+# Create the disconnect route for the websocket
+resource "aws_apigatewayv2_route" "disconnect" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ route_key = "$disconnect"
+ target = "integrations/${aws_apigatewayv2_integration.disconnect.id}"
+ # Chain routes to prevent Concurrent edit conflict exceptions
+ depends_on = [ aws_apigatewayv2_route.connect ]
+}
+
+# Create execution lambda permissions for the websocket
+resource "aws_lambda_permission" "websocket-message" {
+ action = "lambda:InvokeFunction"
+ function_name = var.lambda_message_target
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_apigatewayv2_api.websocket.execution_arn}/*/*"
+}
+
+resource "aws_lambda_permission" "websocket-connect" {
+ action = "lambda:InvokeFunction"
+ function_name = var.lambda_connect_target
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_apigatewayv2_api.websocket.execution_arn}/*/*"
+}
+
+resource "aws_lambda_permission" "websocket-disconnect" {
+ action = "lambda:InvokeFunction"
+ function_name = var.lambda_disconnect_target
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_apigatewayv2_api.websocket.execution_arn}/*/*"
+}
+
+# create a stage for the api gateway
+resource "aws_apigatewayv2_stage" "stage" {
+ api_id = aws_apigatewayv2_api.websocket.id
+ name = "ws"
+ auto_deploy = true
+
+ tags = {
+ "x-nitric-${var.stack_id}-name" = "${var.websocket_name}DefaultStage"
+ "x-nitric-${var.stack_id}-type" = "websocket"
+ }
+}
+```
diff --git a/docs/guides/terraform/docs.code-workspace b/docs/guides/terraform/docs.code-workspace
new file mode 100644
index 000000000..9eb416af5
--- /dev/null
+++ b/docs/guides/terraform/docs.code-workspace
@@ -0,0 +1,11 @@
+{
+ "folders": [
+ {
+ "path": "../../.."
+ },
+ {
+ "path": "../../../../nitric"
+ }
+ ],
+ "settings": {}
+}
\ No newline at end of file