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.

  • Cloud Platforms
  • $aws-sqs-sns
  • 187 lines
  • 2 files
  • ~2,100 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- aws-sqs-sns

Installs to ~/.agents/skills/aws-sqs-sns/ for every project on this machine.

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

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.

~/.agents/skills/aws-sqs-sns/SKILL.md

--- name: aws-sqs-sns description: Designs Amazon SQS and SNS messaging - standard vs FIFO queues, visibility timeout and long polling, dead-letter queues with redrive, message groups… license: MIT ---

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

  1. Read AGENTS.md and the IaC defining queues, topics, subscriptions and consumers.
  2. 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

NeedUse
One producer, one consumer group, work queueSQS standard queue
Strict order per entity (per account, per order) or exactly-once processing within 5 minutesSQS FIFO with MessageGroupId = entity ID
One event, many independent consumersSNS topic fanning out to one SQS queue per consumer
Content-based routing across many services or accounts, schema registry, replayEventBridge (see $aws-eventbridge)
Stream with replay and many readers at high throughputKinesis 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

SettingRule
VisibilityTimeoutAt least the consumer’s maximum processing time; for Lambda consumers 6 times the function timeout. Max 12 hours
ReceiveMessageWaitTimeSeconds20 (long polling). Cuts empty receives and cost
MessageRetentionPeriodDefault 4 days, max 14. Set the DLQ’s retention longer than the source queue’s
RedrivePolicy.maxReceiveCount3-5 for most work. 1 sends anything that fails once (including throttling) to the DLQ
Message sizeUp 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
EncryptionSSE-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 ChangeMessageVisibility as a heartbeat rather than setting a huge default.
  • Batch: ReceiveMessage up to 10 messages, DeleteMessageBatch and SendMessageBatch up to 10 entries. Batch calls can partially fail; check the Failed list, 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

  • MessageGroupId is 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 enable ContentBasedDeduplication. 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 maxReceiveCount low 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.com to kms:GenerateDataKey and kms:Decrypt. The AWS-managed aws/sqs key 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 RawMessageDelivery for SQS subscribers unless they need the SNS envelope; otherwise every consumer must unwrap Message from 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 (RedrivePolicy on the subscription) to capture messages SNS could not deliver after its retries.

DLQ operations

  • Alarm on ApproximateNumberOfMessagesVisible > 0 on 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.
  • ApproximateNumberOfMessages is 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 maxReceiveCount receives while other messages succeed.
  • Send the same message twice and confirm the side effect happens once.
  • Check NumberOfNotificationsFailed on the topic stays at 0.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

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.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem