container-superposition 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +365 -9
  2. package/dist/scripts/init.js +220 -94
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/commands/doctor.js +2 -2
  5. package/dist/tool/commands/explain.d.ts.map +1 -1
  6. package/dist/tool/commands/explain.js +88 -0
  7. package/dist/tool/commands/explain.js.map +1 -1
  8. package/dist/tool/commands/plan.d.ts +51 -0
  9. package/dist/tool/commands/plan.d.ts.map +1 -1
  10. package/dist/tool/commands/plan.js +523 -1
  11. package/dist/tool/commands/plan.js.map +1 -1
  12. package/dist/tool/questionnaire/composer.d.ts +12 -3
  13. package/dist/tool/questionnaire/composer.d.ts.map +1 -1
  14. package/dist/tool/questionnaire/composer.js +133 -20
  15. package/dist/tool/questionnaire/composer.js.map +1 -1
  16. package/dist/tool/schema/types.d.ts +18 -0
  17. package/dist/tool/schema/types.d.ts.map +1 -1
  18. package/dist/tool/utils/gitignore.d.ts +15 -0
  19. package/dist/tool/utils/gitignore.d.ts.map +1 -0
  20. package/dist/tool/utils/gitignore.js +41 -0
  21. package/dist/tool/utils/gitignore.js.map +1 -0
  22. package/dist/tool/utils/services-export.d.ts +14 -0
  23. package/dist/tool/utils/services-export.d.ts.map +1 -0
  24. package/dist/tool/utils/services-export.js +478 -0
  25. package/dist/tool/utils/services-export.js.map +1 -0
  26. package/dist/tool/utils/summary.d.ts +69 -0
  27. package/dist/tool/utils/summary.d.ts.map +1 -0
  28. package/dist/tool/utils/summary.js +260 -0
  29. package/dist/tool/utils/summary.js.map +1 -0
  30. package/docs/overlays.md +48 -5
  31. package/overlays/.presets/microservice.yml +32 -6
  32. package/overlays/.presets/web-api.yml +76 -56
  33. package/overlays/cloudflared/README.md +190 -0
  34. package/overlays/cloudflared/devcontainer.patch.json +3 -0
  35. package/overlays/cloudflared/overlay.yml +15 -0
  36. package/overlays/cloudflared/setup.sh +49 -0
  37. package/overlays/cloudflared/verify.sh +21 -0
  38. package/overlays/direnv/README.md +6 -4
  39. package/overlays/direnv/setup.sh +0 -12
  40. package/overlays/grpc-tools/README.md +242 -0
  41. package/overlays/grpc-tools/devcontainer.patch.json +14 -0
  42. package/overlays/grpc-tools/overlay.yml +14 -0
  43. package/overlays/grpc-tools/setup.sh +57 -0
  44. package/overlays/grpc-tools/verify.sh +47 -0
  45. package/overlays/keycloak/.env.example +5 -0
  46. package/overlays/keycloak/README.md +238 -0
  47. package/overlays/keycloak/devcontainer.patch.json +17 -0
  48. package/overlays/keycloak/docker-compose.yml +32 -0
  49. package/overlays/keycloak/overlay.yml +23 -0
  50. package/overlays/keycloak/verify.sh +54 -0
  51. package/overlays/mailpit/.env.example +4 -0
  52. package/overlays/mailpit/README.md +191 -0
  53. package/overlays/mailpit/devcontainer.patch.json +20 -0
  54. package/overlays/mailpit/docker-compose.yml +17 -0
  55. package/overlays/mailpit/overlay.yml +26 -0
  56. package/overlays/mailpit/verify.sh +52 -0
  57. package/overlays/ngrok/overlay.yml +2 -1
  58. package/overlays/python/README.md +51 -35
  59. package/overlays/python/devcontainer.patch.json +7 -4
  60. package/overlays/python/setup.sh +50 -23
  61. package/overlays/python/verify.sh +29 -1
  62. package/package.json +1 -1
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ # Verification script for gRPC Tools overlay
3
+ # Confirms protoc, buf, and grpcurl are installed
4
+
5
+ set -e
6
+
7
+ echo "🔍 Verifying gRPC Tools overlay..."
8
+ echo ""
9
+
10
+ ALL_CHECKS_PASSED=true
11
+
12
+ # Check protoc
13
+ echo "1️⃣ Checking protoc (Protocol Buffers compiler)..."
14
+ if command -v protoc &> /dev/null; then
15
+ echo " ✅ protoc found: $(protoc --version)"
16
+ else
17
+ echo " ❌ protoc not found"
18
+ ALL_CHECKS_PASSED=false
19
+ fi
20
+
21
+ # Check buf
22
+ echo ""
23
+ echo "2️⃣ Checking buf CLI..."
24
+ if command -v buf &> /dev/null; then
25
+ echo " ✅ buf found: $(buf --version)"
26
+ else
27
+ echo " ❌ buf not found"
28
+ ALL_CHECKS_PASSED=false
29
+ fi
30
+
31
+ # Check grpcurl
32
+ echo ""
33
+ echo "3️⃣ Checking grpcurl..."
34
+ if command -v grpcurl &> /dev/null; then
35
+ echo " ✅ grpcurl found: $(grpcurl --version 2>&1 | head -1)"
36
+ else
37
+ echo " ❌ grpcurl not found"
38
+ ALL_CHECKS_PASSED=false
39
+ fi
40
+
41
+ echo ""
42
+ if [ "$ALL_CHECKS_PASSED" = true ]; then
43
+ echo "✅ gRPC Tools overlay verification complete"
44
+ else
45
+ echo "❌ Some gRPC tools are missing"
46
+ exit 1
47
+ fi
@@ -0,0 +1,5 @@
1
+ # Keycloak Configuration
2
+ KEYCLOAK_VERSION=26.0
3
+ KEYCLOAK_PORT=8180
4
+ KEYCLOAK_ADMIN=admin
5
+ KEYCLOAK_ADMIN_PASSWORD=admin
@@ -0,0 +1,238 @@
1
+ # Keycloak Overlay
2
+
3
+ Open-source identity and access management for developing apps with OAuth2/OIDC authentication.
4
+
5
+ ## Features
6
+
7
+ - **Keycloak 26** - Latest stable version with OIDC/OAuth2 support
8
+ - **Admin console** - Web UI for managing realms, clients, and users (port 8180)
9
+ - **OIDC/OAuth2** - Full OpenID Connect and OAuth 2.0 support
10
+ - **PostgreSQL backend** - Uses the PostgreSQL overlay as database
11
+ - **Docker Compose service** - Runs as separate container
12
+ - **Development mode** - Pre-configured for local development (no TLS required)
13
+
14
+ ## How It Works
15
+
16
+ This overlay adds Keycloak as a Docker Compose service alongside your development container. It requires the `postgres` overlay to provide the database backend.
17
+
18
+ **Architecture:**
19
+
20
+ ```
21
+ Development Container
22
+ └─ Your application code
23
+ └─ Connects to keycloak:8180 for auth
24
+
25
+ Keycloak Container (port 8180)
26
+ └─ Admin console
27
+ └─ OIDC/OAuth2 endpoints
28
+ └─ Connects to postgres:5432
29
+
30
+ PostgreSQL Container (port 5432)
31
+ └─ Keycloak database storage
32
+ ```
33
+
34
+ The Keycloak service is accessible from the dev container using the hostname `keycloak`.
35
+
36
+ ## Configuration
37
+
38
+ ### Environment Variables
39
+
40
+ The overlay includes a `.env.example` file. Copy it to `.env` and customize:
41
+
42
+ ```bash
43
+ cd .devcontainer
44
+ cp .env.example .env
45
+ ```
46
+
47
+ **Default values (.env.example):**
48
+
49
+ ```bash
50
+ # Keycloak Configuration
51
+ KEYCLOAK_VERSION=26.0
52
+ KEYCLOAK_PORT=8180
53
+ KEYCLOAK_ADMIN=admin
54
+ KEYCLOAK_ADMIN_PASSWORD=admin
55
+ ```
56
+
57
+ ⚠️ **Security Note:** Default credentials (`admin`/`admin`) are for development only. Never use these in production.
58
+
59
+ ### PostgreSQL Integration
60
+
61
+ Keycloak uses the PostgreSQL overlay's environment variables:
62
+
63
+ ```bash
64
+ POSTGRES_DB=devdb # Database name
65
+ POSTGRES_USER=postgres # Database user
66
+ POSTGRES_PASSWORD=postgres # Database password
67
+ ```
68
+
69
+ These are configured in the `postgres` overlay's `.env.example`.
70
+
71
+ ## Common Commands
72
+
73
+ ### Access Admin Console
74
+
75
+ ```bash
76
+ # Open in browser
77
+ open http://localhost:8180
78
+
79
+ # Admin credentials
80
+ # Username: admin (or KEYCLOAK_ADMIN value)
81
+ # Password: admin (or KEYCLOAK_ADMIN_PASSWORD value)
82
+ ```
83
+
84
+ ### Realm Management
85
+
86
+ ```bash
87
+ # List realms via Admin REST API
88
+ curl -s \
89
+ -u admin:admin \
90
+ http://localhost:8180/admin/realms | jq '.[].realm'
91
+
92
+ # Create a new realm
93
+ curl -s -X POST \
94
+ -H "Content-Type: application/json" \
95
+ -u admin:admin \
96
+ http://localhost:8180/admin/realms \
97
+ -d '{"realm": "myrealm", "enabled": true}'
98
+ ```
99
+
100
+ ### OIDC Discovery
101
+
102
+ ```bash
103
+ # Discover OIDC endpoints for the master realm
104
+ curl -s http://localhost:8180/realms/master/.well-known/openid-configuration | jq .
105
+
106
+ # Get discovery for a custom realm
107
+ curl -s http://localhost:8180/realms/myrealm/.well-known/openid-configuration | jq .
108
+ ```
109
+
110
+ ### Client Credentials Flow
111
+
112
+ ```bash
113
+ # Get access token using client credentials
114
+ curl -s -X POST \
115
+ http://localhost:8180/realms/master/protocol/openid-connect/token \
116
+ -d "client_id=admin-cli" \
117
+ -d "username=admin" \
118
+ -d "password=admin" \
119
+ -d "grant_type=password" | jq .access_token
120
+ ```
121
+
122
+ ### Token Introspection
123
+
124
+ ```bash
125
+ TOKEN=$(curl -s -X POST \
126
+ http://localhost:8180/realms/master/protocol/openid-connect/token \
127
+ -d "client_id=admin-cli" \
128
+ -d "username=admin" \
129
+ -d "password=admin" \
130
+ -d "grant_type=password" | jq -r .access_token)
131
+
132
+ # Decode token (base64)
133
+ echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
134
+ ```
135
+
136
+ ## Application Integration
137
+
138
+ ### Node.js (using openid-client)
139
+
140
+ ```javascript
141
+ import { Issuer } from 'openid-client';
142
+
143
+ const keycloakIssuer = await Issuer.discover('http://keycloak:8180/realms/myrealm');
144
+
145
+ const client = new keycloakIssuer.Client({
146
+ client_id: 'my-app',
147
+ client_secret: 'my-secret',
148
+ redirect_uris: ['http://localhost:3000/callback'],
149
+ response_types: ['code'],
150
+ });
151
+ ```
152
+
153
+ ### Python (using requests-oauthlib)
154
+
155
+ ```python
156
+ from requests_oauthlib import OAuth2Session
157
+
158
+ oauth = OAuth2Session(
159
+ client_id="my-app",
160
+ redirect_uri="http://localhost:5000/callback",
161
+ scope=["openid", "profile", "email"]
162
+ )
163
+
164
+ authorization_url, state = oauth.authorization_url(
165
+ "http://keycloak:8180/realms/myrealm/protocol/openid-connect/auth"
166
+ )
167
+ ```
168
+
169
+ ### .NET (using Microsoft.AspNetCore.Authentication.OpenIdConnect)
170
+
171
+ ```csharp
172
+ builder.Services.AddAuthentication(options => {
173
+ options.DefaultScheme = "Cookies";
174
+ options.DefaultChallengeScheme = "oidc";
175
+ })
176
+ .AddCookie("Cookies")
177
+ .AddOpenIdConnect("oidc", options => {
178
+ options.Authority = "http://keycloak:8180/realms/myrealm";
179
+ options.ClientId = "my-app";
180
+ options.ClientSecret = "my-secret";
181
+ options.ResponseType = "code";
182
+ options.RequireHttpsMetadata = false; // Development only
183
+ });
184
+ ```
185
+
186
+ ## Use Cases
187
+
188
+ - **OAuth2/OIDC integration testing** - Test authentication flows end-to-end
189
+ - **Multi-tenant applications** - Create separate realms per tenant
190
+ - **SSO development** - Single Sign-On across multiple local services
191
+ - **Identity federation** - Test social login, LDAP, SAML integration
192
+ - **Role-based access control** - Define and test permissions locally
193
+
194
+ ## Troubleshooting
195
+
196
+ ### Keycloak takes too long to start
197
+
198
+ Keycloak can take 60–90 seconds on first startup (database schema creation). Wait for the health check to pass:
199
+
200
+ ```bash
201
+ docker-compose logs -f keycloak
202
+ # Look for: "Keycloak X.X started"
203
+ ```
204
+
205
+ ### Database connection errors
206
+
207
+ Ensure PostgreSQL is running and healthy before Keycloak starts:
208
+
209
+ ```bash
210
+ docker-compose ps postgres
211
+ # Should show "healthy"
212
+ ```
213
+
214
+ ### Cannot connect from application
215
+
216
+ Use `keycloak` (not `localhost`) as the hostname when connecting from inside the container:
217
+
218
+ ```bash
219
+ # Correct (from inside dev container)
220
+ http://keycloak:8180/realms/master
221
+
222
+ # Correct (from host machine browser)
223
+ http://localhost:8180/realms/master
224
+ ```
225
+
226
+ ## References
227
+
228
+ - [Keycloak Documentation](https://www.keycloak.org/documentation)
229
+ - [Keycloak Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/)
230
+ - [OpenID Connect Specification](https://openid.net/connect/)
231
+ - [Keycloak Docker Image](https://quay.io/repository/keycloak/keycloak)
232
+
233
+ **Related Overlays:**
234
+
235
+ - `postgres` - Required database backend
236
+ - `nodejs` - Node.js application development
237
+ - `python` - Python application development
238
+ - `dotnet` - .NET application development
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.base.schema.json",
3
+ "runServices": ["keycloak"],
4
+ "_serviceOrder": 10,
5
+ "forwardPorts": [8180],
6
+ "portsAttributes": {
7
+ "8180": {
8
+ "label": "Keycloak Admin Console",
9
+ "onAutoForward": "openBrowser"
10
+ }
11
+ },
12
+ "remoteEnv": {
13
+ "KEYCLOAK_HOST": "keycloak",
14
+ "KEYCLOAK_PORT": "8180",
15
+ "KEYCLOAK_ISSUER": "http://keycloak:8180/realms/master"
16
+ }
17
+ }
@@ -0,0 +1,32 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ keycloak:
5
+ image: quay.io/keycloak/keycloak:${KEYCLOAK_VERSION:-26.0}
6
+ command: start-dev
7
+ restart: unless-stopped
8
+ environment:
9
+ KC_DB: postgres
10
+ KC_DB_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-devdb}
11
+ KC_DB_USERNAME: ${POSTGRES_USER:-postgres}
12
+ KC_DB_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
13
+ KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin}
14
+ KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
15
+ KC_HTTP_PORT: 8180
16
+ KC_HOSTNAME_STRICT: 'false'
17
+ KC_HTTP_ENABLED: 'true'
18
+ ports:
19
+ - '${KEYCLOAK_PORT:-8180}:8180'
20
+ depends_on:
21
+ - postgres
22
+ networks:
23
+ - devnet
24
+ healthcheck:
25
+ test: ['CMD-SHELL', 'curl -sf http://localhost:8180/health/ready || exit 1']
26
+ interval: 15s
27
+ timeout: 10s
28
+ retries: 10
29
+ start_period: 60s
30
+
31
+ networks:
32
+ devnet:
@@ -0,0 +1,23 @@
1
+ id: keycloak
2
+ name: Keycloak
3
+ description: Open-source identity and access management (OIDC/OAuth2)
4
+ category: dev
5
+ supports:
6
+ - compose
7
+ requires:
8
+ - postgres
9
+ suggests: []
10
+ conflicts: []
11
+ tags:
12
+ - dev
13
+ - auth
14
+ - oidc
15
+ - oauth2
16
+ - identity
17
+ ports:
18
+ - port: 8180
19
+ service: keycloak
20
+ protocol: http
21
+ description: Keycloak admin console and auth endpoints
22
+ path: /
23
+ onAutoForward: openBrowser
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+ # Verification script for Keycloak overlay
3
+ # Confirms Keycloak is running and accessible
4
+
5
+ set -e
6
+
7
+ echo "🔍 Verifying Keycloak overlay..."
8
+ echo ""
9
+
10
+ # Check if curl is available
11
+ echo "1️⃣ Checking curl availability..."
12
+ if ! command -v curl &> /dev/null; then
13
+ echo " ❌ curl not found"
14
+ exit 1
15
+ fi
16
+ echo " ✅ curl found"
17
+
18
+ # Check Keycloak health endpoint
19
+ echo ""
20
+ echo "2️⃣ Checking Keycloak service..."
21
+ KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak}"
22
+ KEYCLOAK_PORT="${KEYCLOAK_PORT:-8180}"
23
+ KEYCLOAK_READY=false
24
+
25
+ for i in {1..40}; do
26
+ if curl -sf "http://${KEYCLOAK_HOST}:${KEYCLOAK_PORT}/health/ready" &> /dev/null; then
27
+ echo " ✅ Keycloak service is ready"
28
+ KEYCLOAK_READY=true
29
+ break
30
+ fi
31
+ sleep 3
32
+ done
33
+
34
+ if [ "$KEYCLOAK_READY" = false ]; then
35
+ echo " ❌ Keycloak service not ready after 2 minutes"
36
+ echo " ℹ️ Keycloak can take a while to start on first run"
37
+ exit 1
38
+ fi
39
+
40
+ # Check OIDC discovery endpoint
41
+ echo ""
42
+ echo "3️⃣ Checking OIDC discovery endpoint..."
43
+ if curl -sf "http://${KEYCLOAK_HOST}:${KEYCLOAK_PORT}/realms/master/.well-known/openid-configuration" &> /dev/null; then
44
+ echo " ✅ OIDC discovery endpoint is accessible"
45
+ else
46
+ echo " ❌ OIDC discovery endpoint not accessible"
47
+ exit 1
48
+ fi
49
+
50
+ echo ""
51
+ echo "✅ Keycloak overlay verification complete"
52
+ echo " Admin console: http://localhost:${KEYCLOAK_PORT}"
53
+ echo " Admin credentials: admin / admin (default)"
54
+ echo " OIDC discovery: http://localhost:${KEYCLOAK_PORT}/realms/master/.well-known/openid-configuration"
@@ -0,0 +1,4 @@
1
+ # Mailpit Configuration
2
+ MAILPIT_VERSION=latest
3
+ MAILPIT_UI_PORT=8025
4
+ MAILPIT_SMTP_PORT=1025
@@ -0,0 +1,191 @@
1
+ # Mailpit Overlay
2
+
3
+ Email testing tool that captures all outbound emails, with a web UI for browsing and an API for automated testing.
4
+
5
+ ## Features
6
+
7
+ - **Mailpit** - Fast, lightweight email testing tool
8
+ - **Web UI** - Beautiful interface for viewing captured emails (port 8025)
9
+ - **SMTP server** - Accepts all email without authentication (port 1025)
10
+ - **REST API** - Programmatic access to captured messages
11
+ - **Search and filter** - Find emails by recipient, subject, or content
12
+ - **No accidental sends** - All emails are captured locally, never delivered
13
+
14
+ ## How It Works
15
+
16
+ This overlay adds Mailpit as a Docker Compose service. Configure your application to send emails via SMTP to `mailpit:1025` (from inside the container) and all emails will be captured and visible in the web UI.
17
+
18
+ ## Configuration
19
+
20
+ ### Environment Variables
21
+
22
+ The overlay includes a `.env.example` file. Copy it to `.env` and customize:
23
+
24
+ ```bash
25
+ cd .devcontainer
26
+ cp .env.example .env
27
+ ```
28
+
29
+ **Default values (.env.example):**
30
+
31
+ ```bash
32
+ # Mailpit Configuration
33
+ MAILPIT_VERSION=latest
34
+ MAILPIT_UI_PORT=8025
35
+ MAILPIT_SMTP_PORT=1025
36
+ ```
37
+
38
+ ### SMTP Settings for Your Application
39
+
40
+ Configure your application to use these settings:
41
+
42
+ | Setting | Value |
43
+ | -------------- | ------------------------------------------------------ |
44
+ | SMTP host | `mailpit` (inside container) / `localhost` (from host) |
45
+ | SMTP port | `1025` |
46
+ | Authentication | None required |
47
+ | TLS/SSL | Not required |
48
+
49
+ ## Common Commands
50
+
51
+ ### View Captured Emails
52
+
53
+ ```bash
54
+ # Open web UI in browser
55
+ open http://localhost:8025
56
+
57
+ # List messages via API
58
+ curl -s http://localhost:8025/api/v1/messages | jq .
59
+
60
+ # Get message count
61
+ curl -s http://localhost:8025/api/v1/info | jq .Messages
62
+ ```
63
+
64
+ ### Search Emails
65
+
66
+ ```bash
67
+ # Search by query
68
+ curl -s "http://localhost:8025/api/v1/messages?query=password+reset" | jq .
69
+
70
+ # Filter by recipient
71
+ curl -s "http://localhost:8025/api/v1/messages?query=to:user@example.com" | jq .
72
+ ```
73
+
74
+ ### Clear All Emails
75
+
76
+ ```bash
77
+ # Delete all messages via API
78
+ curl -s -X DELETE http://localhost:8025/api/v1/messages
79
+ ```
80
+
81
+ ## Application Configuration Examples
82
+
83
+ ### Node.js (using nodemailer)
84
+
85
+ ```javascript
86
+ import nodemailer from 'nodemailer';
87
+
88
+ const transporter = nodemailer.createTransport({
89
+ host: process.env.SMTP_HOST || 'mailpit',
90
+ port: parseInt(process.env.SMTP_PORT || '1025'),
91
+ secure: false,
92
+ auth: null, // No authentication needed
93
+ });
94
+
95
+ await transporter.sendMail({
96
+ from: 'noreply@example.com',
97
+ to: 'user@example.com',
98
+ subject: 'Welcome!',
99
+ text: 'Welcome to our app.',
100
+ html: '<b>Welcome to our app.</b>',
101
+ });
102
+ ```
103
+
104
+ ### Python (using smtplib)
105
+
106
+ ```python
107
+ import smtplib
108
+ from email.mime.text import MIMEText
109
+ import os
110
+
111
+ def send_email(to, subject, body):
112
+ msg = MIMEText(body, 'html')
113
+ msg['Subject'] = subject
114
+ msg['From'] = 'noreply@example.com'
115
+ msg['To'] = to
116
+
117
+ smtp_host = os.getenv('SMTP_HOST', 'mailpit')
118
+ smtp_port = int(os.getenv('SMTP_PORT', '1025'))
119
+
120
+ with smtplib.SMTP(smtp_host, smtp_port) as server:
121
+ server.sendmail(msg['From'], [to], msg.as_string())
122
+ ```
123
+
124
+ ### .NET (using MailKit)
125
+
126
+ ```csharp
127
+ using MailKit.Net.Smtp;
128
+ using MimeKit;
129
+
130
+ var message = new MimeMessage();
131
+ message.From.Add(new MailboxAddress("App", "noreply@example.com"));
132
+ message.To.Add(new MailboxAddress("User", "user@example.com"));
133
+ message.Subject = "Welcome!";
134
+ message.Body = new TextPart("html") { Text = "<b>Welcome!</b>" };
135
+
136
+ using var client = new SmtpClient();
137
+ await client.ConnectAsync(
138
+ Environment.GetEnvironmentVariable("SMTP_HOST") ?? "mailpit",
139
+ int.Parse(Environment.GetEnvironmentVariable("SMTP_PORT") ?? "1025"),
140
+ false
141
+ );
142
+ await client.SendAsync(message);
143
+ await client.DisconnectAsync(true);
144
+ ```
145
+
146
+ ## Automated Testing
147
+
148
+ ### Check Email was Sent
149
+
150
+ ```bash
151
+ # Send test email, then verify it was received
152
+ curl -s http://localhost:8025/api/v1/messages | jq '.messages | length'
153
+ # Should be > 0
154
+
155
+ # Get latest email subject
156
+ curl -s http://localhost:8025/api/v1/messages | jq '.messages[0].Subject'
157
+ ```
158
+
159
+ ### Integration Test Pattern
160
+
161
+ ```javascript
162
+ // After triggering an action that sends email:
163
+ const response = await fetch('http://localhost:8025/api/v1/messages');
164
+ const data = await response.json();
165
+
166
+ const latestEmail = data.messages[0];
167
+ expect(latestEmail.To[0].Address).toBe('user@example.com');
168
+ expect(latestEmail.Subject).toBe('Password Reset');
169
+
170
+ // Clean up for next test
171
+ await fetch('http://localhost:8025/api/v1/messages', { method: 'DELETE' });
172
+ ```
173
+
174
+ ## Use Cases
175
+
176
+ - **Email flow testing** - Verify registration, password reset, and notification emails
177
+ - **Template development** - Preview rendered HTML email templates
178
+ - **Integration testing** - Assert emails are sent with correct content
179
+ - **No accidental sends** - Safely test in development without real email delivery
180
+
181
+ ## References
182
+
183
+ - [Mailpit Documentation](https://mailpit.axllent.org/docs/)
184
+ - [Mailpit API Reference](https://mailpit.axllent.org/docs/api-v1/)
185
+ - [Mailpit GitHub](https://github.com/axllent/mailpit)
186
+
187
+ **Related Overlays:**
188
+
189
+ - `nodejs` - Node.js with nodemailer
190
+ - `python` - Python email development
191
+ - `dotnet` - .NET with MailKit
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.base.schema.json",
3
+ "runServices": ["mailpit"],
4
+ "forwardPorts": [8025, 1025],
5
+ "portsAttributes": {
6
+ "8025": {
7
+ "label": "Mailpit Web UI",
8
+ "onAutoForward": "openBrowser"
9
+ },
10
+ "1025": {
11
+ "label": "Mailpit SMTP",
12
+ "onAutoForward": "ignore"
13
+ }
14
+ },
15
+ "remoteEnv": {
16
+ "SMTP_HOST": "mailpit",
17
+ "SMTP_PORT": "1025",
18
+ "MAILPIT_URL": "http://mailpit:8025"
19
+ }
20
+ }
@@ -0,0 +1,17 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ mailpit:
5
+ image: axllent/mailpit:${MAILPIT_VERSION:-latest}
6
+ restart: unless-stopped
7
+ environment:
8
+ MP_SMTP_AUTH_ACCEPT_ANY: '1'
9
+ MP_SMTP_AUTH_ALLOW_INSECURE: '1'
10
+ ports:
11
+ - '${MAILPIT_UI_PORT:-8025}:8025'
12
+ - '${MAILPIT_SMTP_PORT:-1025}:1025'
13
+ networks:
14
+ - devnet
15
+
16
+ networks:
17
+ devnet:
@@ -0,0 +1,26 @@
1
+ id: mailpit
2
+ name: Mailpit
3
+ description: Email testing tool with web UI and SMTP server
4
+ category: dev
5
+ supports:
6
+ - compose
7
+ requires: []
8
+ suggests: []
9
+ conflicts: []
10
+ tags:
11
+ - dev
12
+ - email
13
+ - smtp
14
+ - testing
15
+ ports:
16
+ - port: 8025
17
+ service: mailpit
18
+ protocol: http
19
+ description: Mailpit web UI
20
+ path: /
21
+ onAutoForward: openBrowser
22
+ - port: 1025
23
+ service: mailpit
24
+ protocol: tcp
25
+ description: Mailpit SMTP server
26
+ onAutoForward: ignore