diff --git a/deploy/prod/docker-compose.yml b/deploy/prod/docker-compose.yml index 6a2b74bc7..d46139243 100644 --- a/deploy/prod/docker-compose.yml +++ b/deploy/prod/docker-compose.yml @@ -67,7 +67,10 @@ services: PLUGIN__WORKER__MAX_TOTAL_MEMORY_MB: "4096" PLUGIN__WORKER__REQUIRE_HARD_LIMITS: "true" LANGBOT_PLUGIN_RUNTIME_CONTROL_TOKEN: ${PLUGIN_RUNTIME_CONTROL_TOKEN} - BOX__ENABLED: "true" + # Cloud v2 currently grants no managed Box capability. Keep the shared + # runtime deployed but disable Core integration until a hard-quota-capable + # backend can satisfy the fail-closed Cloud readiness contract. + BOX__ENABLED: "false" BOX__BACKEND: nsjail BOX__RUNTIME__ENDPOINT: ws://box:5410 BOX__ADMISSION__REQUIRED: "true" diff --git a/src/langbot/pkg/cloud/bootstrap.py b/src/langbot/pkg/cloud/bootstrap.py index 8e9ee1886..c8341e56c 100644 --- a/src/langbot/pkg/cloud/bootstrap.py +++ b/src/langbot/pkg/cloud/bootstrap.py @@ -138,8 +138,14 @@ class VerifiedCloudDeployment: if plugin_worker.get('require_hard_limits') is not True: raise CloudBootstrapError('Cloud Runtime requires plugin.worker.require_hard_limits=true') box_config = config.get('box', {}) - if box_config.get('enabled') is not True: - raise CloudBootstrapError('Cloud runtime requires box.enabled=true') + box_enabled = box_config.get('enabled') + if box_enabled is False: + # Explicitly disabling Box removes the sandbox surface entirely and + # therefore does not weaken tenant isolation. Validate the strict + # runtime/admission contract only when the surface is enabled. + return + if box_enabled is not True: + raise CloudBootstrapError('Cloud runtime requires box.enabled to be an explicit boolean') if box_config.get('backend') != 'nsjail': raise CloudBootstrapError('Cloud runtime requires box.backend=nsjail') runtime_endpoint = str(box_config.get('runtime', {}).get('endpoint', '') or '').strip() diff --git a/tests/unit_tests/cloud/test_bootstrap.py b/tests/unit_tests/cloud/test_bootstrap.py index 77ccefa5d..e27367d8d 100644 --- a/tests/unit_tests/cloud/test_bootstrap.py +++ b/tests/unit_tests/cloud/test_bootstrap.py @@ -228,10 +228,23 @@ async def test_cloud_pgvector_contract_is_fail_closed(pgvector_config, message): ) +async def test_cloud_runtime_allows_explicitly_disabled_box(): + config = _cloud_config() + config['box']['enabled'] = False + + deployment = await resolve_deployment( + instance_uuid='instance-a', + instance_config=config, + entry_points=lambda: _EntryPoints([_EntryPoint(_Provider())]), + now=1_000, + ) + + assert isinstance(deployment, VerifiedCloudDeployment) + + @pytest.mark.parametrize( ('mutate', 'message'), [ - (lambda config: config['box'].update(enabled=False), 'box.enabled=true'), (lambda config: config['box'].update(backend='docker'), 'box.backend=nsjail'), (lambda config: config['box']['runtime'].update(endpoint=''), 'box.runtime.endpoint'), (