Skip to content

ADR 0005: app.manifest.yml — Self-Describing Apps

Status: Accepted Date: 2026-04-29

Context

The portal needs to know which apps exist, their display names, icons, routing, and entitlement rules. The gateway needs routing info. CI needs metadata for affected-build calculations. Humans need a place to look up an app's owner.

The two patterns are:

  • Central registry — a single config file (apps.yml) listing every app and its metadata.
  • Self-describing apps — each app carries a manifest in its own directory; the platform discovers apps by scanning the filesystem.

Decision

Use self-describing apps via app.manifest.yml at the root of every app directory. Schema is defined in schemas/app-manifest.schema.json (JSON Schema draft 2020-12). CI rejects PRs with invalid manifests.

Auto-discovery flow: 1. The portal scans apps/**/app.manifest.yml and clients/**/app.manifest.yml at runtime. 2. Filters by lifecycle.status != "archived" and hub.visible == true. 3. Queries Authentik for the current user's entitled application slugs. 4. Intersects: only apps where auth.authentik_app_slug matches an entitled app are shown. 5. Groups by hub.category, sorts by hub.order.

Adding a new app to the hub = creating its manifest. No portal code changes.

Key schema choices

  • id (UUID) is separate from slug. If an app gets renamed, the slug and directory change but the id stays. Entitlements, audit logs, and cross-app references all key off id. Renaming doesn't orphan history.
  • manifest_version: 1 — explicit schema version. Lets us evolve the schema without breaking old manifests.
  • No secrets. Manifests are committed. Anything secret goes in .env.
  • Optional fields have sensible defaults. The scaffold script produces a minimal but fully valid manifest; humans add detail later.
  • dependencies.events_publish/subscribe captures async wiring even before a message bus exists. Costs nothing now, lets the docs site render a service map automatically when we have one.

For the full schema, see ../reference/app-manifest.md.

Consequences

Positive: - No central config file to keep in sync. The filesystem is the registry. - Adding/removing/archiving an app touches one directory. - Validation in CI catches typos and schema drift before they hit prod. - The manifest is plain YAML — humans can edit it, agents can generate it, scripts can read it.

Negative: - The portal must do filesystem scanning. Cost is small (manifests are tiny), but it's not zero. Caching layer can be added if it becomes hot. - A schema change requires updating every existing manifest — partially mitigated by manifest_version allowing graceful coexistence of old and new versions during migration.

Alternatives considered

  • Central apps.yml. Rejected — every new app would need two PRs (one to add the app, one to register it). High friction for agents.
  • Convention-only (no manifest). Rejected — too many things (icons, descriptions, ownership, entitlements) need explicit declaration.