home dashboard: surface docker apps via core's dynamic-app hook #3

Merged
erahhal merged 1 commit from home-dashboard-docker-apps into main 2026-06-12 20:38:59 -07:00
Owner

What

Deployed docker-mode AI apps (built in the AI builder, served by the
homefree-ai meta-proxy at <subdomain>.apps.<domain>) now appear under "My
Apps"
on the home dashboard, alongside the flake-mode apps that already showed
there.

How — the plugin side of a generic core hook

Core gained a plugin-agnostic extension point:
homefree.dashboard.dynamic-app-sources (a list of JSON-file paths core reads at
request time, filtering by a generic visibility). This PR is the homefree-ai
side: it projects our runtime registry into that generic tile schema and writes
the file. All homefree-ai specifics (registry shape, share_mode, the .apps.
URL form) stay here — core knows none of them.

  • app/runtime/dashboard_export.py (new): map each deployed AppEntry → a
    generic tile — status running/stopped only (missing/error skipped);
    share_modevisibility (public→public, household→authenticated,
    owner→owner); network_access→access badge (lan/public/sso); URL from
    subdomain or slug + system_domain; category = "My Apps". Atomic write
    (tmp + os.replace), mirroring the registry's idiom.
  • app/runtime/registry.py: regenerate the tile file on every registry
    mutation via _atomic_write (best-effort + lazy import — a render failure
    never breaks a registry write).
  • app/generator/deployer.py: seed the file once at
    reconcile_on_startup; add teardown(slug) (stop + remove container, drop
    registry entry).
  • app/main.py: DELETE /api/projects/{slug} now calls teardown so a
    deleted app drops off the dashboard. Fixes a latent gap — Registry.delete
    previously had zero callers, so deleted projects lingered in the registry.
  • apps/homefree-ai/default.nix: register the tile-file path with core via
    homefree.dashboard.dynamic-app-sources (gated on homefree-ai.enable).

Dependency

Requires the matching core change — the homefree.dashboard.dynamic-app-sources
option in homefree. Without it, NixOS evaluation fails on an unknown option, so
the core PR must land first (or together). (That core PR is a separate, generic
change and is not part of this repo.)

Verification

  • Unit-tested dashboard_export.render() across all status / share_mode /
    network_access combinations (incl. subdomain→slug fallback; missing/error
    skipped).
  • Cross-repo contract test: this module's real output fed into core's reader —
    owner sees their owner-only + household + public apps; another household user
    sees household + public (not owner-only); anonymous sees only public.
  • Verified live on a box: deploying a docker app writes
    <data>/dashboard-apps.json; the apps render under "My Apps"; deleting one
    removes its tile.
## What Deployed **docker-mode** AI apps (built in the AI builder, served by the homefree-ai meta-proxy at `<subdomain>.apps.<domain>`) now appear under **"My Apps"** on the home dashboard, alongside the flake-mode apps that already showed there. ## How — the plugin side of a generic core hook Core gained a **plugin-agnostic** extension point: `homefree.dashboard.dynamic-app-sources` (a list of JSON-file paths core reads at request time, filtering by a generic `visibility`). This PR is the homefree-ai side: it projects our runtime registry into that generic tile schema and writes the file. All homefree-ai specifics (registry shape, `share_mode`, the `.apps.` URL form) stay here — core knows none of them. - **`app/runtime/dashboard_export.py`** (new): map each deployed `AppEntry` → a generic tile — status `running`/`stopped` only (`missing`/`error` skipped); `share_mode` → `visibility` (`public`→public, `household`→authenticated, `owner`→owner); `network_access`→access badge (`lan`/`public`/`sso`); URL from `subdomain or slug` + `system_domain`; `category = "My Apps"`. Atomic write (tmp + `os.replace`), mirroring the registry's idiom. - **`app/runtime/registry.py`**: regenerate the tile file on every registry mutation via `_atomic_write` (best-effort + lazy import — a render failure never breaks a registry write). - **`app/generator/deployer.py`**: seed the file once at `reconcile_on_startup`; add `teardown(slug)` (stop + remove container, drop registry entry). - **`app/main.py`**: `DELETE /api/projects/{slug}` now calls `teardown` so a deleted app drops off the dashboard. Fixes a latent gap — `Registry.delete` previously had **zero callers**, so deleted projects lingered in the registry. - **`apps/homefree-ai/default.nix`**: register the tile-file path with core via `homefree.dashboard.dynamic-app-sources` (gated on `homefree-ai.enable`). ## Dependency Requires the matching **core** change — the `homefree.dashboard.dynamic-app-sources` option in `homefree`. Without it, NixOS evaluation fails on an unknown option, so the core PR must land first (or together). *(That core PR is a separate, generic change and is not part of this repo.)* ## Verification - Unit-tested `dashboard_export.render()` across all status / share_mode / network_access combinations (incl. subdomain→slug fallback; `missing`/`error` skipped). - Cross-repo contract test: this module's real output fed into core's reader — owner sees their owner-only + household + public apps; another household user sees household + public (not owner-only); anonymous sees only public. - Verified live on a box: deploying a docker app writes `<data>/dashboard-apps.json`; the apps render under "My Apps"; deleting one removes its tile.
Deployed docker-mode AI apps (built in the AI builder, served by the
meta-proxy at <subdomain>.apps.<domain>) now appear under "My Apps" on
the home dashboard, alongside flake-mode apps.

This is the plugin side of core's generic dashboard extension point
(homefree.dashboard.dynamic-app-sources): the plugin projects its runtime
registry into core's generic tile schema and writes the file core reads.
All homefree-ai specifics (registry shape, share_mode, .apps. URLs) stay
here; core stays plugin-agnostic.

- runtime/dashboard_export.py: map each deployed AppEntry -> a generic
  tile (status running/stopped; share_mode -> visibility public/
  authenticated/owner; network_access -> access badge; URL from subdomain
  + system_domain; category "My Apps"). Atomic write.
- runtime/registry.py: regenerate the tile file on every registry mutation
  via _atomic_write (best-effort; never breaks a write).
- generator/deployer.py: seed the file at reconcile_on_startup; add
  teardown(slug) (stop+remove container, drop registry entry).
- main.py: DELETE project now calls teardown so deleted apps drop off the
  dashboard (Registry.delete previously had no callers).
- default.nix: register the tile-file path with core via
  homefree.dashboard.dynamic-app-sources (gated on enable).

Depends on the matching core change (the homefree.dashboard.dynamic-app-
sources option in homefree); without it NixOS eval fails on the unknown
option.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
homefree-plugins/homefree-ai!3
No description provided.