commit 93096ea5b6054e2f6887a68b4a532606faa7f6c7 Author: Will Anderson Date: Sat May 2 12:53:00 2026 -0500 add sandbox stub service - 4-route placeholder until real soul wires up Go HTTP server with five handlers: GET / -> 200 {env, status, soul} GET /health -> 200 {ok:true} POST /api/share -> 410 not_available_in_sandbox GET /said -> 410 not_available_in_sandbox GET /share/* -> 410 not_available_in_sandbox any other -> 404 not_found Distroless final image. Cross-compiled on host (Apple Silicon QEMU + Go crashes with lfstack.push when go build runs inside an emulated linux/amd64 container). Pushed to us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:initial. Replaced when the real soul build pipeline lands. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f1c95f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +*.tfplan +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2e47c19 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# Pre-built binary in (this directory)/build/sandbox-stub-linux-amd64. +# We cross-compile on the host (Apple Silicon -> linux/amd64) instead of +# running `go build` inside an emulated amd64 container, which crashes Go's +# runtime (lfstack.push fatal) under QEMU/Rosetta. +# +# Build script: +# GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ +# go build -trimpath -ldflags="-s -w" -o build/sandbox-stub-linux-amd64 ./... +# docker buildx build --platform linux/amd64 \ +# -t us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:initial \ +# --push . + +FROM gcr.io/distroless/static-debian12:nonroot +COPY build/sandbox-stub-linux-amd64 /sandbox-stub +EXPOSE 8080 +USER nonroot:nonroot +ENTRYPOINT ["/sandbox-stub"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..ace58ea --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# sandbox-stub + +Four-route placeholder for `sandbox.neurontechnologies.ai`. Replaced when the +real soul build pipeline lands. + +## Routes + +| Method | Path | Status | Body | +|--------|-------------|--------|-------------------------------------------------| +| GET | `/` | 200 | `{"env":"sandbox","status":"ready","soul":"not_loaded"}` | +| GET | `/health` | 200 | `{"ok":true}` | +| POST | `/api/share`| 410 | `{"error":"not_available_in_sandbox"}` | +| GET | `/said` | 410 | `{"error":"not_available_in_sandbox"}` | +| GET | `/share/*` | 410 | `{"error":"not_available_in_sandbox"}` | +| any | other | 404 | `{"error":"not_found"}` | + +## Build + push + +```bash +cd ~/Development/neuron-technologies/products/sandbox-stub +gcloud auth configure-docker us-central1-docker.pkg.dev --quiet +docker buildx build --platform linux/amd64 \ + -t us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:initial \ + --push . +``` + +## Why 410 on share / said + +Sandbox runs experimental builds. Public artifact endpoints stay strictly in +prod. 410 Gone makes the lockdown explicit even before the real soul wires up. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..fb88c45 --- /dev/null +++ b/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Cross-compile and push the sandbox stub image. +# Builds the Go binary natively on the host (amd64 cross-target) to avoid +# the QEMU/Rosetta lfstack.push panic when `go build` runs inside an +# emulated linux/amd64 container. +set -euo pipefail + +cd "$(dirname "$0")" + +mkdir -p build + +echo ">> cross-compile linux/amd64" +GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ + go build -trimpath -ldflags="-s -w" -o build/sandbox-stub-linux-amd64 ./... + +echo ">> docker build + push" +docker buildx build --platform linux/amd64 \ + -t us-central1-docker.pkg.dev/neuron-785695/neuron-sandbox/sandbox:initial \ + --push . + +echo ">> done" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9d0a81f --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/neuron-technologies/sandbox-stub + +go 1.22 diff --git a/main.go b/main.go new file mode 100644 index 0000000..30de8a9 --- /dev/null +++ b/main.go @@ -0,0 +1,78 @@ +// sandbox-stub: 4-route placeholder for sandbox.neurontechnologies.ai. +// +// Replaces nothing in prod. Lives behind Cloudflare Access locked to +// email_domain == neurontechnologies.ai. Returns 410 Gone on the public +// share/artifact paths so the lockdown surface is explicit even before the +// real soul wires up. +// +// Routes: +// GET / -> 200 {"env":"sandbox","status":"ready","soul":"not_loaded"} +// GET /health -> 200 {"ok":true} +// POST /api/share -> 410 {"error":"not_available_in_sandbox"} +// GET /said -> 410 {"error":"not_available_in_sandbox"} +// GET /share/* -> 410 {"error":"not_available_in_sandbox"} +// * -> 404 {"error":"not_found"} +// +// Note: we use /health (not /healthz) because Cloud Run's frontend reserves +// /healthz and intercepts it before the request reaches the container. +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "strings" +) + +func writeJSON(w http.ResponseWriter, status int, body any) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("X-Sandbox", "true") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(body) +} + +func gone(w http.ResponseWriter, _ *http.Request) { + writeJSON(w, http.StatusGone, map[string]string{"error": "not_available_in_sandbox"}) +} + +func main() { + mux := http.NewServeMux() + + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, map[string]bool{"ok": true}) + }) + + mux.HandleFunc("/api/share", gone) + mux.HandleFunc("/said", gone) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // Lockdown: any /share/... path is also gone. + if strings.HasPrefix(r.URL.Path, "/share/") || r.URL.Path == "/share" { + gone(w, r) + return + } + + if r.URL.Path == "/" { + writeJSON(w, http.StatusOK, map[string]string{ + "env": "sandbox", + "status": "ready", + "soul": "not_loaded", + }) + return + } + + writeJSON(w, http.StatusNotFound, map[string]string{"error": "not_found"}) + }) + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + addr := ":" + port + log.Printf("sandbox-stub listening on %s", addr) + if err := http.ListenAndServe(addr, mux); err != nil { + log.Fatal(err) + } +}