Sandbox Environment
The Platform-Kernel sandbox is a Docker Compose overlay on top of the full infrastructure stack. It adds three things the bare stack doesn't have:
- Seed data injected into PostgreSQL at first boot (test tenant, users, roles, wallets)
- Envoy proxy in front of all Go services as the single ingress point (
localhost:8080) - Extended start periods for health checks to accommodate slow first-boot migrations
Sandbox = production stack + seed data. There is no "mock" infrastructure. Every API call you make in sandbox hits real Kafka, real ClickHouse, real Vault. This ensures module behaviour is identical in dev and prod.
Architecture
Starting the Sandbox
The sandbox uses the docker-compose.sandbox.yml overlay.
Run both files together from .dev/kernel/:
docker compose \
--env-file docker/versions.env \
-f docker/docker-compose.yml \
-f docker/docker-compose.sandbox.yml \
up --build -d
First-boot takes 3–5 minutes. ClamAV downloads ~300MB of antivirus signatures. PostgreSQL runs the seed script. ClickHouse initializes encrypted volumes. Subsequent restarts are under 30 seconds.
Monitor progress:
docker compose ps
docker compose logs -f --tail=50
Wait until Envoy is healthy:
curl -sf http://localhost:9901/ready
# LIVE
What's Pre-seeded
The file docker/sandbox/seed.sql is injected into PostgreSQL by the Docker
entrypoint on first run. It creates a predictable set of test data:
Test Tenant
| Field | Value |
|---|---|
| ID | 00000000-0000-7000-0000-000000000001 |
| Name | Sandbox Corp |
| Status | active |
| Plan | pro (unlimited dev limits) |
Test Users
| Password | Role | |
|---|---|---|
[email protected] | Sandbox2026! | Tenant Admin (all permissions) |
[email protected] | Sandbox2026! | Standard User (read-only) |
[email protected] | Sandbox2026! | Developer (module install) |
Pre-created Resources
- 2 wallets (USD, EUR) for the admin user with test balances
- 3 pre-registered roles:
tenant_admin,developer,viewer - Feature flag
sandbox_modeset totruefor the test tenant - 5 sample audit records across different resource types
Seed is applied once on first PostgreSQL data-volume initialisation. To re-seed (reset all data), stop the stack, remove the volume, and restart:
docker compose down -v
docker compose --env-file docker/versions.env \
-f docker/docker-compose.yml \
-f docker/docker-compose.sandbox.yml \
up --build -d
Service Endpoints in Sandbox
When running the sandbox overlay, all HTTP traffic goes through Envoy on
port 8080. Direct service ports remain bound for observability but you
should not use them for API calls.
| Service | Sandbox Endpoint |
|---|---|
| REST API (all primitives) | http://localhost:8080/api/v1/ |
| WebSocket (Notify) | ws://localhost:8080/ws |
| Envoy Admin UI | http://localhost:9901 |
| RabbitMQ Management | http://localhost:15672 (kernel / rabbitmq_dev_password) |
| ClickHouse HTTP | http://localhost:8123 |
| Vault UI | http://localhost:8200 (token: kernel-dev-root-token) |
| GoFeatureFlag API | http://localhost:1031 |
| SeaweedFS S3 | http://localhost:8333 |
Authenticating in Sandbox
Obtain a JWT for the pre-seeded admin user:
curl -s -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "Sandbox2026!"
}' | jq .
Response:
{
"access_token": "<JWT — valid 15 minutes>",
"refresh_token": "<opaque token — valid 7 days>",
"token_type": "Bearer",
"expires_in": 900
}
Export the token for subsequent requests:
export TOKEN="<access_token from above>"
Verify your identity:
curl -s http://localhost:8080/api/v1/auth/me \
-H "Authorization: Bearer $TOKEN" | jq .
Connecting Your Module to the Sandbox
Initialize the SDK in your module pointing to the local sandbox Gateway:
import { kernel } from '@platform/sdk-core';
kernel.init({
gateway: 'http://localhost:8080',
token: process.env.PLATFORM_ACCESS_TOKEN, // from env or auth flow
});
Then use any primitive normally — it hits the real sandbox services:
// Create a record in your module's autoApi model
const contact = await kernel.data().create('contacts', {
first_name: 'Jane',
last_name: 'Doe',
});
// Publish an event
await kernel.events().publish('crm.contact.created', {
contactId: contact.id,
tenantId: contact.tenant_id,
});
// Check feature flag
const isFeatureEnabled = await kernel.flags().isEnabled('sandbox_mode');
Verifying Sandbox Health
Run all five checks to confirm the sandbox is fully operational:
# 1. Gateway liveness
curl -sf http://localhost:8080/live # 200 OK
# 2. Gateway full readiness (checks all downstream services)
curl -sf http://localhost:8080/health | jq .status
# "ok"
# 3. IAM: login with seed user
curl -sf -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
| jq '.token_type'
# "Bearer"
# 4. Kafka broker is reachable
docker exec platform-kafka \
/opt/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server localhost:9092 2>&1 | grep "broker version"
# 5. ClickHouse is responding (used by Audit + Analytics)
curl -sf "http://localhost:8123/?query=SELECT%201"
# 1
Sandbox vs Full Stack Differences
| Aspect | Full Stack (docker-compose.yml) | Sandbox Overlay (+ docker-compose.sandbox.yml) |
|---|---|---|
| Entry point | Individual service ports | Envoy on :8080 (single ingress) |
| Seed data | None (empty databases) | Pre-seeded tenant, users, wallets, roles |
| Health check timing | Standard (20–30 s start period) | Extended (60–90 s, accounts for seed execution) |
| Use case | CI/CD integration tests | Module development, manual testing |
Stopping the Sandbox
# Stop without removing data volumes (fastest restart)
docker compose -f docker/docker-compose.yml \
-f docker/docker-compose.sandbox.yml \
down
# Stop AND remove all data (full reset)
docker compose -f docker/docker-compose.yml \
-f docker/docker-compose.sandbox.yml \
down -v