AWS SQS and SNS skill for Codex
Queues that never lose or double-charge. Codex picks between standard queues, FIFO, SNS fan-out and EventBridge from the ordering and consumer needs, then sets visibility timeout, retention and maxReceiveCount from real processing times. It writes the queue and KMS policies that SNS delivery silently depends on, builds idempotent consumers with batch error checks, and treats DLQ redrive and purge as confirmed operations.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- aws-sqs-sns Installs to ~/.agents/skills/aws-sqs-sns/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install aws-sqs-sns The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project aws-sqs-sns Installs to .agents/skills/aws-sqs-sns/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/aws-sqs-sns.zipunzip -o aws-sqs-sns.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) aws-sqs-sns PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $aws-sqs-sns, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Designs Amazon SQS and SNS messaging - standard vs FIFO queues, visibility timeout and long polling, dead-letter queues with redrive, message groups and deduplication, SNS fan-out to SQS with subscription filter policies, raw message delivery, KMS and queue policy permissions, and idempotent consumers. Use when adding a queue or topic, messages are processed twice or lost, a DLQ is filling up, ordering matters, SNS deliveries to a queue silently fail, or replacing synchronous calls with async messaging.
Use it when
- Adding a queue, topic or fan-out between services
- Duplicate, lost or out-of-order message processing
- A dead-letter queue is filling up
- SNS deliveries to an encrypted queue fail silently
Use something else when
- You need content-based routing, schedules or pipes across many services: use aws-eventbridge
- The broker is RabbitMQ rather than AWS: use rabbitmq-messaging
How to check the result
A forced consumer failure lands only the bad message in the DLQ, a duplicate send produces one side effect, and each subscriber receives exactly the messages its filter policy allows.
The mistake it prevents
Encrypting the subscriber queue with the AWS-managed aws/sqs key, which SNS cannot use, so fan-out deliveries fail without the publisher noticing.
Done well means: every consumer is idempotent, every queue has a DLQ with an alarm and a redrive procedure, timeouts are derived from processing time rather than defaults, and the producer, topic, queue and consumer permissions are scoped so that delivery cannot fail silently.
Before changing anything
- Read
AGENTS.mdand the IaC defining queues, topics, subscriptions and consumers. - For an existing system, inspect attributes (needs network and credentials):
Q=$(aws sqs get-queue-url --queue-name orders --query QueueUrl --output text)
aws sqs get-queue-attributes --queue-url $Q --attribute-names All
aws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:eu-west-1:111111111111:order-events
aws sns get-subscription-attributes --subscription-arn <sub-arn>
Look at VisibilityTimeout, MessageRetentionPeriod, RedrivePolicy,
ReceiveMessageWaitTimeSeconds, KmsMasterKeyId, ApproximateNumberOfMessages*, and on
subscriptions RawMessageDelivery, FilterPolicy, RedrivePolicy.
Choosing the pattern
| Need | Use |
|---|---|
| One producer, one consumer group, work queue | SQS standard queue |
| Strict order per entity (per account, per order) or exactly-once processing within 5 minutes | SQS FIFO with MessageGroupId = entity ID |
| One event, many independent consumers | SNS topic fanning out to one SQS queue per consumer |
| Content-based routing across many services or accounts, schema registry, replay | EventBridge (see $aws-eventbridge) |
| Stream with replay and many readers at high throughput | Kinesis or Kafka, not SQS |
Prefer SNS to SQS to consumer over SNS directly to Lambda or HTTP: the queue gives buffering, retries under your control, a DLQ and backpressure.
Queue settings
| Setting | Rule |
|---|---|
VisibilityTimeout | At least the consumer’s maximum processing time; for Lambda consumers 6 times the function timeout. Max 12 hours |
ReceiveMessageWaitTimeSeconds | 20 (long polling). Cuts empty receives and cost |
MessageRetentionPeriod | Default 4 days, max 14. Set the DLQ’s retention longer than the source queue’s |
RedrivePolicy.maxReceiveCount | 3-5 for most work. 1 sends anything that fails once (including throttling) to the DLQ |
| Message size | Up to 1 MiB on SQS. Check the current SNS publish limit (256 KB at time of writing) before fanning out large messages; store big payloads in S3 and send a pointer |
| Encryption | SSE-SQS by default, or SSE-KMS with a customer-managed key when you need key policy control |
Standard queue DLQs must be standard; FIFO DLQs must be FIFO. A standard queue DLQ keeps the original enqueue timestamp, so a message near the end of its retention can expire from the DLQ almost immediately; hence the longer DLQ retention.
{
"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:eu-west-1:111111111111:orders-dlq\",\"maxReceiveCount\":\"5\"}"
}
Consumer rules
- At-least-once delivery on standard queues and on SNS: the same message can arrive twice. Make processing idempotent with a dedupe key (business ID, or message ID stored with a TTL in DynamoDB/Redis) or a conditional write.
- Delete the message only after the work is committed. Deleting first loses messages on crash.
- If processing may exceed the visibility timeout, extend it with
ChangeMessageVisibilityas a heartbeat rather than setting a huge default. - Batch:
ReceiveMessageup to 10 messages,DeleteMessageBatchandSendMessageBatchup to 10 entries. Batch calls can partially fail; check theFailedlist, not just the HTTP status. - For Lambda consumers enable partial batch responses (
ReportBatchItemFailures); see$aws-lambda.
import boto3, json
sqs = boto3.client("sqs")
def poll(queue_url: str) -> None:
while True:
resp = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=10,
WaitTimeSeconds=20, MessageSystemAttributeNames=["ApproximateReceiveCount"])
done = []
for m in resp.get("Messages", []):
try:
handle(json.loads(m["Body"])) # idempotent
done.append({"Id": m["MessageId"], "ReceiptHandle": m["ReceiptHandle"]})
except RetryableError:
pass # becomes visible again after timeout
if done:
r = sqs.delete_message_batch(QueueUrl=queue_url, Entries=done)
for f in r.get("Failed", []):
log.warning("delete failed %s", f)
FIFO specifics
MessageGroupIdis the unit of ordering and of parallelism. One group processes one message at a time; a single hot group serializes everything. Use the entity ID, never a constant.- Deduplication: pass
MessageDeduplicationId(preferred, from a business key) or enableContentBasedDeduplication. The dedupe window is 5 minutes; it does not protect against duplicates sent later. - A failing message blocks its group until it succeeds or moves to the DLQ, so keep
maxReceiveCountlow on FIFO queues. - Enable high-throughput mode (
DeduplicationScope=messageGroup,FifoThroughputLimit=perMessageGroupId) when throughput matters.
SNS fan-out
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowOrderEventsTopic",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:eu-west-1:111111111111:billing-orders",
"Condition": { "ArnEquals": { "aws:SourceArn": "arn:aws:sns:eu-west-1:111111111111:order-events" } }
}]
}
- Each subscribed queue needs this queue policy. CDK’s
topic.addSubscription(new SqsSubscription(queue))writes it for you. - If the queue uses SSE-KMS, the key policy must allow
sns.amazonaws.comtokms:GenerateDataKeyandkms:Decrypt. The AWS-managedaws/sqskey cannot be used for this; use SSE-SQS or a customer-managed key. Missing KMS permissions make deliveries fail silently from the producer’s point of view. - Turn on
RawMessageDeliveryfor SQS subscribers unless they need the SNS envelope; otherwise every consumer must unwrapMessagefrom JSON. - Filter at the subscription so consumers only receive what they handle:
{
"eventType": ["order.created", "order.cancelled"],
"amount": [{ "numeric": [">=", 1000] }],
"region": [{ "anything-but": ["test"] }]
}
Set FilterPolicyScope to MessageBody to filter on JSON body fields; the default scope
filters on message attributes, which the publisher must set.
- Add a subscription-level DLQ (
RedrivePolicyon the subscription) to capture messages SNS could not deliver after its retries.
DLQ operations
- Alarm on
ApproximateNumberOfMessagesVisible > 0on every DLQ. - Inspect before redriving: read a few messages, find the cause, deploy the fix.
- Redrive back to the source queue:
aws sqs start-message-move-task \
--source-arn arn:aws:sqs:eu-west-1:111111111111:orders-dlq \
--max-number-of-messages-per-second 50
aws sqs list-message-move-tasks --source-arn arn:aws:sqs:eu-west-1:111111111111:orders-dlq
Redriving re-triggers side effects in consumers (emails, charges). Confirm with the user
before redriving production messages, and before purge-queue, which deletes every message.
Gotchas
- Short polling (the default
WaitTimeSeconds=0) samples a subset of servers and returns empty even when messages exist. Use long polling. ApproximateNumberOfMessagesis approximate; never use it for correctness.- Delay queues and per-message delay max out at 15 minutes; use EventBridge Scheduler for longer delays.
- Publishing many messages from a request handler without batching adds latency; use
PublishBatch/SendMessageBatch(10 entries). - Cross-account fan-out needs the topic policy to allow the subscriber account and the queue policy to allow the topic ARN.
Verification
- Send a test message and confirm each subscriber receives exactly the filtered set.
- Make the consumer fail for one message and confirm it reaches the DLQ after
maxReceiveCountreceives while other messages succeed. - Send the same message twice and confirm the side effect happens once.
- Check
NumberOfNotificationsFailedon the topic stays at 0.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "AWS SQS and SNS"
short_description: "Queues that never lose or double-charge"
default_prompt: "$aws-sqs-sns Fan out order events from SNS to billing and shipping queues with filters" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.