From 54dfb861c4f3838a2a0a97f0d636280a5e3cc4e4 Mon Sep 17 00:00:00 2001 From: danielfdez17 Date: Thu, 5 Nov 2026 16:51:34 +0100 Subject: [PATCH] docs(modules-of-choice): DEFENSE demos and README links for #177 live gates Three-step demos for the factory major, SSRF minor, and collab m175. Row 14 points at grobase PR #98 live Integration Tests and the static Modules of choice workflow; SSRF stays its own minor with the unit-test demo called out. --- README.md | 13 ++++++++---- wiki/DEFENSE.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 046770765..a4b0eb84f 100644 --- a/README.md +++ b/README.md @@ -710,8 +710,8 @@ prismatica `ci.yml`. | 11 | Health/status page, backups, disaster recovery | DevOps | Minor | 1 | **B** | `GB infra/docker/services/status-page/{server,availability}.py`; `GB orchestrators/compose/base/ops.yml`; `GB scripts/verify/m241-status-backup-recovery.sh` | m241 → GB `operational-reliability.yml:27`; m192 → GB-CI (`ci.yml:582`), **red on grobase `develop`** ([run 41870904860](https://github.com/Univers42/grobase/actions/runs/41870904860)) | [`wiki/operations/04-troubleshooting.md`](wiki/operations/04-troubleshooting.md) | bring up the `ops` plane (not in the default edition) → open the status page → stop a container, then `m241 --live` | | 12 | GDPR compliance | Data | Minor | 1 | **B** | `OS scripts/bridge-account-export.mjs:143`; `OS scripts/bridge-account-erasure.mjs:79`; `OS src/features/settings/ui/AccountExportSection.tsx`; `OS src/features/settings/ui/danger/AccountDangerZone.tsx` | `OS tests/bridge/bridge-account-export*` (3), `bridge-account-erasure` (1) → OS-CI Bridge tests; `G .github/workflows/models-grants.yml` | [`wiki/security/01-model.md`](wiki/security/01-model.md) | Settings › export my data → open the archive → delete the account | | 13 | Two-factor authentication | User management | Minor | 1 | **B** | `OS scripts/bridge-account-mfa-totp.mjs:68`; `OS scripts/bridge-account-mfa.mjs:187`; `OS scripts/bridge-account-passkeys.mjs:100`; `OS src/features/settings/profile/TwoFactorModal.tsx` | `OS tests/bridge/bridge-account-mfa-*` (5), `bridge-account-passkeys` (1) → OS-CI Bridge tests | osionos `docs/account-platform` | enable TOTP → sign out → sign in with the code | -| 14 | **Module of choice:** contract-driven application factory | Free choice | Major | 2 | **B** | `GB src/control-plane/internal/tenants/selfserve_apps.go:55-58`; `GB src/control-plane/cmd/tenant-control/mount_appsselfserve.go:26`; `GB scripts/verify/m177-selfserve-apps.sh` | m176 → GB-CI (`ci.yml:192`, static leg, in the Shellcheck job); m176 and m177 → groot `modules-of-choice.yml:39-40`, static mode only | [`wiki/platform/06-contracts.md`](wiki/platform/06-contracts.md), [justification](#justification--module-of-choice-major-contract-driven-application-factory) | set `APPS_SELFSERVE_ENABLED` (empty by default) → OTP login, `POST /v1/tenants/me/apps` twice → one app's key reads the other: 4xx | -| 15 | **Module of choice:** SSRF guard on the HTTP engine | Free choice | Minor | 1 | **B** | `GB src/data-plane-router/crates/data-plane-pool/src/dial_guard/classify.rs:90-155`; `GB src/data-plane-router/crates/data-plane-pool/src/http/validate.rs:83-153` | `GB …/dial_guard/classify_tests.rs` → GB-CI `rust-data-plane` + "Engine conformance (http)" | [`wiki/security/04-network.md`](wiki/security/04-network.md), [justification](#justification--module-of-choice-minor-ssrf-guard-on-the-http-engine) | mount an `http` source at `169.254.169.254` → query it → refused. Whether the default edition mounts the `http` engine is unverified (guess: it does not) | +| 14 | **Module of choice:** contract-driven application factory | Free choice | Major | 2 | **B** | `GB src/control-plane/internal/tenants/selfserve_apps.go:55-58`; `GB src/control-plane/cmd/tenant-control/mount_appsselfserve.go:26`; `GB scripts/verify/m177-selfserve-apps.sh` | Static: m176/m177 → groot [`modules-of-choice.yml`](.github/workflows/modules-of-choice.yml); m176 → GB-CI Shellcheck. Live (`BAAS_VERIFY_LIVE=1`): GB Integration Tests on [Univers42/grobase#98](https://github.com/Univers42/grobase/pull/98) — m177 PASS on [run 42059555959](https://github.com/Univers42/grobase/actions/runs/42059555959) | [`wiki/platform/06-contracts.md`](wiki/platform/06-contracts.md), [`wiki/DEFENSE.md`](wiki/DEFENSE.md), [justification](#justification--module-of-choice-major-contract-driven-application-factory) | set `APPS_SELFSERVE_ENABLED=1` → OTP/password login → `POST /v1/tenants/me/apps` twice → own key 200, cross key 404 ([DEFENSE demos](wiki/DEFENSE.md)) | +| 15 | **Module of choice:** SSRF guard on the HTTP engine | Free choice | Minor | 1 | **B** | `GB src/data-plane-router/crates/data-plane-pool/src/dial_guard/classify.rs:90-155`; `GB src/data-plane-router/crates/data-plane-pool/src/http/validate.rs:83-153` | `GB …/dial_guard/classify_tests.rs` and `ssrf_blocks_internal_and_metadata_ips` → GB-CI `rust-data-plane` + "Engine conformance (http)" | [`wiki/security/04-network.md`](wiki/security/04-network.md), [`wiki/DEFENSE.md`](wiki/DEFENSE.md), [justification](#justification--module-of-choice-minor-ssrf-guard-on-the-http-engine) | `cargo test -p data-plane-pool ssrf_blocks_internal_and_metadata_ips` (blocks `169.254.169.254`); kept as its own minor, not folded into row 14 | | 16 | Complete LLM system interface: text, streaming, error handling, rate limiting. Images exist but are **off by default** and not part of this claim | Artificial Intelligence | Major | 2 | **B** | osionos `services/osionos-ai/src/routes/runs.ts:178-324`, `src/runs/{runner,upstream,upstreamError,streamPump}.ts`, `src/rateLimit.ts:84`, `src/http.ts:108-129`; client `src/features/ai/model/runStream.ts`. All 12 hops: [`wiki/project/ai-llm-interface.md`](wiki/project/ai-llm-interface.md) | `services/osionos-ai/tests/{runs,complete,upstreamError*,runRateLimit,failover,quotaFailover}.test.ts` → OS-CI "AI service (tsc + suite)" (`ci.yml:314`); `tests/canvas/ai-{chat-store,run-dispatch,run-stream}.test.ts` → "quality (root)" Canvas unit tests (`ci.yml:285`) | [`wiki/project/ai-llm-interface.md`](wiki/project/ai-llm-interface.md), [`wiki/runbooks/osionos-ai.md`](wiki/runbooks/osionos-ai.md), [M1 sheet](https://github.com/Univers42/osionos/blob/develop/docs/ai/modules/m1-images.md) | `docker compose --profile ai up -d --build` → ask a question and watch it stream → reload mid-answer (it resumes), or stop it | | 17 | Complete RAG on a large dataset | Artificial Intelligence | Major | 2 | **B** | osionos `services/osionos-ai/src/retrieval/search.ts:277,469,564`, `src/retrieval/rerank.ts:16`; `G models/osionos-ai-rag-migration.sql:60,91` | `services/osionos-ai/tests/{retrieval,aclAgreement,rerank}.test.ts` → OS-CI AI service | [M2 sheet](https://github.com/Univers42/osionos/blob/develop/docs/ai/modules/m2-rag.md), osionos `services/osionos-ai/eval/wiki/RESULTS-rag.md` | `--profile ai` → ask a workspace question → click a citation, then show the 110 363-chunk measurement (run on a separate eval stack; the reranker is off by default) | | 18 | Content moderation AI | Artificial Intelligence | Minor | 1 | **B** | osionos `scripts/bridge-moderation-gate.mjs:70`; `services/osionos-ai/src/moderation/l1.ts:99` | `services/osionos-ai/tests/moderation*.test.ts` (4) → OS-CI AI service; `tests/bridge/bridge-moderation*` (5) → Bridge tests | [M4 sheet](https://github.com/Univers42/osionos/blob/develop/docs/ai/modules/m4-moderation.md) | set `OSIONOS_MODERATION_ACTIONS=hold` and `OSIONOS_MODERATION_STATE=1` → post a toxic comment → it waits in the admin console (no auto-removal, by design) | @@ -761,7 +761,10 @@ is new at A. The rows below no longer count. - **Why it deserves a Major.** It spans the Go control plane, the Rust data plane and the database layer, and it comes with isolation proofs: gate `m177` proves self-serve creation, and gate `m176` proves one application cannot read another's data (a foreign mount resolves to zero - rows — a 404, never a cross read). + rows — a 404, never a cross read). Static legs run in groot + [`Modules of choice`](.github/workflows/modules-of-choice.yml); the live half is in grobase + Integration Tests ([Univers42/grobase#98](https://github.com/Univers42/grobase/pull/98)). + Three-step demos: [`wiki/DEFENSE.md`](wiki/DEFENSE.md). ### Justification — Module of choice (Minor): SSRF guard on the HTTP engine @@ -773,7 +776,9 @@ is new at A. The rows below no longer count. later DNS rebind cannot redirect it inward. - **Value and weight.** Small, self-contained and demonstrable in seconds (a mount pointing at `169.254.169.254` is refused), and it shows security - judgement on a feature we chose to build. + judgement on a feature we chose to build. Kept as a separate minor from the factory major — + a validator may still fold it into the data plane; say that risk aloud. Demo: + [`wiki/DEFENSE.md`](wiki/DEFENSE.md). --- diff --git a/wiki/DEFENSE.md b/wiki/DEFENSE.md index 809a1c8b5..87408b30e 100644 --- a/wiki/DEFENSE.md +++ b/wiki/DEFENSE.md @@ -70,6 +70,60 @@ have none, so that category and the AI-opponent module are out — and **custom four-part justification in the README (why, what challenge, what value, why that weight). Our recommendation: the contract-driven factory as major, the SSRF guard as minor. +### Demo — Major: contract-driven application factory (m176 + m177) + +From a clean clone with `apps/grobase` at the pin (`git submodule update --init apps/grobase`): + +1. **Static proof (no stack).** + `bash apps/grobase/scripts/verify/m177-selfserve-apps.sh` + Expect `m177 PASS` and checks naming the flag-gated mount, `EnsureDatabase`, and the slug/DSN + Go tests. + `bash apps/grobase/scripts/verify/m176-contract-isolation-live.sh` + Expect `m176 PASS` and the resolver gated on `tenant_id`. + Same gates in CI: [`Modules of choice`](../.github/workflows/modules-of-choice.yml). + +2. **Self-serve create (live stack).** With `make all` (or grobase `make up`) and + `APPS_SELFSERVE_ENABLED=1` on tenant-control, a logged-in JWT: + `curl -sS -X POST "$KONG_URL/v1/tenants/me/apps" -H "Authorization: Bearer $DEV_JWT" -H 'Content-Type: application/json' -d '{"name":"alpha"}'` + Expect JSON with a fresh `db_id`, `api_key` and `tenant`. Create a second app (`beta`); the two + `db_id`s differ. + +3. **Cross-app isolation (live).** + `curl -sS -o /dev/null -w '%{http_code}\n' "$KONG_URL/query/v1/$BETA_DB/tables" -H "apikey: $ANON_KEY" -H "X-Baas-Api-Key: $ALPHA_KEY"` + Expect `404` (foreign key → zero mount rows). Own key on own `db_id` is `200`. + Live CI: grobase Integration Tests runs `m177` and `m176` with `BAAS_VERIFY_LIVE=1` + ([#177](https://github.com/Univers42/groot/issues/177)). + +### Demo — Minor: SSRF guard on the HTTP engine + +Kept as its own minor (not folded into the major): it is a security judgement on the HTTP mount +engine, not a factory feature. Risk to name aloud: a validator may call it "part of the data +plane" and refuse the separate point — have the unit-test demo ready and the README justification +open. + +1. **Unit proof (the claim they ask for).** From `apps/grobase`: + `cargo test -p data-plane-pool ssrf_blocks_internal_and_metadata_ips -- --nocapture` + Expect the test to pass while listing `169.254.169.254` among the blocked addresses + (`src/data-plane-router/crates/data-plane-pool/src/http/mod.rs`). + +2. **Same suite in CI.** grobase `CI` runs `cargo test --workspace`; the 30 HTTP SSRF tests are + in that job. Point at a green run on `main`. + +3. **What you say while it runs.** A mount whose base URL resolves to loopback, RFC 1918, + link-local (including cloud metadata) or an IPv4-mapped form is refused before the dial; the + connection is then pinned to the validated public IP so a later DNS rebind cannot walk inward. + +### Demo — Collab namespace isolation (m175, supports rows 2 and 10) + +1. `bash apps/grobase/scripts/verify/m175-collab-namespace-isolation.sh` + Expect the realtime-core authorisation tests green (wildcard cannot reach `collab:` when the + protected list is set). +2. Live (optional): `BAAS_VERIFY_LIVE=1 bash apps/grobase/scripts/verify/m175-collab-namespace-isolation.sh` + on a stack with realtime — wildcard denied on `collab:`, exact grant allowed, parity when the + flag is off. +3. Product click: two browsers on one osionos page; each sees the other's cursor. Raise + `EDITION=realtime|prod|full` first. + --- ## Code quality and teamwork