Reference guide for local development alternatives to cloud services. Used by architects to plan adapter patterns enabling local execution and E2E testing without cloud deployment.
This skill provides guidance on selecting local alternatives for cloud services, enabling:
All cloud service integrations MUST use the Repository/Adapter Pattern:
┌─────────────────┐
│ Business Logic │
└────────┬────────┘
│ uses
▼
┌─────────────────┐
│ Interface │ ← Defined in specs
└────────┬────────┘
│ implemented by
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│ Cloud │ │ Local │
│Adapter│ │Adapter│
└───────┘ └───────┘
Key Requirements:
.specs/interfaces/LOCAL_RUN=true mode| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS SQS | RabbitMQ | rabbitmq:3-management | Use AMQP protocol |
| Azure Service Bus | RabbitMQ | rabbitmq:3-management | Or use Azurite (limited) |
| Google Cloud Pub/Sub | RabbitMQ | rabbitmq:3-management | Or use emulator |
| AWS SNS + SQS | RabbitMQ + Exchange | rabbitmq:3-management | Fan-out via exchanges |
Interface Pattern:
MessageQueue:
- publish(topic: string, message: Message): Promise<void>
- subscribe(topic: string, handler: MessageHandler): Subscription
- acknowledge(messageId: string): Promise<void>
Adapter Selection:
LOCAL_RUN=true → RabbitMQ adapter (localhost:5672)LOCAL_RUN=false → Cloud adapter (SQS/Service Bus/Pub/Sub)| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS S3 | MinIO | minio/minio | S3-compatible API |
| Azure Blob Storage | Azurite | mcr.microsoft.com/azure-storage/azurite | Full emulation |
| Google Cloud Storage | MinIO | minio/minio | Or fake-gcs-server |
Interface Pattern:
ObjectStorage:
- upload(bucket: string, key: string, data: Buffer): Promise<string>
- download(bucket: string, key: string): Promise<Buffer>
- delete(bucket: string, key: string): Promise<void>
- getSignedUrl(bucket: string, key: string, expiry: number): Promise<string>
- list(bucket: string, prefix?: string): Promise<ObjectMetadata[]>
Adapter Selection:
LOCAL_RUN=true → MinIO adapter (localhost:9000)LOCAL_RUN=false → Cloud adapter (S3/Blob/GCS)| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS RDS PostgreSQL | PostgreSQL | postgres:15 | Direct compatibility |
| Azure Database PostgreSQL | PostgreSQL | postgres:15 | Direct compatibility |
| AWS RDS MySQL | MySQL | mysql:8 | Direct compatibility |
| Azure SQL | SQL Server | mcr.microsoft.com/mssql/server | Or PostgreSQL |
| Google Cloud SQL | PostgreSQL/MySQL | postgres:15 | Direct compatibility |
Interface Pattern:
Database:
- query(sql: string, params: any[]): Promise<Result>
- transaction(fn: (tx: Transaction) => Promise<T>): Promise<T>
- healthCheck(): Promise<boolean>
Note: Use same database engine locally - no adapter needed if using standard SQL. Use environment variables for connection strings.
| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS DynamoDB | DynamoDB Local | amazon/dynamodb-local | Official emulator |
| Azure Cosmos DB | Cosmos DB Emulator | Windows only, or use MongoDB | Limited Linux support |
| MongoDB Atlas | MongoDB | mongo:6 | Direct compatibility |
| Google Firestore | Firestore Emulator | gcr.io/google.com/cloudsdktool/cloud-sdk | Via gcloud |
| Redis (ElastiCache) | Redis | redis:7 | Direct compatibility |
Interface Pattern:
DocumentStore:
- get(collection: string, id: string): Promise<Document | null>
- put(collection: string, id: string, document: Document): Promise<void>
- delete(collection: string, id: string): Promise<void>
- query(collection: string, filter: Filter): Promise<Document[]>
| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS SNS | RabbitMQ (fanout) | rabbitmq:3-management | Exchange pattern |
| Azure Event Grid | RabbitMQ | rabbitmq:3-management | Topic exchanges |
| SendGrid/SES (Email) | MailHog | mailhog/mailhog | Email capture |
| Twilio (SMS) | Mock service | Custom | Log to console/file |
Interface Pattern:
NotificationService:
- sendEmail(to: string[], subject: string, body: string): Promise<void>
- sendSms(to: string, message: string): Promise<void>
- publishEvent(topic: string, event: Event): Promise<void>
Adapter Selection:
LOCAL_RUN=true → MailHog (email), Console logger (SMS), RabbitMQ (events)LOCAL_RUN=false → Cloud services| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS Cognito | Keycloak | quay.io/keycloak/keycloak | Full OIDC support |
| Azure AD B2C | Keycloak | quay.io/keycloak/keycloak | Or mock JWT |
| Auth0 | Keycloak | quay.io/keycloak/keycloak | OIDC compatible |
| Firebase Auth | Keycloak | quay.io/keycloak/keycloak | Or emulator |
Interface Pattern:
AuthService:
- validateToken(token: string): Promise<TokenPayload>
- getUserInfo(userId: string): Promise<UserInfo>
- hasPermission(userId: string, permission: string): Promise<boolean>
Local Simplification:
For E2E testing, LOCAL_RUN=true can use a simplified auth that:
local-setup skill)| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS Secrets Manager | Local .env | N/A | Or HashiCorp Vault |
| Azure Key Vault | Local .env | N/A | Or HashiCorp Vault |
| Google Secret Manager | Local .env | N/A | Or HashiCorp Vault |
| HashiCorp Vault | Vault Dev Mode | vault:latest | vault server -dev |
Interface Pattern:
SecretsManager:
- getSecret(name: string): Promise<string>
- setSecret(name: string, value: string): Promise<void>
Adapter Selection:
LOCAL_RUN=true → Environment variables or .env fileLOCAL_RUN=false → Cloud secrets manager| Cloud Service | Local Alternative | Notes |
|---|---|---|
| AWS Lambda | Local HTTP server | Express/FastAPI endpoint |
| Azure Functions | Local HTTP server | Or func start |
| Google Cloud Functions | Local HTTP server | Or Functions Framework |
Adapter Pattern:
| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS OpenSearch | OpenSearch | opensearchproject/opensearch:2 | Direct compatibility |
| Azure Cognitive Search | OpenSearch | opensearchproject/opensearch:2 | Similar API |
| Elasticsearch Service | Elasticsearch | elasticsearch:8 | Direct compatibility |
| Algolia | MeiliSearch | getmeili/meilisearch | Similar features |
Interface Pattern:
SearchService:
- index(collection: string, id: string, document: Document): Promise<void>
- search(collection: string, query: SearchQuery): Promise<SearchResult>
- delete(collection: string, id: string): Promise<void>
| Cloud Service | Local Alternative | Docker Image | Notes |
|---|---|---|---|
| AWS ElastiCache (Redis) | Redis | redis:7 | Direct compatibility |
| Azure Cache for Redis | Redis | redis:7 | Direct compatibility |
| Memorystore | Redis | redis:7 | Direct compatibility |
Interface Pattern:
CacheService:
- get(key: string): Promise<T | null>
- set(key: string, value: T, ttl?: number): Promise<void>
- delete(key: string): Promise<void>
- exists(key: string): Promise<boolean>
When architects specify services, they MUST include adapter requirements:
## External Service: {Service Name}
### Purpose
{What this service does}
### Cloud Implementation
| Aspect | Value |
|--------|-------|
| **Provider** | {AWS/Azure/GCP} |
| **Service** | {Service name} |
| **Configuration** | {Key config} |
### Local Implementation
| Aspect | Value |
|--------|-------|
| **Alternative** | {Local service} |
| **Docker Image** | {image:tag} |
| **Port** | {port} |
| **Configuration** | {Key config} |
### Interface Contract
See: [{interface-name}](../interfaces/{interface-name}.md)
### Adapter Selection
| Environment | Adapter | Connection |
|-------------|---------|------------|
| Production | {CloudAdapter} | {connection string} |
| Local (LOCAL_RUN=true) | {LocalAdapter} | localhost:{port} |
### Data Consistency
- Schema: {Same/Compatible/Mapped}
- Message Format: {JSON/Protobuf/etc}
- Serialization: {Same across adapters}
Architects should reference this in .specs/deployment/local-services.md: