Repository navigation
feat(router): promote stateless sandbox evaluator to production, keep /share out (#3825) - #3826
PetrefiedThunder wants to merge 2 commits into
Conversation
… /share out (#3825) /tools/inflow-lab ships in production and POSTs to /api/ingestion/api/v1/sandbox/evaluate, which always 404'd because `sandbox` was absent from PRODUCTION_ENABLED_ROUTERS. experimental-features.ts already documented the symptom: "the sandbox-evaluate feeder inside the page degrades to an inline error." Promoting the router as it stood would also have mounted two endpoints that do not belong on the production surface. POST /share is an unauthenticated, tenant-less write that stores up to 2MB of caller-supplied CSV in sandbox_shares for 30 days; its share_url points at /sandbox/results/[id], a page deleted when the public sandbox was retired, so a successful share returns a link to a 404; and its 10/hour/IP cap is a per-process dict that does not hold across replicas. So split rather than promote wholesale: - Move /share and /share/{id} into sandbox/share_router.py under a new `sandbox_share` owner, which stays experimental and unpromoted. - Promote `sandbox` (evaluate + trace) into PRODUCTION_ENABLED_ROUTERS. Both are stateless: they persist nothing, accept no tenant id, and read no tenant data, so there is no tenant boundary to cross. Bounded by a per-IP rate limit and 2MB / 500-event caps. - Classify both as PUBLIC_OPERATIONS with a none:stateless-no-persistence storage boundary; the route-complete manifest rejected them as unclassified until this was explicit. Promotion-gate evidence per PRODUCTION_SURFACE.md:112-120 lives in test_sandbox_production_promotion_3825.py (21 cases): tenant isolation, contract, failure modes, and guards that fail if a write path, a tenant field, or a third route is ever added to the promoted router — plus one asserting sandbox_share is never in the production allowlist. Verification: full ingestion suite 0 failures; router policy, router-surface doc invariant, route-security manifest, server import smoke, monolith route parity, SDK route contract, OpenAPI production surface and tests/invariants all green; manifest --check clean at 261 enabled / 178 omitted-404. DB-backed tests skipped locally (no Postgres/Docker in this environment). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014PP3CSvvRruKPuYTrLoXzq
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
PR Blocker ChecklistHead SHA: Status: Ready
|
✅ Bundle Size Report
Bundle is within the 10.0MB limit. |
|
|
Vercel preview: https://regengine-hlleswb6p-petrefiedthunders-projects.vercel.app Access is via Vercel login (Deployment Protection). |
…3825) The repo-root security/RLS/isolation suite caught a pinned copy of the production allowlist that my grep for `^ENABLED_ROUTERS=` missed, because it is a JSON key rather than an env line: Release-evidence validation failed: - docs/security/release-evidence/2026-08-03-security-remediation-candidate.json: $.enabled_routers: must exactly match production policy (missing=['sandbox'], extra=[]) scripts/validate_release_evidence.py requires that field to equal PRODUCTION_ENABLED_ROUTERS exactly, so promoting a router means updating the record. Rather than silently appending to a security artifact, the note now records that sandbox was promoted after this record's 2026-08-26 verification date, was NOT exercised by it, and carries its own promotion-gate evidence. The record is an explicitly unapproved candidate (status not_approved, release_approved false) whose route_manifest counts were already stale against main, so the field is a living constraint rather than a frozen attestation. Also mirrors the share-router split into services/ingestion/main.py. That legacy per-service entrypoint mounts app.sandbox.router directly, so after the split it would have silently dropped both /share endpoints; server/main.py documents that the two mount sites are kept in sync. Verified: validate_release_evidence.py and generate_route_security_manifest.py --check both clean; check_enabled_outbound_http.py passes; the failing job's non-DB steps all green locally (outbound-HTTP guard, disabled-router task handlers, release evidence, anti-stub canonical guard); route parity, router policy, route-security manifest, router-surface doc invariant and import smoke green; sandbox suites green. The job's remaining steps need isolated test databases, which this environment has no Postgres for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014PP3CSvvRruKPuYTrLoXzq
Summary
sandbox(the stateless/evaluate+/tracepair) intoPRODUCTION_ENABLED_ROUTERS, so the Inflow Lab CSV checker stops 404ing in production./shareendpoints intosandbox/share_router.pyunder a new, unpromotedsandbox_shareowner, so promotion does not drag an unauthenticated write path onto the production surface.PRODUCTION_SURFACE.md:112-120, including guards that fail if the promoted router ever gains a write path, a tenant field, or a third route.Why
/tools/inflow-labships in production (HTTP 200) andInflowLabClient.tsx:1104POSTs to/api/ingestion/api/v1/sandbox/evaluate, which always 404'd.frontend/src/lib/experimental-features.ts:77already documented the symptom: "the sandbox-evaluate feeder inside the page degrades to an inline error." Probed before the change:Note this supersedes the original framing: the per-route allowlist this was once going to add already exists, and production now rejects
ENABLE_EXPERIMENTAL_ROUTERSoutright. The only remaining step was promotion by name.Why
/shareis not promoted with itThe allowlist is router-granular, so promoting
sandboxas it stood would have mounted all four endpoints. Three independent reasons that was wrong:POST /shareinserts up to 2 MB of caller-supplied CSV — plausibly real supplier data — intosandbox_shareswith no auth principal, retained 30 days. The table exists (docs/schema/expected_schema.json:6766), so it would have been live.share_url=f"/sandbox/results/{share_id}..."; that page was deleted when the public sandbox was retired, so a successful share returns a link to a 404._share_bucketsis a per-process dict, so the 10/hour/IP cap is per-replica, not per-deployment.Inflow Lab only calls
/evaluate, so nothing needed/shareto ship.Changes
server/router_policy.pysandbox→PRODUCTION_ENABLED_ROUTERS, out ofEXPERIMENTAL_ROUTERS; newsandbox_shareinVALID_ROUTER_NAMES+EXPERIMENTAL_ROUTERSserver/main.pysandbox_sharemounts behind its own_router_enabledgatesandbox/share_router.pysandbox/router.pyserver/route_security_policy.pyPUBLIC_OPERATIONSwith anone:stateless-no-persistenceboundarydocs/security/route-security-manifest.jsonROUTER_SURFACE.md,PRODUCTION_SURFACE.md,.env.exampleENABLED_ROUTERSline plus ownership and rationale sections (both docs are invariant-enforced)The manifest generator refused the promotion until the operations were classified —
RouteManifestError: Unclassified production operation sandbox:POST:/api/v1/sandbox/evaluate. That gate worked as designed.Type of Change
Test Plan
test_sandbox_production_promotion_3825.py, 21 cases: release-gate membership (includingsandbox_sharemust never be in the production allowlist), tenant isolation (no tenant path param, no tenant field on any request model, forgedtenant_id/X-Tenant-IDchanges nothing, and a source-level assertion that the promoted module contains noshared.database/get_db/sqlalchemy/INSERT/UPDATE/DELETE), contract, and six failure modes.services/ingestion/testssuite: 0 failures/errors. Green:tests/test_router_policy.py,tests/invariants/(incl.test_router_surface_doc_2838),tests/security/test_route_security_manifest.py,tests/test_server_import_smoke.py,tests/test_monolith_route_parity.py,tests/sdk/test_route_contract.py,tests/contract/test_openapi_production_surface_2861.py,services/shared/tests/test_secrets_fail_closed.py.generate_route_security_manifest.py --checkclean.ruffclean on every changed file. Frontendsandbox-cta.test.tspasses.sandboxis inproduction_router_groupswith both ops enabled atnone:stateless-no-persistence, while bothsandbox_shareops sit indisabled_operations; the manifest's own probe confirms all 178 omitted paths return exact 404.Four existing tests asserted "sandbox 404s in production" — the precise behaviour being changed. Each was updated to assert the new split rather than relaxed: the negative-contract probe now requires
/evaluate→ 400 and/share→ 404.Not verified here: DB-backed tests skipped for lack of Postgres/Docker in this environment. Per #2530 these must fail rather than skip under
REGENGINE_REQUIRE_DB=1, so CI remains the authority. No schema or migration change, so noexpected_schema.jsonregeneration applies.Checklist
proof_verify/proof_recallare classified as public, tenantless operationssys.modules[X] = stubtests addedDeployment note
ENABLED_ROUTERSmust be exact in production, so this needs the Railway variable updated to append,sandboxin the same release. Without it the app refuses to boot (Production ENABLED_ROUTERS does not match the release-approved set exactly: missing=['sandbox']) — fail-closed, by design.Related Issues
Fixes #3825.
Follow-up, deliberately not bundled:
/shareis dead functionality pointing at a deleted page. It should be removed outright, or repointed if sharing is still wanted — a product call.Generated by Claude Code