Skip to content

Dashboard API enable/activate doesn't invalidate .compose-flags cache — CLI sees stale stack #298

@yasinBursali

Description

@yasinBursali

Problem

When the dashboard API enables an extension (_activate_service in routers/extensions.py), it renames compose.yaml.disabledcompose.yaml but does not invalidate .compose-flags.

The CLI's `get_compose_flags` reads .compose-flags from cache (fast path in dream-cli:295) — which was written at install time and only regenerated when the CLI itself toggles extensions (cmd_enable/cmd_disable via _regenerate_compose_flags).

Result: after API-driven enable, any subsequent CLI command (`dream start`, `dream restart`, `dream stop`) operates on a stale stack that doesn't include the newly enabled services.

Reproduction

  1. Install DreamServer, some extensions disabled (e.g., tts, whisper)
  2. From the dashboard UI, click Enable on tts or apply a template containing tts
  3. Dashboard API calls _activate_service("tts") which renames extensions/services/tts/compose.yaml.disabledcompose.yaml
  4. Run `dream start` from the terminal
  5. Bug: tts is not started. Docker reports dream-tts as an orphan container. The resolved compose stack doesn't include the tts compose file.
  6. Root cause: .compose-flags still contains the old flag list from install time, without extensions/services/tts/compose.yaml

Impact

  • PR 2 (service templates) directly triggers this: template apply renames compose files but doesn't update the cache, so the required follow-up `dream start` misses the newly enabled services.
  • Any API-driven extension toggle silently breaks CLI operations until the user manually removes `.compose-flags` or `dream disable && dream enable` the same service.

Fix Options

Option A: Invalidate cache from API

Have _activate_service (and any other API path that toggles compose files) remove or regenerate .compose-flags after the rename. Simplest fix:
```python
import os
compose_flags = INSTALL_DIR / ".compose-flags"
if compose_flags.exists():
compose_flags.unlink()
```
This falls through to dynamic resolution on next CLI invocation.

Option B: Drop the cache entirely

.compose-flags was added as an install-time fast path. The resolver is fast enough at runtime that the cache may not be worth the cache-invalidation complexity. Remove get_compose_flags fast path, always resolve dynamically.

Option C: Use mtime-based cache invalidation

Compare the cache file mtime against all extensions/services/*/compose.yaml* mtimes. Regenerate if any extension compose file is newer.

Recommended

Option A — it's minimal and correct. The dashboard API already imports INSTALL_DIR from config. Add cache invalidation to both _activate_service (enable path) and disable_extension (disable path).

Files Affected

  • `dream-server/extensions/services/dashboard-api/routers/extensions.py` — `_activate_service`, `disable_extension`
  • `dream-server/extensions/services/dashboard-api/routers/templates.py` — indirectly via `_activate_service` calls
  • `dream-server/dream-cli` — already correct (`_regenerate_compose_flags` is called from CLI enable/disable paths)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions