A production-ready HTTP server that emulates the Google Secret Manager API for local development. This emulator provides a complete implementation of the Secret Manager REST API, enabling seamless local development without requiring actual Google Cloud resources.
- Complete API Coverage: Full implementation of Google Secret Manager REST API
- Production Parity: Exact error response formats and HTTP status codes matching Google Cloud
- Local Development: Run entirely offline with no Google Cloud dependencies
- Persistent Storage: Optional JSON file persistence for data across restarts
- Docker Support: Production-ready container with health checks
- Mock Authentication: Configurable authentication bypass for development
- CORS Support: Enable cross-origin requests for web applications
- Graceful Shutdown: Proper cleanup and data persistence on shutdown
- Clone the repository:
git clone https://github.com/charlesgreen/gsm.git
cd gsm- Start the emulator:
docker-compose up -dThe emulator will be available at http://localhost:8085
- Install dependencies:
go mod download- Run the server:
go run cmd/server/main.godocker build -t gsm-emulator .
docker run -p 8085:8085 gsm-emulatorThe emulator implements the complete Google Secret Manager REST API v1:
GET /health- Health check endpointGET /ready- Readiness check endpoint
POST /v1/projects/{project}/secrets- Create a new secretGET /v1/projects/{project}/secrets- List secrets in a projectGET /v1/projects/{project}/secrets/{secret}- Get secret metadataDELETE /v1/projects/{project}/secrets/{secret}- Delete a secret
POST /v1/projects/{project}/secrets/{secret}:addVersion- Add a new versionGET /v1/projects/{project}/secrets/{secret}/versions/{version}:access- Access secret dataGET /v1/projects/{project}/secrets/{secret}/versions- List versionsDELETE /v1/projects/{project}/secrets/{secret}/versions/{version}- Delete a version
curl -X POST http://localhost:8085/v1/projects/my-project/secrets \
-H "Content-Type: application/json" \
-d '{
"secretId": "my-secret",
"secret": {
"labels": {"env": "development"}
}
}'curl -X POST http://localhost:8085/v1/projects/my-project/secrets/my-secret:addVersion \
-H "Content-Type: application/json" \
-d '{
"payload": {
"data": "bXktc2VjcmV0LXZhbHVl"
}
}'curl http://localhost:8085/v1/projects/my-project/secrets/my-secret/versions/latest:accessThis emulator is designed to provide exact production parity with Google Cloud Secret Manager, ensuring that applications behave identically in development and production environments.
All error responses match the exact Google Cloud API error format:
{
"error": {
"code": 404,
"message": "Secret [projects/my-project/secrets/my-secret] not found.",
"status": "NOT_FOUND"
}
}The emulator returns the same HTTP status codes as production:
- 200 OK: Successful GET requests
- 201 Created: Successful resource creation
- 204 No Content: Successful DELETE requests
- 400 Bad Request (INVALID_ARGUMENT): Invalid request format or missing required fields
- 404 Not Found (NOT_FOUND): Resource doesn't exist
- 409 Conflict (ALREADY_EXISTS): Attempting to create existing resource
- 500 Internal Server Error (INTERNAL): Server-side processing errors
Error messages include complete resource paths matching Google Cloud conventions:
- Secrets:
projects/{project}/secrets/{secret} - Versions:
projects/{project}/secrets/{secret}/versions/{version}
Run the included validation script to verify production parity:
# Start the emulator
go run cmd/server/main.go &
# Run parity validation tests
./scripts/validate_parity.sh
# Or run the Go test suite
go test ./tests/integration/production_parity_test.go -vThe parity tests validate:
- ✅ Correct HTTP status codes for all scenarios
- ✅ Proper JSON error response structure
- ✅ Resource-specific error messages with full paths
- ✅ Consistent error handling across all endpoints
- ✅ Transaction rollback behavior matching production
- Reliable Testing: Local tests accurately predict production behavior
- Debugging Consistency: Same error responses help identify issues early
- Reduced Risk: Eliminates deployment surprises from behavioral differences
- Seamless Integration: Drop-in replacement for production GSM during development
Configure the emulator using environment variables:
| Variable | Default | Description |
|---|---|---|
GSM_PORT |
8085 |
Server port |
GSM_HOST |
0.0.0.0 |
Bind address |
GSM_STORAGE_FILE |
(none) | JSON file for persistence |
GSM_LOG_LEVEL |
info |
Log level (debug/info/warn/error) |
GSM_ENABLE_CORS |
true |
Enable CORS headers |
GSM_ENABLE_AUTH |
false |
Enable mock authentication |
The emulator is compatible with the official Google Cloud Secret Manager client library. Simply override the endpoint:
package main
import (
"context"
"fmt"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"google.golang.org/api/option"
)
func main() {
ctx := context.Background()
// Use emulator endpoint
client, err := secretmanager.NewClient(ctx,
option.WithEndpoint("http://localhost:8085"),
option.WithoutAuthentication(),
)
if err != nil {
panic(err)
}
defer client.Close()
// Use client normally
// ... your secret manager operations
}func newSecretManagerClient(ctx context.Context) (*secretmanager.Client, error) {
if emulatorHost := os.Getenv("SECRET_MANAGER_EMULATOR_HOST"); emulatorHost != "" {
return secretmanager.NewClient(ctx,
option.WithEndpoint("http://"+emulatorHost),
option.WithoutAuthentication(),
)
}
// Production: use default authentication
return secretmanager.NewClient(ctx)
}Add to your existing Firebase docker-compose.yml:
version: '3.8'
services:
# Your existing Firebase emulators...
firebase-emulator:
# ... existing configuration
secret-manager-emulator:
image: charlesgreen/gsm:latest
ports:
- "8085:8085"
environment:
- GSM_STORAGE_FILE=/app/data/secrets.json
volumes:
- ./emulator-data/secrets:/app/data
networks:
- firebase-network
networks:
firebase-network:
external: true- Go 1.22 or later
- Docker (optional)
# Build binary
go build -o bin/gsm-server cmd/server/main.go
# Build Docker image
docker build -t gsm-emulator .# Run unit tests
go test ./tests/unit/...
# Run integration tests
go test ./tests/integration/...
# Run production parity tests
go test ./tests/integration/production_parity_test.go -v
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Validate production parity with shell script
./scripts/validate_parity.shThe project uses golangci-lint for comprehensive code linting and quality checks.
# Install using go
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Or using brew (macOS)
brew install golangci-lint
# Or using curl (Linux/macOS)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2# Run all configured linters
golangci-lint run
# Run with verbose output
golangci-lint run -v
# Run and fix auto-fixable issues
golangci-lint run --fix
# Run only specific linters
golangci-lint run --enable-only=errcheck,govetThe project includes a .golangci.yml configuration file with the following linters enabled:
- errcheck: Check for unchecked errors
- govet: Vet examines Go source code and reports suspicious constructs
- staticcheck: Advanced Go linter
- revive: Fast, configurable, extensible, flexible Go linter
- gocritic: Highly opinionated Go linter
- prealloc: Find slice declarations with non-zero initial length
- whitespace: Whitespace linter
- And more...
The linting runs automatically in CI/CD pipelines to ensure code quality.
├── cmd/server/ # Main application entry point
├── internal/
│ ├── api/
│ │ ├── handlers/ # HTTP request handlers
│ │ ├── middleware/ # HTTP middleware
│ │ └── routes/ # Route configuration
│ ├── models/ # Data models and structures
│ └── storage/ # Storage implementations
├── pkg/client/ # Example client code
├── tests/ # Test files
├── Dockerfile # Container configuration
├── docker-compose.yml # Development setup
└── README.md # This fileThe provided Dockerfile creates a minimal, secure image:
- Uses multi-stage build for small image size
- Runs as non-root user for security
- Includes health checks
- Supports volume mounts for persistence
# Build and push to registry
docker build -t your-registry/gsm-emulator:latest .
docker push your-registry/gsm-emulator:latest
# Deploy
docker run -d \
--name secret-manager-emulator \
-p 8085:8085 \
-v ./secrets:/app/data \
-e GSM_STORAGE_FILE=/app/data/secrets.json \
your-registry/gsm-emulator:latestapiVersion: apps/v1
kind: Deployment
metadata:
name: secret-manager-emulator
spec:
replicas: 1
selector:
matchLabels:
app: secret-manager-emulator
template:
metadata:
labels:
app: secret-manager-emulator
spec:
containers:
- name: gsm-emulator
image: charlesgreen/gsm:latest
ports:
- containerPort: 8085
env:
- name: GSM_STORAGE_FILE
value: /app/data/secrets.json
volumeMounts:
- name: storage
mountPath: /app/data
livenessProbe:
httpGet:
path: /health
port: 8085
readinessProbe:
httpGet:
path: /ready
port: 8085
volumes:
- name: storage
persistentVolumeClaim:
claimName: gsm-storage- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write tests for new functionality
- Follow Go conventions and best practices
- Update documentation for API changes
- Ensure Docker builds successfully
- Test with the official Google Cloud client library
- Check port 8085 is available
- Verify Docker daemon is running
- Check logs:
docker-compose logs secret-manager-emulator
- Ensure
GSM_ENABLE_AUTH=falsefor development - Use
option.WithoutAuthentication()in client code - Verify endpoint URL is correct
- Check volume mount configuration
- Verify write permissions on storage directory
- Ensure
GSM_STORAGE_FILEpath is accessible
- Verify emulator is running:
curl http://localhost:8085/health - Check firewall/network configuration
- Ensure correct host and port binding
MIT License - see LICENSE file for details.
- Google Cloud Secret Manager API documentation
- The Go community for excellent libraries and tools
- Contributors and users of this project
For more examples and detailed API documentation, see the examples directory.