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
- Sign in with your
@neurontechnologies.aiGoogle account athttps://sandbox.neurontechnologies.ai/. Cloudflare Access intercepts the request, redirects you to the Google IdP, and only allowsemail_domain == neurontechnologies.aithrough. - After auth, you land on the running stub. With the stub deployed, the
root response is:
{"env":"sandbox","status":"ready","soul":"not_loaded"} - 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:
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_KEYis intentionally not mounted onneuron-sandbox-us. Defense-in-depth against an experimental soul accidentally emailing real users. - No public share endpoints.
/api/share,/said, and/share/<id>return410 Gonewith{"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-sandboxSA has zero project-level role bindings. Its only grants areroles/secretmanager.secretAccessoron the three sandbox-specific secrets. Verify with:# 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.
- Sign in to https://supabase.com/dashboard with the Neuron Technologies org owner account.
- 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). - After provisioning, capture from Project Settings -> API:
- Project URL:
https://<ref>.supabase.co anonpublic keyservice_rolesecret key
- Project URL:
- Push them into Secret Manager (this overwrites the placeholder versions
that terraform created):
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 - Force a new sandbox revision so it picks up the secrets:
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 - 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)
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:
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) |