diff --git a/Makefile b/Makefile index 08d0b06..0b733fb 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,8 @@ control-plane-forward: ## — forward the Sandbox Control Plane API to 127.0.0.1 port-forward deploy/sandbox-control-plane 18080:8080 console-forward: ## — forward Console to 127.0.0.1:18081 + @echo "Console: http://127.0.0.1:18081" + @echo "Local login key (run in another terminal): make --no-print-directory dev-token" KUBECONFIG=$(KUBECONFIG) kubectl --context $(SANDBOX_KUBE_CONTEXT) --namespace sandbox-system \ port-forward deploy/sandbox-console 18081:8080 diff --git a/README.md b/README.md index fdd78b1..815e54e 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ operation fails; it never falls back to running on the host. ```bash make bootstrap # create .venv and install SDK + test dependencies -make test # 833 unit and contract tests, no network, no cluster +make test # 834 unit and contract tests, no network, no cluster make verify # complete Python, Console, manifest, Helm, wheel gate make help # every Make target with its one-line description ``` @@ -147,6 +147,25 @@ Lima cluster, and proves all of the following against the live deployment: - an unavailable Control Plane fails closed without executing on the host; - `/healthz`, `X-Request-Id`, and Prometheus metrics are visible. +### Open the Console + +After `make quickstart` succeeds, keep the Console forward running in one terminal: + +```bash +make console-forward +``` + +Open . In another terminal, print the local login key: + +```bash +make --no-print-directory dev-token +``` + +Paste that value into **API key** and choose **Enter console**. The local key is +administrator-equivalent: do not share it or put it in a URL. The Console retains it +only in that browser tab's `sessionStorage`; signing out or closing the tab removes it. +Production deployments should use OIDC or a revocable API key instead. + The command ends with the first Runtime-call latency and total elapsed time. It also writes `.sandbox/quickstart-summary.json` and `.sandbox/showcase-result.json`, so install success, phase duration, manual diff --git a/scripts/quickstart.sh b/scripts/quickstart.sh index 869d243..b9a4e4b 100755 --- a/scripts/quickstart.sh +++ b/scripts/quickstart.sh @@ -97,3 +97,12 @@ run_phase local-cluster make -C "$REPO_ROOT" --no-print-directory up-local run_phase value-proof make -C "$REPO_ROOT" --no-print-directory smoke-local STATUS=passed CURRENT_PHASE=complete + +cat <<'EOF' + +Open the Console: + 1. Run `make console-forward` in one terminal. + 2. Open http://127.0.0.1:18081. + 3. Run `make --no-print-directory dev-token` in another terminal and paste + the value into "API key". The local key is administrator-equivalent. +EOF diff --git a/scripts/smoke-local.py b/scripts/smoke-local.py index 3ee7040..488f2a9 100755 --- a/scripts/smoke-local.py +++ b/scripts/smoke-local.py @@ -153,7 +153,8 @@ def main() -> int: print(f"First Runtime call: {first_call_seconds:.3f}s") print(f"Value proof total: {duration:.3f}s") print(f"Evidence: {result_path}") - print("Console: run `make console-forward`, then open http://127.0.0.1:18081") + print("Console: run `make console-forward`, open http://127.0.0.1:18081,") + print("then paste the value from `make --no-print-directory dev-token` into API key") return 0 diff --git a/tests/test_documentation.py b/tests/test_documentation.py index a05ca9f..803ad47 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -142,6 +142,16 @@ def test_console_credential_wording_matches_browser_storage(self) -> None: auth_source = (ROOT / "console/src/auth.ts").read_text(encoding="utf-8") self.assertIn("window.sessionStorage.setItem", auth_source) + def test_readme_closes_the_local_console_login_loop(self) -> None: + readme = (ROOT / "README.md").read_text(encoding="utf-8") + heading = readme.index("### Open the Console") + section = readme[heading:readme.index("\n## ", heading + 4)] + self.assertIn("make console-forward", section) + self.assertIn("http://127.0.0.1:18081", section) + self.assertIn("make --no-print-directory dev-token", section) + self.assertIn("API key", section) + self.assertIn("administrator-equivalent", section) + def test_dev_token_make_target_does_not_echo_its_recipe(self) -> None: makefile = (ROOT / "Makefile").read_text(encoding="utf-8") target = makefile.split("dev-token:", 1)[1].split("\n\n", 1)[0]