Skip to content

SNS

Protocol: Query (XML) and JSON 1.0 (both supported) Endpoint: POST http://localhost:4566/

Supported Actions

Action Description
CreateTopic Create a topic
DeleteTopic Delete a topic
ListTopics List all topics
GetTopicAttributes Get topic configuration
SetTopicAttributes Update topic configuration
Subscribe Subscribe an endpoint (SQS, HTTP, Lambda, email)
Unsubscribe Remove a subscription
ListSubscriptions List all subscriptions
ListSubscriptionsByTopic List subscriptions for a specific topic
GetSubscriptionAttributes Get subscription settings
SetSubscriptionAttributes Update subscription settings
ConfirmSubscription Confirm a pending subscription
Publish Publish a message to a topic
PublishBatch Publish up to 10 messages in one call
TagResource Tag a topic
UntagResource Remove tags from a topic
ListTagsForResource List tags on a topic
CreatePlatformApplication Create a mobile push platform app (iOS or Android)
DeletePlatformApplication Delete a platform app and its endpoints
GetPlatformApplicationAttributes Read platform app attributes
SetPlatformApplicationAttributes Update platform app attributes (e.g. Enabled)
ListPlatformApplications List platform applications in the region
CreatePlatformEndpoint Register a device token under a platform app
DeleteEndpoint Delete a platform endpoint
GetEndpointAttributes Read endpoint attributes
SetEndpointAttributes Update endpoint attributes (e.g. Enabled=false to simulate token expiry)
ListEndpointsByPlatformApplication List endpoints under a platform app

Configuration

Variable Default Description
FLOCI_SERVICES_SNS_ENABLED true Enable or disable the service
FLOCI_STORAGE_SERVICES_SNS_MODE (global default) Storage mode override for SNS (memory, persistent, hybrid, wal)
FLOCI_STORAGE_SERVICES_SNS_FLUSH_INTERVAL_MS 5000 Flush interval for hybrid/wal storage modes (milliseconds)

Examples

export AWS_ENDPOINT_URL=http://localhost:4566

# Create a topic
TOPIC_ARN=$(aws sns create-topic --name notifications \
  --query TopicArn --output text \
  --endpoint-url $AWS_ENDPOINT_URL)

# Subscribe an SQS queue
QUEUE_ARN=$(aws sqs get-queue-attributes \
  --queue-url $AWS_ENDPOINT_URL/000000000000/orders \
  --attribute-names QueueArn \
  --query Attributes.QueueArn --output text \
  --endpoint-url $AWS_ENDPOINT_URL)

aws sns subscribe \
  --topic-arn $TOPIC_ARN \
  --protocol sqs \
  --notification-endpoint $QUEUE_ARN \
  --endpoint-url $AWS_ENDPOINT_URL

# Publish a message
aws sns publish \
  --topic-arn $TOPIC_ARN \
  --message '{"event":"user.registered"}' \
  --endpoint-url $AWS_ENDPOINT_URL

# Fan-out: publish and verify the SQS queue received the message
aws sqs receive-message \
  --queue-url $AWS_ENDPOINT_URL/000000000000/orders \
  --endpoint-url $AWS_ENDPOINT_URL

SNS → SQS Fan-Out

Floci supports real SNS → SQS fan-out. When you publish to a topic, all SQS-subscribed queues receive the message immediately.

Supported subscription protocols: - sqs — delivers to a Floci SQS queue - lambda — invokes a Floci Lambda function - http / https — posts to an HTTP endpoint - application — fans out to a mobile push platform endpoint (see Mobile push)

FIFO topics

A topic whose name ends in .fifo is a FIFO topic. Publish and PublishBatch require a MessageGroupId, and a message is deduplicated against its MessageDeduplicationId for five minutes. Set ContentBasedDeduplication on the topic to derive that id from the message body.

The FifoThroughputScope attribute decides how wide that deduplication reaches:

Value Deduplication scope
Topic (default) Across the whole topic: the same MessageDeduplicationId is a duplicate no matter which message group it arrives under
MessageGroup Within a single message group: the same MessageDeduplicationId under two different MessageGroupIds is two distinct messages

MessageGroup is what a fan-out that reuses one deduplication id per group needs, for example publishing the same event to a topic once per tenant with the tenant as the message group.

aws sns create-topic --name events.fifo \
  --attributes FifoTopic=true,FifoThroughputScope=MessageGroup \
  --endpoint-url $AWS_ENDPOINT_URL

When the topic forwards to an SQS FIFO queue, set the matching queue attributes (DeduplicationScope=messageGroup and FifoThroughputLimit=perMessageGroupId), otherwise the queue deduplicates topic-wide on the way in.

CloudFormation carries both settings through: AWS::SNS::Topic forwards FifoThroughputScope and ContentBasedDeduplication, and a FIFO topic left unnamed gets a generated name ending in .fifo.

Control Tower managed topic

AWS Control Tower creates the regional aws-controltower-AggregateSecurityNotifications topic before Landing Zone Accelerator deploys its audit notification forwarder. Floci lazily materializes that exact same-account managed topic when Subscribe first references it, matching the Control Tower prerequisite without weakening normal SNS validation. Subscribing to any other missing topic still returns NotFound.

