Skip to main content
pn-paper-channel is deployed on AWS using two CloudFormation stacks:
  • storage.yml — provisions all stateful resources (DynamoDB tables, SQS queues, KMS key, log groups).
  • microservice.yml — provisions the ECS Fargate service, IAM policies, EventBridge pipes, Lambda, and API Gateway.
The microservice-dev-cfg.json file contains the parameter values used in the dev environment.

DynamoDB

Fourteen tables store request state, addresses, tender configuration, costs, events, and more.

SQS

Multiple queues handle internal scheduling, external channel inputs, delivery push outputs, and OCR.

KMS

A symmetric AES-256-GCM key encrypts address data at rest in DynamoDB.

EventBridge

PaperChannelOutcomeEvent events are published to the pn-CoreEventBus. An EventBridge Pipe bridges the prepare-phase queue to the delayer.

Lambda

pn-paper-channel-TenderAPI provides read-only access to tender data without going through the main ECS service.

IAM

A managed policy grants the ECS task role least-privilege access to SQS, DynamoDB, KMS, EventBridge, and CloudWatch.

DynamoDB tables

All tables use PAY_PER_REQUEST billing mode and have point-in-time recovery enabled. Changes are streamed to Kinesis for CDC.
CloudFormation resourceTable name (production)Partition keySort keyPurpose
RequestDeliveryDynamoTable{ProjectName}-PaperRequestDeliveryrequestIdMain delivery request records. GSIs on fiscalCode and correlationId.
AddressDynamoTable{ProjectName}-PaperAddressrequestIdaddressTypePer-request addresses. KMS-encrypted at rest. TTL enabled.
TenderDynamoTable{ProjectName}-PaperTendertenderCodeLegacy tender records. GSI on author+date.
DeliveryDriverDynamoTable{ProjectName}-PaperDeliveryDrivertenderCodetaxIdLegacy delivery driver records. GSIs on author+startDate and tenderCode.
CostDynamoTable{ProjectName}-PaperCostdriverCodeuuidCodeLegacy cost records per driver. GSI on tenderCode.
ZoneDynamoTable{ProjectName}-PaperZonecountryItCountry-to-zone mapping. GSI on countryEn.
CapDynamoTable{ProjectName}-PaperCapauthorcapItalian CAP to lot mapping.
DeliveryFileDynamoTable{ProjectName}-PaperDeliveryFileuuidTracks tender file upload status.
PaperRequestErrorDynamoTable{ProjectName}-PaperRequestErrorrequestIdcreatedRequest processing errors. GSIs on author+created and category+cause.
PaperEventsDynamoTable{ProjectName}-PaperEventspkskPaper delivery events. TTL enabled.
ClientDynamoTable{ProjectName}-ClientsclientIdClient ID to prefix-value mapping. GSI on prefixValue.
AttachmentsConfigDynamoTable{ProjectName}-AttachmentsConfigconfigKeystartValidityTime-based attachment rule configuration. TTL enabled.
PaperEventErrorDynamoTable{ProjectName}-PaperEventErrorrequestIdstatusBusinessDateTimeOut-of-order event errors. GSI on flowType. TTL enabled.
PaperChannelTenderDynamoTable{ProjectName}-PaperChannelTendertenderIdSimplified-flow tender records.
PaperChannelGeokeyDynamoTable{ProjectName}-PaperChannelGeokeytenderProductGeokeyactivationDateGeokey versions per tender and product.
PaperChannelDeliveryDriverDynamoTable{ProjectName}-PaperChannelDeliveryDriverdeliveryDriverIdSimplified-flow delivery driver records.
PaperChannelCostDynamoTable{ProjectName}-PaperChannelCosttenderIdproductLotZoneSimplified-flow costs per tender, product, lot, and zone.

SQS queues

Internal queues (owned by this microservice)

CloudFormation resourceQueue nameVisibility timeoutPurpose
ScheduledRequestsQueue{ProjectName}-paper_channel_requests60 sInternal scheduling queue for delivery requests.
PaperNormalizeAddressQueue{ProjectName}-paper-normalize-address60 sInternal queue driving PREPARE phase 1 (address normalization).
PaperChannelOcrOutputsQueue{ProjectName}-paper_channel_ocr_outputs60 sReceives OCR response messages.
ExternalChannelToPaperChannelDryRunQueue{ProjectName}-external_channel_to_paper_channel_dry_run60 sReceives dry-run messages proxied from External Channel.

External queues (consumed or produced)

PropertyDirectionRemote service
pn.paper-channel.queue-external-channelConsume (input)External Channel — paper tracking events
pn.paper-channel.queue-national-registriesConsume (input)National Registries — address lookup responses
pn.paper-channel.queue-f24Consume (input)F24 — PDF generation responses
pn.paper-channel.queue-radd-altConsume (input)RADD Alt — alternative delivery events
pn.paper-channel.queue-delayer-to-paperchannelConsume (input)pn-delayer — delayed prepare-phase events
pn.paper-channel.queue-delivery-pushProduce (output)Delivery Push — notification status updates
pn.paper-channel.queue-paperchannel-to-delayerProduce (output)pn-delayer via EventBridge Pipe
pn.paper-channel.queue-url-ocr-inputsProduce (output)OCR service in a separate AWS account

KMS key

pn-paper-channel provisions a symmetric AES-256-GCM KMS key (PCKmsEncDecDynamoDataKey) with key rotation enabled. The key alias is alias/{ProjectName}-paper-channel-dynamo. The key is used to encrypt the AddressDynamoTable at rest (SSESpecification). The ECS task role has kms:Encrypt, kms:Decrypt, and kms:ReEncrypt* permissions on this key. For local development, retrieve the LocalStack KMS key ARN and set it in config/application.properties:
aws.kms.keyId=arn:aws:kms:us-east-1:000000000000:key/<localstack-key-id>

EventBridge

PaperChannelOutcomeEvent

The service publishes events to the {ProjectName}-CoreEventBus with:
  • detail-type: PaperChannelOutcomeEvent
  • source: pn-paper-channel
These properties are controlled by pn.paper-channel.eventbus.detail.type and pn.paper-channel.eventbus.source.

Prepare-phase EventBridge Pipe

An AWS::Pipes::Pipe resource (PaperChannelPrepareToDelayerQueuePipe) bridges the PaperChannelToDelayerQueue to CoreEventBus with detail-type: PreparePhaseOneOutcomeEvent. The pipe desired state is controlled by the PaperChannelPrepareToDelayerQueuePipeDesiredState parameter (RUNNING or STOPPED).

Lambda: TenderAPI

The {ProjectName}-paper-channel-TenderAPI Lambda (runtime: nodejs24.x, timeout: 30 s, memory: 512 MB) provides read-only access to tender data directly from DynamoDB. It is invoked using the AWS CLI or SDK and does not expose an HTTP endpoint. See the Tender management guide for the full payload API.

IAM

The PaperChannelMicroserviceTaskPolicy managed policy grants the ECS task role:
# SQS — all internal, input, and output queues
- sqs:ChangeMessageVisibility, ReceiveMessage, DeleteMessage,
  GetQueueAttributes, GetQueueUrl, SendMessage (and batch variants)

# EventBridge
- events:PutEvents on {ProjectName}-CoreEventBus

# KMS
- kms:Encrypt, kms:Decrypt, kms:ReEncrypt* on PCKmsEncDecDynamoDataKey

# DynamoDB — all tables and their indexes
- dynamodb:BatchGetItem, BatchWriteItem, ExecuteTransaction, GetItem,
  PutItem, DeleteItem, Query, TransactGetItems, TransactWriteItems,
  UpdateItem, Scan

# CloudWatch
- cloudwatch:PutMetricData, GetMetricStream, GetMetricData on *
The AddressManagerApiKey is stored in AWS Secrets Manager under the secret name pn-PaperChannel-Secrets and is injected into the container via ContainerSecret1.

CloudFormation deployment

Deploy the storage stack first, then the microservice stack. Pass the storage stack outputs as parameters to the microservice stack. Example using the AWS CLI:
# Deploy storage
aws cloudformation deploy \
  --template-file scripts/aws/cfn/storage.yml \
  --stack-name pn-paper-channel-storage \
  --parameter-overrides ProjectName=pn ...

# Deploy microservice (pass storage outputs as parameters)
aws cloudformation deploy \
  --template-file scripts/aws/cfn/microservice.yml \
  --stack-name pn-paper-channel \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides \
    ProjectName=pn \
    ContainerImageUri=<ecr-uri> \
    PCKmsEncDecDynamoDataKeyARN=<from-storage-outputs> \
    RequestDeliveryDynamoTableName=<from-storage-outputs> \
    ...
For the full list of parameters, see scripts/aws/cfn/microservice.yml and scripts/aws/cfn/microservice-dev-cfg.json.

Build docs developers (and LLMs) love