40c6a31784
Locked down to email_domain == neurontechnologies.ai via Cloudflare Access. Dedicated SA with zero prod IAM. Isolated Supabase project (not a schema in prod). No outbound email path. /api/share and /said return 410 Gone in sandbox so the public-artifact surface stays strictly in prod. Stub deploy ready. Real soul build pipeline lands separately.
169 lines
7.9 KiB
Markdown
169 lines
7.9 KiB
Markdown
# sandbox.neurontechnologies.ai
|
|
|
|
Internal-only experimentation environment. The real soul (with safety stripped)
|
|
runs here when the build pipeline lands. Sandbox is intentionally separate
|
|
from stage so the stage path stays clean for normal QA work.
|
|
|
|
The terraform itself lives at `servers/gcp/sandbox.tf` (single file, mirrors
|
|
the existing flat-file layout under `servers/gcp/`). This directory holds
|
|
documentation only.
|
|
|
|
## What is sandbox
|
|
|
|
| Layer | Where |
|
|
|------------------|-------------------------------------------------------------|
|
|
| Domain | `sandbox.neurontechnologies.ai` |
|
|
| Cloud Run | `neuron-sandbox-us` (us-central1, single region) |
|
|
| Image registry | `us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/` |
|
|
| Service account | `neuron-sandbox@neuron-785695.iam.gserviceaccount.com` |
|
|
| Access control | Cloudflare Zero Trust Access, single rule |
|
|
| Identity rule | `email_domain == neurontechnologies.ai` |
|
|
| Supabase | Separate project (NOT a schema in prod's project) |
|
|
| Secret Manager | `sandbox-supabase-{url,anon-key,service-key}` |
|
|
|
|
## How to get in
|
|
|
|
1. Sign in with your `@neurontechnologies.ai` Google account at
|
|
`https://sandbox.neurontechnologies.ai/`. Cloudflare Access intercepts
|
|
the request, redirects you to the Google IdP, and only allows
|
|
`email_domain == neurontechnologies.ai` through.
|
|
2. After auth, you land on the running stub. With the stub deployed, the
|
|
root response is:
|
|
```json
|
|
{"env":"sandbox","status":"ready","soul":"not_loaded"}
|
|
```
|
|
3. There are no service tokens, no mTLS certs, no bypass paths. The single
|
|
policy is the entire access surface.
|
|
|
|
Verify the lockdown from a shell at any time:
|
|
|
|
```bash
|
|
curl -sI https://sandbox.neurontechnologies.ai/ | head -2
|
|
# HTTP/2 302
|
|
# location: https://neuralplatform.cloudflareaccess.com/cdn-cgi/access/login/sandbox.neurontechnologies.ai?...
|
|
```
|
|
|
|
If the first line is anything other than 302 to the cloudflareaccess domain,
|
|
something has broken. Treat it as a P0.
|
|
|
|
## What's NOT in sandbox
|
|
|
|
- **No outbound email.** `RESEND_API_KEY` is intentionally not mounted on
|
|
`neuron-sandbox-us`. Defense-in-depth against an experimental soul
|
|
accidentally emailing real users.
|
|
- **No public share endpoints.** `/api/share`, `/said`, and `/share/<id>`
|
|
return `410 Gone` with `{"error":"not_available_in_sandbox"}`. Public
|
|
artifact distribution stays in prod.
|
|
- **No prod data.** Sandbox uses an isolated Supabase project. Nothing in
|
|
the prod Supabase is reachable from the sandbox SA.
|
|
- **No prod IAM.** The `neuron-sandbox` SA has zero project-level role
|
|
bindings. Its only grants are `roles/secretmanager.secretAccessor` on the
|
|
three sandbox-specific secrets. Verify with:
|
|
```bash
|
|
# As neuron-sandbox SA, all of these MUST return PERMISSION_DENIED:
|
|
gcloud secrets versions access latest --secret=stripe-secret-key
|
|
gcloud secrets versions access latest --secret=anthropic-api-key
|
|
gcloud secrets versions access latest --secret=supabase-service-key
|
|
gcloud run services describe marketing-prod-us --region=us-central1
|
|
```
|
|
- **No prod LLM credentials.** Sandbox does NOT mount `anthropic-api-key`.
|
|
When the real soul lands, it will need its own sandbox-only LLM key
|
|
(cheaper key, lower rate limits) provisioned and granted to the sandbox SA.
|
|
|
|
## Provision the isolated Supabase project (one-time, manual)
|
|
|
|
There is no Supabase Management API token in `~/Secrets/api-keys/` or in
|
|
GCP Secret Manager today, so the project creation is a manual step.
|
|
|
|
1. Sign in to https://supabase.com/dashboard with the Neuron Technologies
|
|
org owner account.
|
|
2. New project. Name: `neuron-sandbox`. Region: `us-east-1` (closest to
|
|
us-central1 Cloud Run latency-wise; single-region experimental env, so
|
|
geo doesn't matter much).
|
|
3. After provisioning, capture from Project Settings -> API:
|
|
- Project URL: `https://<ref>.supabase.co`
|
|
- `anon` public key
|
|
- `service_role` secret key
|
|
4. Push them into Secret Manager (this overwrites the placeholder versions
|
|
that terraform created):
|
|
```bash
|
|
echo -n 'https://<ref>.supabase.co' | gcloud secrets versions add \
|
|
sandbox-supabase-url --data-file=- --project=neuron-785695
|
|
echo -n '<anon-key>' | gcloud secrets versions add \
|
|
sandbox-supabase-anon-key --data-file=- --project=neuron-785695
|
|
echo -n '<service-key>' | gcloud secrets versions add \
|
|
sandbox-supabase-service-key --data-file=- --project=neuron-785695
|
|
```
|
|
5. Force a new sandbox revision so it picks up the secrets:
|
|
```bash
|
|
gcloud run services update neuron-sandbox-us --region=us-central1 \
|
|
--project=neuron-785695 \
|
|
--image=us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:initial
|
|
```
|
|
6. Do NOT seed any tables in this project yet. Real soul might want a
|
|
different schema than the demo soul. Schema lands when real soul lands.
|
|
|
|
When a Supabase Management API token does become available, automate this
|
|
by writing a small script in `scripts/` that calls
|
|
`POST https://api.supabase.com/v1/projects` and immediately writes the
|
|
returned values back to Secret Manager. Track that as a follow-up.
|
|
|
|
## Deploy a new build (manual, until CI is wired)
|
|
|
|
```bash
|
|
cd ~/Development/neuron-technologies/products/sandbox-stub # or real soul source
|
|
bash ./build.sh # cross-compile + push to AR
|
|
gcloud run services update neuron-sandbox-us \
|
|
--region=us-central1 \
|
|
--project=neuron-785695 \
|
|
--image=us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:<tag>
|
|
```
|
|
|
|
The terraform `google_cloud_run_v2_service.neuron_sandbox` has
|
|
`lifecycle.ignore_changes = [template[0].containers[0].image]` so CI
|
|
rollouts won't be undone by the next `terraform apply`.
|
|
|
|
CI wiring lands as part of the real-soul-build follow-up backlog item
|
|
(see `mcp__neuron__planWork` for the tracked item).
|
|
|
|
## Disaster recovery
|
|
|
|
Sandbox is disposable. If state gets weird, nuke it and rebuild:
|
|
|
|
```bash
|
|
cd ~/Development/infrastructure/servers/gcp
|
|
terraform destroy \
|
|
-target=cloudflare_zero_trust_access_policy.sandbox_only_neurontech \
|
|
-target=cloudflare_zero_trust_access_application.sandbox \
|
|
-target=cloudflare_record.sandbox \
|
|
-target=google_cloud_run_domain_mapping.sandbox \
|
|
-target=google_cloud_run_v2_service_iam_member.neuron_sandbox_public \
|
|
-target=google_cloud_run_v2_service.neuron_sandbox \
|
|
-target=google_secret_manager_secret_iam_member.sandbox_url_accessor \
|
|
-target=google_secret_manager_secret_iam_member.sandbox_anon_accessor \
|
|
-target=google_secret_manager_secret_iam_member.sandbox_service_accessor \
|
|
-target=google_secret_manager_secret_version.sandbox_supabase_url_placeholder \
|
|
-target=google_secret_manager_secret_version.sandbox_supabase_anon_key_placeholder \
|
|
-target=google_secret_manager_secret_version.sandbox_supabase_service_key_placeholder \
|
|
-target=google_secret_manager_secret.sandbox_supabase_url \
|
|
-target=google_secret_manager_secret.sandbox_supabase_anon_key \
|
|
-target=google_secret_manager_secret.sandbox_supabase_service_key \
|
|
-target=google_service_account.neuron_sandbox \
|
|
-target=google_artifact_registry_repository.sandbox
|
|
|
|
# Then re-apply:
|
|
terraform apply
|
|
```
|
|
|
|
If the Supabase project was provisioned, also delete it from the Supabase
|
|
dashboard and re-create with a new name. The Secret Manager secrets carry
|
|
forward as placeholders; rewrite them with the new project's values.
|
|
|
|
## Files
|
|
|
|
| File | What |
|
|
|-------------------------------------------------------|-------------------------------------|
|
|
| `servers/gcp/sandbox.tf` | All sandbox terraform |
|
|
| `servers/gcp/sandbox/README.md` | This document |
|
|
| `~/Development/neuron-technologies/products/sandbox-stub/` | Stub source code (Go, 4 routes) |
|