fix(deploy)+feat(auth): self-host first-run bootstrap, anonymous demo, and guided OIDC/SSO setup - #4335
Merged
Merged
Conversation
…il loud Two first-run defects surfaced by dogfooding the Docker Compose self-host on a fresh VM: 1. Preflight (--write-secret) wrote deploy/secrets/* as 0400. Compose (non-swarm) bind-mounts these into /run/secrets/* preserving host perms (the mode/uid/gid long-syntax fields are swarm-only, ignored by compose), so the non-root container users — postgres UID 70 running initdb, the API app user — hit EACCES and crash-loop (e.g. Permission denied on /run/secrets/audit_hmac_key). Write 0644 instead; on a single-tenant self-host VM the host filesystem is the trust boundary. Aligned the manual secret-generation guidance (deploy/secrets/README.md, DEPLOY_QUICKSTART, DEPLOY_PLATFORM, compose header comments, init-wrapper hint) to 0644. 2. init.sql read init.app_password with missing_ok=true and silently skipped app-role creation when unset/empty, so a failed init-wrapper (e.g. unreadable secret) surfaced later as an opaque runtime "password authentication failed for user agent_bom_app". Now RAISE EXCEPTION to abort init loudly at the real root cause. Added regression tests: preflight writes 0644; init.sql raises (does not skip) when the app-password GUC is unset. k8s/Helm path confirmed unaffected — defaultMode 0400 stays readable under fsGroup 1000 (verified empirically on kind v1.36.1).
…pens without a sign-in wall DEMO_ESTATE + NO_AUTH_ROLE=viewer never took effect because AGENT_BOM_ALLOW_UNAUTHENTICATED_API was unset, so the seeded demo estate fell back to the API-key sign-in page instead of opening anonymously.
Add an `auth` command group with a `setup-oidc` subcommand that onboards
browser SSO ("Sign in with Google" or any OIDC issuer) without hand-wiring
env vars. It only collects, validates, and emits AGENT_BOM_OIDC_* config —
it does not fork or change the auth mechanism.
- Interactive (TTY) and fully flag-driven non-interactive modes.
- Derives the redirect URI as <base-url>/v1/auth/oidc/callback (the real
dashboard callback route) and defaults audience to the client ID (the
browser ID token aud convention).
- Emits AGENT_BOM_OIDC_ALLOW_DEFAULT_TENANT=1 for a single-tenant self-host
so login actually resolves a tenant; suppressed when --tenant-claim is set.
- Reuses oidc.discover_oidc for the issuer connectivity check (warns, never
fails) rather than forking discovery.
- Optional file write to deploy/secrets/oidc.env at mode 0644 (compose
env_file must be readable by the non-root API container); only on --write
or explicit confirmation, with a client-secret directory warning.
- Point the dashboard sign-in hint and a new docs/AUTH_SSO.md at the command.
msaad00
force-pushed
the
fix/self-host-secret-bootstrap
branch
from
July 21, 2026 05:07
cbfe471 to
2bcfa4b
Compare
agent-bom
enabled auto-merge (squash)
July 21, 2026 05:08
agent-bom
previously approved these changes
Jul 21, 2026
andres-linero
previously approved these changes
Jul 21, 2026
… aborts; document auth CLI group - init.sql: distinguish NULL app_password GUC (unset — Alembic migrations, integration-test bootstrap, wrapper-less dev provision the role out of band) from an empty GUC (misconfigured secret). NULL skips app-role creation; empty RAISE EXCEPTION. The previous unconditional RAISE broke the Postgres Integration Contract, which runs init.sql on the test DB without the GUC. - site-docs/reference/cli.md: add the 'auth' / 'auth setup-oidc' rows so check_cli_reference_alignment passes.
andres-linero
previously approved these changes
Jul 21, 2026
agent-bom
previously approved these changes
Jul 21, 2026
xdist workers reused threading.local coverage-warning state, so a prior offline_ecosystem_gap (no reason key) made test_package_scan_preserves_parser_coverage_warning KeyError on [0][reason]. Reset scan warning channels in the autouse fixture and assert by reason. Co-authored-by: W S <msaad00@users.noreply.github.com>
agent-bom
previously approved these changes
Jul 21, 2026
andres-linero
previously approved these changes
Jul 21, 2026
andres-linero
pushed a commit
that referenced
this pull request
Jul 21, 2026
…-backed auto-enable (#4336) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary Closes the 2026-07-21 audit **P0/P1** gaps in one PR (does not touch hosted-poc compose anonymous-demo wiring — that stays in #4335). - **Demo resilience:** `demo-redeploy` triggers on successful `Release` `workflow_run` (GITHUB_TOKEN-safe), builds images before `up -d`, fails loud if `DEMO_DEPLOY_DIR` is not a git checkout; Deployment Freshness probes `demo.agent-bom.com/health`; README documents `uvx agent-bom scan --demo --offline` fallback. - **MCP attest user surface:** `agent-bom attest mcp sign|verify` derives evidence from scan JSON; 0.96.4 changelog line narrowed to “foundation”; CLI_MAP + site-docs updated. - **Store-backed default path:** unset `AGENT_BOM_GRAPH_STORE_BACKED_BUILD` auto-enables above `AGENT_BOM_GRAPH_STORE_BACKED_MIN_ENTITIES` (default 5000); explicit off preserved; ADR-006 + env allowlist updated. - **README visuals:** how-it-works + blast-radius light/dark diagrams simplified to the three-lane product story and a single blast-radius path. - **CI hygiene:** drop no-op `dependency-pin-check.yml` and the non-blocking main-only `ci.yml` `agent-bom-scan` job (covered by PR Security Gate + post-merge self-scan). ## Verification ```bash uv run ruff check src/ uv run mypy src/agent_bom/mcp_scan_attestation.py src/agent_bom/cli/_attest_group.py src/agent_bom/api/pipeline.py --ignore-missing-imports --disable-error-code import-untyped uv run pytest -q tests/test_mcp_attest_cli.py tests/api/test_store_backed_auto_enable.py tests/test_mcp_scan_attestation.py # 28 passed uv run pre-commit run --files <changed files> # ruff/format/bandit/env-var/comment hygiene passed; mypy hook follows imports into pre-existing no-any-return elsewhere uv run agent-bom scan --demo --offline -f json -o /tmp/demo-smoke.json # demo smoke produced findings JSON (exit 1 expected for critical findings) uv run agent-bom attest mcp --help ``` ## Notes - Host restore for a live 502 remains an operator/VM concern; this PR hardens redeploy trigger + monitoring + local fallback. - Residual store-backed peak is still O(N) — docs state measured reduction, not a strict memory bound. - Intentionally avoids `deploy/docker-compose.hosted-poc.yml` to stay clear of #4335. - Develop-skill style local gates run before this push; waiting on CI to finish (do not stack follow-up commits unless CI is red for a real failure). [how-it-works light](https://cursor.com/agents/bc-a579ae44-c48d-4515-8cf6-2da701406ba6/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhow-it-works-light.svg) [blast-radius light](https://cursor.com/agents/bc-a579ae44-c48d-4515-8cf6-2da701406ba6/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fblast-radius-light.svg) <sub>To show artifacts inline, <a href="https://cursor.com/dashboard/cloud-agents#my-pull-requests">enable</a> in settings.</sub> <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-a579ae44-c48d-4515-8cf6-2da701406ba6"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-a579ae44-c48d-4515-8cf6-2da701406ba6"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: W S <msaad00@users.noreply.github.com>
The scanner-coverage-warning reset commit also deleted the auth/storage/server env-var restoration loop in reset_global_test_state(). Without it, a test that sets AGENT_BOM_POSTGRES_URL (or rate-limiter env) leaks it into later tests in the same xdist worker, which then fail with 'psycopg not installed' / 'Postgres rate limiter could not initialize'. Restore the loop (kept on main); the _reset_scan_warning_state() addition stays.
auto-merge was automatically disabled
July 21, 2026 15:36
Pull request was closed
agent-bom
enabled auto-merge (squash)
July 21, 2026 15:38
agent-bom
approved these changes
Jul 21, 2026
andres-linero
approved these changes
Jul 21, 2026
agent-bom
added a commit
that referenced
this pull request
Jul 21, 2026
…all (#4337) Fixes six first-run bugs in the Helm chart — the k8s analogs of the Docker-Compose self-host issues (#4335). Found by static audit, fixed and verified with `helm lint`/`template` + a kind-cluster test. No live EKS install was run (see residual). ## Bugs fixed 1. **Half-wired auth Secret (HIGH).** The recommended `focused-pilot`/`eks-vanilla`/`sqlite-pilot` profiles `envFrom` a Secret (`agent-bom-control-plane[-auth]`) that no template creates and no example documented → pods stuck `CreateContainerConfigError` on first install. Ship `examples/control-plane-auth-secret.example.yaml` (one block per profile, every key commented + generate command) and document it in `examples/README.md` with a per-profile required-key matrix. 2. **Multi-replica login trap (HIGH).** Default `api.replicas: 2` makes `AGENT_BOM_BROWSER_SESSION_SIGNING_KEY` mandatory (`browser_session.py` fails closed with `BrowserSessionError`), but no profile listed it → UI login dead. Now in the documented required-key set. 3. **Snowflake key mode (MED).** `cronjob.yaml` mounted the key `defaultMode: 0400`; set to `0440`. (Kind test showed kubelet fsGroup already rewrites 0400→0440 on disk, so this was not a live break — `0440` makes the manifest match enforced reality.) 4. **Dead-dashboard auth posture (MED).** Non-OIDC profiles had no configured auth backend. Each profile now documents a working backend; the sqlite demo gets a visible `AGENT_BOM_ALLOW_UNAUTHENTICATED_API=1` (viewer). 5. **Undocumented required audit key (LOW).** `eks-vanilla` forces `AGENT_BOM_REQUIRE_AUDIT_HMAC=1`; `AGENT_BOM_AUDIT_HMAC_KEY` is now in its required-key list. 6. **Broken quick-start in values header (LOW).** `values.yaml` header no longer implies a bare `--set controlPlane.enabled=true` works; points at a profile + the Secret requirement (matching README). The migration-job `fail` guard is untouched. **S1 (cookie-secure):** no change needed — `_session_cookie_secure` already defaults Secure for production/clustered (replicas>1); both TLS profiles run replicas 2. Forcing it on the plain-HTTP sqlite demo would drop the login cookie. CORS confirmed unneeded (same-origin proxying). ## Secret keys (confirmed against code, not guessed) `AGENT_BOM_POSTGRES_URL`/`AGENT_BOM_DB`, `AGENT_BOM_BROWSER_SESSION_SIGNING_KEY`, `AGENT_BOM_AUDIT_HMAC_KEY`, `AGENT_BOM_CONNECTIONS_KEY`, `AGENT_BOM_API_KEYS` (`key:role`). ## Validation - `helm lint deploy/helm/agent-bom` → 0 failed. - `helm template` for each affected profile → renders; every `secretRef` now resolves to a documented example. - `pytest tests/test_deploy_manifests.py` → 70 passed (4 new tests proven RED on main → GREEN). - Kind-cluster test confirmed the fsGroup/0400→0440 mount behavior. - Chart/appVersion + image tags stay 0.97.1 (version-alignment gate untouched). ## Residual Verified at render + secret-key-contract + code-behavior + kind level. A full end-to-end `helm install` against real RDS/ALB/IRSA (live browser login on EKS) was not run. Co-authored-by: Andres Linero <andres.nyc.col@gmail.com> Co-authored-by: agent-bom <andwgdysaad@gmail.com>
andres-linero
pushed a commit
that referenced
this pull request
Jul 21, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary - Bump release-managed package, UI, Docker, Helm, registry, OpenClaw, Snowflake, and docs surfaces to **0.97.2**. - Move `[Unreleased]` into the `0.97.2` changelog covering auth/OIDC bootstrap (#4335), Helm first-install auth Secrets (#4337), attest CLI + demo/store-backed graph (#4336), and MCP honesty / audit exit codes (#4346). - Promote simplified teal blast-radius SVGs (replacing the denser violet-accent art that lagged how-it-works) and align `product-screenshots.json` metadata (no full PNG recapture). ## Verification ```bash uv run agent-bom --version # 0.97.2 uv run agent-bom scan --demo --offline -o /tmp/ab-demo-scan.json uv run agent-bom attest mcp sign ... --server filesystem-server --tenant-id demo # signed=true uv run agent-bom attest mcp verify ... --public-key ... --tenant-id demo uv run agent-bom auth setup-oidc --provider google --client-id demo-client --base-url https://example.internal --non-interactive helm lint deploy/helm/agent-bom helm template ab deploy/helm/agent-bom -f deploy/helm/agent-bom/examples/eks-control-plane-sqlite-pilot-values.yaml uv run python scripts/bump-version.py 0.97.2 --check uv run python scripts/check_release_consistency.py uv run python scripts/check_product_surface_contract.py uv run python scripts/export_openapi.py --check uv run python scripts/generate_agent_capability_manifest.py --check uv run python scripts/lint_release_workflow.py .github/workflows/release.yml uv run pytest -q tests/test_version_alignment.py tests/test_mcp_attest_cli.py # 9 passed curl -fsS https://demo.agent-bom.com/health # status=ok, version=0.97.1 (hosted still on prior tag until release), unauthenticated_allowed=true ``` ## Notes - Prepares the release only; does **not** tag or publish PyPI/Docker. - After merge: tag `v0.97.2` via the normal Release workflow. - `#4344` (external pre-install dependency risk signal proposal) is intentionally deferred — not a patch blocker. - Screenshot PNGs remain the same-day 0.97.1 DEMO DATA capture; manifest `visible_version`/`release_version` aligned for the release gate. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-a579ae44-c48d-4515-8cf6-2da701406ba6"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-a579ae44-c48d-4515-8cf6-2da701406ba6"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: W S <msaad00@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related self-host improvements surfaced by dogfooding a fresh
docker compose up, combined into one PR.Part 1 — first-run bootstrap fixes (every item reproduced on a fresh box)
deploy/secrets/*as0400 root. Compose (non-swarm) bind-mounts them into/run/secrets/*preserving host perms (themode/uid/gidlong-syntax fields are swarm-only), so the non-root API user and non-root postgres init user getPermission denied— the API crash-loops ([Errno 13] Permission denied: '/run/secrets/audit_hmac_key') and postgres can't read the app password. Preflight now writes0644; the manualchmod 0400guidance in docs/compose comments is aligned so it stops re-triggering the crash-loop.init.sqlread the app password withcurrent_setting('init.app_password', true)(missing_ok) and createdagent_bom_appwith a NULL password when the secret was unreadable — surfacing later as a confusingpassword authentication failed for user "agent_bom_app". It nowRAISE EXCEPTIONat the real root cause.hosted-pocsetDEMO_ESTATE=1+NO_AUTH_ROLE=viewerbut notALLOW_UNAUTHENTICATED_API=1, so the seeded demo estate fell back to the API-key sign-in wall. Flag added (+ test assertion updated).k8s/Helm is not affected — the chart runs non-root with
fsGroup, keepingdefaultMode: 0400secret mounts readable (verified on a kind cluster). Untouched.Part 2 — guided OIDC/SSO onboarding (
agent-bom auth setup-oidc)Onboarding on top of the existing browser-OIDC mechanism — collects, validates, and emits config; does not fork or touch auth/RBAC/token-validation.
authcommand group +setup-oidcwizard (interactive + non-interactive flags). Derivesredirect_uri = <base>/v1/auth/oidc/callback(verified against the real route), defaultsAGENT_BOM_OIDC_AUDIENCEto the client ID (matchesverify_browser_id_token), and emitsAGENT_BOM_OIDC_ALLOW_DEFAULT_TENANT=1for the single-tenant Google case (no tenant claim). Connectivity check reuses the existing OIDC discovery. Writes an env file only on--write/confirmation (0644, so the composeenv_fileis readable by the non-root API container).docs/AUTH_SSO.md— 5-minute Google path + generic OIDC (Okta/Entra/Auth0/Keycloak), cross-linked from ENTERPRISE_DEPLOYMENT.Validation
pytest tests/test_auth_setup_oidc.py tests/test_hosted_poc_preflight.py tests/test_compose_secrets_healthcheck.py→ 61 passed (discovery mocked, no network).ruff check/ruff format/mypyclean on changed py; CLI registration/help suites pass (no regression).init.sqlvalidated against a live postgres:16: empty password → init aborts; happy path → role created with password.npm run lint/buildnot run in the ephemeral worktree — flagged as the one unverified gate.