feat: add the Roomote deployment-managed compute provider#402
Conversation
|
No new code issues found. See task
Reviewed 9332596 |
Adds 'roomote' as a first-class compute provider: managed sandboxes preconfigured by the hosting deployment, running on the existing Modal machinery with deployment-managed credentials (ROOMOTE_CLOUD_TOKEN_ID / ROOMOTE_CLOUD_TOKEN_SECRET) instead of an operator's own Modal account. - Setup catalog models the credentials as managed infrastructure fields: never operator form inputs, so the wizard and Settings offer the provider only when the deployment env already satisfies it. The picker and Settings now hide deployment-managed providers that cannot be configured from the UI (implements the documented infrastructureSatisfied gating). - Factory/controller delegate to the Modal path with a parameterized vendor; task runs persist vendor='roomote' so sleep-check, log fetching, retention, and snapshots resolve the right credentials via the provider catalog. - MODAL_BASE_IMAGE_REF derivation from the worker image now covers the new provider; the worker-side label maps to 'modal' so cgroup usage polling behaves identically. - Sleep-check partial indexes gain the new vendor (migration included). - getDefaultAvailableComputeProvider no longer assumes deployment-managed providers are satisfiable when called without live status.
Adds ROOMOTE_CLOUD_BACKEND (default 'modal', the only supported value today) so the managed provider's engine is a per-deployment choice with an engine-neutral contract: ROOMOTE_CLOUD_TOKEN_ID is now optional (API-key engines use only the secret slot), the backend selector is a deployment-managed catalog field (resolved with the DB fallback, reserved from the editor, stripped from sandboxes), and the factory and controller resolve the backend before delegating — an unsupported value fails loudly at spawn instead of silently running on the wrong engine. Future engines (E2B/Daytona) extend ROOMOTE_CLOUD_BACKENDS and add their branch at the two dispatch sites, plus backend-aware capability/ label/resource/usage metadata; see compute-providers/roomote-cloud.ts for the seam documentation including the drain-before-switch caveat.
…on renumber - spawn-modal-worker: pass the persisted vendor to OIDC priming instead of the literal 'modal'. primeSandboxOidcTargets builds its compute client from that provider, so a roomote launch on a deployment with only ROOMOTE_CLOUD_TOKEN_* would otherwise fail to resolve MODAL_TOKEN_* whenever the environment has OIDC targets. - ComputeProviders (Settings): resolve the effective default against the unfiltered provider list and keep it in the default-provider select as a disabled '(unavailable)' item when its section is hidden. Previously, removing Roomote Cloud credentials after it was persisted as the default made both the select value and the missing-configuration alert silently disappear while dispatch kept targeting it. - db: regenerate the sleep-check index migration as 0015 on top of develop's 0012-0014, restoring the journal and 0012 snapshot that the rebase had displaced.
6b3412d to
7b529f2
Compare
|
All three review findings addressed in the rebase (
Validation after rebase: lint / check-types / knip pass; types, env, compute-providers, and controller test suites green. The |
…CLOUD_SLUG The managed provider previously leaned on MODAL_APP_NAME for per-deployment attribution, which is engine-specific and — being shared with the plain Modal provider — would also rename the app in an operator's own bring-your-own Modal workspace. ROOMOTE_CLOUD_SLUG is the deployment identity; each backend maps it to its native grouping mechanism. The Modal backend groups sandboxes under the app roomote-<slug> (an explicit MODAL_APP_NAME still wins as an escape hatch; the plain Modal provider ignores the slug entirely). The slug is a deployment-managed catalog field: DB-fallback resolution, reserved from the env editor, stripped from task sandboxes.
|
Follow-up per design discussion: per-deployment attribution now uses an engine-neutral |
- StepComputeProvider fixtures now carry a realistic operator-editable credential field (fields: [] represents a deployment-managed provider, which the picker deliberately hides when unsatisfied), plus a test covering that gating in both states. - Environments: drop 'roomote' from the snapshot-provider list until it is gated on configured providers — an unconditional entry would offer roomote snapshot controls on deployments without the provider. Task-sleep snapshots are unaffected (snapshotCapableComputeProviders keeps the vendor).
The traced r.svg has hardcoded fills, so BrandIcon renders it as a CSS mask over bg-current (the RoomoteWordmark technique) to inherit the surrounding text color like the other brand icons — white inside the picker buttons, foreground-colored in Settings.
Display strings only; the provider id, env vars, and persisted vendor stay 'roomote'.
A secret-only deployment passed configSatisfied, so Roomote Sandbox was offered (and defaulted) as usable while both the controller and the client factory reject launches without the token id — every task would fail to start. Keep the id required; future API-key backends should make requiredness backend-aware instead of dropping it outright. Adds a test pinning the secret-only-unsatisfied behavior.
|
Fourth finding addressed in |
Excluding roomote from the snapshot-provider list entirely removed the only create/refresh/clear controls on deployments where the managed provider IS configured, and hid any existing roomote snapshot. The list now includes roomote when compute status reports it configured (via a new useComputeProviderConfigured hook), and an environment that already holds a roomote snapshot keeps its controls regardless, so stale snapshots stay manageable after credentials are removed.
|
Environments snapshot gating addressed in the latest push: |
roomote task runs persisted every create/bootstrap/cleanup and worker-launch mutation event as provider 'modal'. createModalMachine now emits computeClient.vendor and spawnModalWorker emits its vendor, so event metadata matches the persisted task-run vendor. Client construction and resource resolution keep the literal engine. Adds a test asserting every event from a roomote-flavored launch carries the roomote vendor.
|
Sixth finding addressed in the latest push: |
cleanupModalInstance (invoked from the create path and worker spawn on failure) still hardcoded provider 'modal' in its destroy_instance events. It now emits the client's vendor; the computeClient param picks up 'vendor' alongside 'destroyInstance', which both callers already pass. Adds tests asserting the destroy events carry the roomote vendor on success and failure paths.
|
Follow-up on the event-metadata finding: the residual path was |
What
Adds
roomoteas a first-class compute provider: managed sandboxes preconfigured by the hosting deployment, running on the existing Modal machinery with deployment-managed credentials (ROOMOTE_CLOUD_TOKEN_ID/ROOMOTE_CLOUD_TOKEN_SECRET) instead of an operator's own Modal account. Operators on deployments that carry these credentials get a zero-setup sandbox option in the wizard, while bring-your-own Modal/E2B/Daytona/Blaxel remain fully available alongside it.Design
Catalog — the new provider's credentials are modeled as managed infrastructure fields: never operator form inputs. The wizard picker and Settings now hide providers that have no operator-editable fields unless the deployment already satisfies them, implementing the gating that the
infrastructureSatisfieddoc comment described. Deployments without the env vars never see the provider; deployments with them get it as a one-click choice.Runtime — the factory and controller delegate to the Modal path with a parameterized vendor (
ModalConfig.vendor), and task runs persistvendor='roomote'. Persisting the real vendor matters: sleep-check, task-log fetching, standby retention, and snapshots all resolve credentials per vendor through the setup catalog, so a masqueradedmodalvendor would resolve the wrong (absent) credentials on deployments that only carry the managed pair.Worker side —
getWorkerComputeProviderLabelmaps the new provider tomodal, so cgroup layout detection and usage polling behave identically; usage recording reuses the Modal policy for the same reason.Base image —
MODAL_BASE_IMAGE_REFstays shared: derivation from the deployment worker image (DOCKER_WORKER_IMAGE/ bakedRELEASE_VERSION) now covers both Modal-backed providers.DB — the three sleep-check partial indexes gain the new vendor; migration
0012_great_corsairincluded.Fallback semantics —
getDefaultAvailableComputeProviderno longer assumes deployment-managed providers are satisfiable when called without live status (they can only be satisfied by the runtime env).Notes for reviewers
roomotebrand-icon key currently hitsBrandIcon's empty-span fallback; needs a logo mark as a design follow-up.DEFAULT_COMPUTE_PROVIDERenv enum now acceptsroomoteso hosted provisioning can default to it.Testing
pnpm lint,pnpm check-types,pnpm knipall pass.pnpm test: all workspaces green except 19 pre-existing@roomote/webfailures from a stale local test DB (llm_usage_eventsmissing; reproduced identically on a clean tree, unrelated to this change).ROOMOTE_CLOUD_*(and refusesMODAL_TOKEN_*fallback) and reports its vendor; setup status satisfies the provider from runtime env alone and marks it unsatisfiable/hidden without the managed credentials.Backend seam (second commit)
The managed provider is engine-neutral by contract:
ROOMOTE_CLOUD_BACKEND(defaultmodal, the only supported value today) selects which engine runs its sandboxes, so future deployments can run on E2B/Daytona under the same "Roomote" experience.ROOMOTE_CLOUD_TOKEN_IDis now optional — engines differ in credential shape (Modal uses id+secret; API-key engines use only the secret slot); spawn-time validation is backend-specific.ROOMOTE_CLOUD_BACKENDS, add a branch at the two dispatch sites, and make the static per-provider metadata (capabilities, worker label, resource model, usage policy) backend-aware. The seam doc incompute-providers/roomote-cloud.tscovers this plus the drain-before-switch caveat: machine ids and snapshots are engine-specific, so changing a live deployment's backend requires drainingroomote-vendor runs first.