Mobile push (mock)

Floci mocks SNS mobile push for iOS and Android. No real APNS or FCM connection is made — every push is captured in memory so tests can assert what would have been sent.

Supported platforms: APNS, APNS_SANDBOX, GCM, FCM. Any other platform value returns InvalidParameter.

End-to-end flow

APP_ARN=$(aws sns create-platform-application \
  --name ios-app --platform APNS \
  --attributes PlatformCredential=fake-cert \
  --endpoint-url http://localhost:4566 --query PlatformApplicationArn --output text)

ENDPOINT_ARN=$(aws sns create-platform-endpoint \
  --platform-application-arn $APP_ARN \
  --token ios-device-token-abc \
  --endpoint-url http://localhost:4566 --query EndpointArn --output text)

# Plain string payload
aws sns publish --target-arn $ENDPOINT_ARN --message '{"aps":{"alert":"hi"}}' \
  --endpoint-url http://localhost:4566

# Platform-specific payloads with MessageStructure=json
aws sns publish --target-arn $ENDPOINT_ARN --message-structure json \
  --message '{"default":"fallback","APNS":"{\"aps\":{\"alert\":\"ios\"}}","GCM":"{\"notification\":{\"body\":\"android\"}}"}' \
  --endpoint-url http://localhost:4566

When MessageStructure="json", Floci picks the key matching the endpoint's platform (APNS, APNS_SANDBOX, GCM, or FCM), falling back to default. The envelope must be a JSON object and must include default — otherwise InvalidParameter.

Broadcast to devices via a topic

Subscribe platform endpoints to a topic with Protocol="application", then publish to the topic to fan out to every subscribed device — each endpoint is captured exactly as if you had published to it directly (same platform-payload resolution, same Enabled gating). A disabled endpoint in the fan-out is skipped; the rest still receive the push.

aws sns subscribe --topic-arn $TOPIC_ARN \
  --protocol application --notification-endpoint $ENDPOINT_ARN \
  --endpoint-url http://localhost:4566

aws sns publish --topic-arn $TOPIC_ARN --message-structure json \
  --message '{"default":"market open","GCM":"{\"notification\":{\"body\":\"market alert\"}}"}' \
  --endpoint-url http://localhost:4566

Broadcast pushes surface in the same retrospection API, keyed by EndpointArn.

Per-protocol payloads on topic publish

MessageStructure="json" resolves per subscriber, not just for mobile endpoints. Each subscription receives the value under its own protocol key — sqs, lambda, http, https, email, email-json, sms, or the push platform (APNS, GCM, …) for application — falling back to default when that key is absent.

aws sns publish --topic-arn $TOPIC_ARN --message-structure json \
  --message '{"default":"hello","sqs":"hi sqs","GCM":"{\"notification\":{\"body\":\"hi device\"}}"}' \
  --endpoint-url http://localhost:4566

The SQS subscriber receives hi sqs, the platform endpoint receives the GCM payload, and every other subscriber receives hello. As with any topic publish, the envelope must be a JSON object carrying default, validated before fan-out begins.

Inspecting captured pushes

# All captured pushes (newest first), or filtered by endpoint
curl http://localhost:4566/_aws/sns/push-notifications
curl "http://localhost:4566/_aws/sns/push-notifications?EndpointArn=$ENDPOINT_ARN"

# Reset between tests
curl -X DELETE http://localhost:4566/_aws/sns/push-notifications

Simulating expired tokens

Two ways to make Publish fail with EndpointDisabledException:

  1. Explicit — call SetEndpointAttributes with Enabled=false. Matches the real AWS flow after an async APNS/FCM failure.
  2. Sentinel — create an endpoint whose token contains EXPIRED (case-insensitive). Floci marks it Enabled=false on creation, so the first publish fails. Lets you exercise the unhappy path with a single API call.

Error codes

Action Condition Error code HTTP
CreatePlatformApplication Missing Name InvalidParameter 400
CreatePlatformApplication Unsupported Platform (e.g. WNS, ADM) InvalidParameter 400
CreatePlatformEndpoint Missing Token InvalidParameter 400
CreatePlatformEndpoint Unknown PlatformApplicationArn NotFound 404
CreatePlatformEndpoint Same Token, different CustomUserData or attrs InvalidParameter 400
CreatePlatformEndpoint Platform app disabled PlatformApplicationDisabledException 400
Publish Unknown endpoint ARN NotFound 404
Publish TargetArn is a platform application ARN InvalidParameter 400
Publish Endpoint Enabled=false EndpointDisabledException 400
Publish Platform application Enabled=false PlatformApplicationDisabledException 400
Publish MessageStructure=json missing default key InvalidParameter 400
Publish MessageStructure=json message is not valid JSON InvalidParameter 400
GetPlatformApplicationAttributes Unknown ARN NotFound 404
GetEndpointAttributes Unknown ARN NotFound 404
SetEndpointAttributes Unknown ARN NotFound 404

DeletePlatformApplication and DeleteEndpoint are idempotent — they succeed silently if the resource does not exist, matching real SNS behavior.