docs(resource-manager): document settings schema + new endpoints
All checks were successful
CI / lint (push) Successful in 28s
CI / test (push) Successful in 34s
CI / validate-json (push) Successful in 24s
CI / markdown-links (push) Successful in 12s

Reflects commit 61c7ee2 — manifest gains `settings` + `description_long`,
API gains `GET/POST /api/apps/<name>/settings`, install/reinstall accepts
a `settings` object. Drops the stale "in-UI .env editor" from the
out-of-scope list since that's what just shipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Maksymilian Syrnicki 2026-04-15 13:07:23 +02:00
parent 61c7ee232c
commit 70001f54fd

View file

@ -2,7 +2,7 @@
The layer between Furtka apps and the underlying system (disk, Docker, network). Apps don't touch Docker or the filesystem directly — they declare what they need in a manifest and the Resource Manager provisions, runs, and tracks them. The layer between Furtka apps and the underlying system (disk, Docker, network). Apps don't touch Docker or the filesystem directly — they declare what they need in a manifest and the Resource Manager provisions, runs, and tracks them.
**Status:** v1 shipped 2026-04-15 in commits `cfc4c0b..c6ed7a8`. First consumer is the LAN fileshare app at `apps/fileshare/`. Web UI at `http://<host>.local/apps`. Not yet validated on a real VM with Docker — that's the first test of the next session. **Status:** v1 shipped 2026-04-15 in commits `cfc4c0b..c6ed7a8`, validated end-to-end on Proxmox VM same day (install → Web UI → fileshare install → SMB client → reboot-persistence all green). Commit `61c7ee2` adds the in-browser settings form and `description_long` so users no longer need SSH to configure an app. First consumer is the LAN fileshare app at `apps/fileshare/`. Web UI at `http://<host>.local/apps`.
For the conversation that produced these decisions and the Q&A live in the chat, see `~/.claude/plans/stateful-juggling-pike.md`. For the conversation that produced these decisions and the Q&A live in the chat, see `~/.claude/plans/stateful-juggling-pike.md`.
@ -24,7 +24,7 @@ The directory **name** is the app name. The manifest's `name` field must match i
### Manifest schema ### Manifest schema
JSON, all fields required: JSON. Fields marked *required* are mandatory; *optional* ones default as noted.
```json ```json
{ {
@ -32,18 +32,56 @@ JSON, all fields required:
"display_name": "Network Files", "display_name": "Network Files",
"version": "0.1.0", "version": "0.1.0",
"description": "SMB share for LAN devices", "description": "SMB share for LAN devices",
"description_long": "Longer user-facing help shown above the settings form.",
"volumes": ["files"], "volumes": ["files"],
"ports": [445, 139], "ports": [445, 139],
"icon": "icon.svg" "icon": "icon.svg",
"settings": [
{
"name": "SMB_USER",
"label": "Benutzername",
"description": "Der Name, mit dem sich Geräte am Share anmelden.",
"type": "text",
"default": "furtka",
"required": true
},
{
"name": "SMB_PASSWORD",
"label": "Passwort",
"description": "Mindestens 8 Zeichen. Wird nie angezeigt.",
"type": "password",
"required": true
}
]
} }
``` ```
- `name` — machine id, must equal the install folder name. Top-level fields:
- `display_name` — shown in the UI.
- `version` — free-form string (semver expected, not enforced). - `name` *(required)* — machine id, must equal the install folder name.
- `volumes` — list of short names. Furtka creates each as `furtka_<app>_<vol>` (collision-free across apps). Compose files MUST reference the namespaced name as `external: true`. - `display_name` *(required)* — shown in the UI.
- `ports` — informational for the UI. Compose owns the actual port binding. - `version` *(required)* — free-form string (semver expected, not enforced).
- `icon` — relative path inside the app folder. - `description` *(required)* — one-line summary rendered on the card.
- `description_long` *(optional, default `""`)* — multi-sentence help text rendered above the settings form. Plain text, newlines preserved.
- `volumes` *(required)* — list of short names. Furtka creates each as `furtka_<app>_<vol>` (collision-free across apps). Compose files MUST reference the namespaced name as `external: true`.
- `ports` *(required)* — informational for the UI. Compose owns the actual port binding.
- `icon` *(required)* — relative path inside the app folder.
- `settings` *(optional, default `[]`)* — see next section.
### Settings schema
Each entry in `settings` declares one environment variable the user can fill in via the Web UI (or via `POST /api/apps/install` with a `settings` object). On install/edit the values are written to `.env` in the app folder.
- `name` *(required)* — env-var name. Must match `^[A-Z_][A-Z0-9_]*$` (UPPER_SNAKE_CASE). Duplicates rejected.
- `label` *(optional, defaults to `name`)* — human-readable label rendered as the form label.
- `description` *(optional, default `""`)* — one-sentence help text rendered under the label.
- `type` *(optional, default `"text"`)* — one of `"text"`, `"password"`, `"number"`. Controls the HTML input type AND whether the current value is masked on the settings GET endpoint (password values are never returned, only written).
- `required` *(optional, default `false`)* — checked against the *merged* `.env` after submit, so edit-mode can omit unchanged fields and still pass.
- `default` *(optional, default `null`)* — applied on first install if the user didn't submit the field. String-coerced if non-string.
**Merge semantics on edit:** the installed app's existing `.env` is the base, submitted settings overlay it. Omit a field and its current value is preserved; submit `""` and it's cleared (which triggers `required` rejection).
**Placeholder rejection still applies:** if a final `.env` value matches `furtka.installer.PLACEHOLDER_SECRETS` (currently `{"changeme"}`), install fails with `InstallError` — so apps shipping an `.env.example` with `changeme` stay safe even if the user skips the form.
--- ---
@ -77,9 +115,11 @@ The installer copies files into `/var/lib/furtka/apps/<name>/`, preserves any ex
Endpoints: Endpoints:
- `GET /` and `/apps` — self-contained HTML UI. - `GET /` and `/apps` — self-contained HTML UI.
- `GET /api/apps` — installed apps as JSON. - `GET /api/apps` — installed apps as JSON (each includes `has_settings` so the UI can show the "Einstellungen" button only when relevant).
- `GET /api/bundled` — apps available in `/opt/furtka/apps/` that aren't installed. - `GET /api/bundled` — apps available in `/opt/furtka/apps/` that aren't installed.
- `POST /api/apps/install` `{"name": "..."}` — install/reinstall. - `GET /api/apps/<name>/settings` — returns the manifest's settings alongside current `.env` values. Works for both installed and bundled apps. Password values are returned as empty strings.
- `POST /api/apps/<name>/settings` `{"settings": {...}}` — merges values into the installed app's `.env` and reconciles. Only for already-installed apps.
- `POST /api/apps/install` `{"name": "...", "settings": {...}}` — install/reinstall. `settings` is optional; if present the `.env` is written from it before the placeholder check.
- `POST /api/apps/remove` `{"name": "..."}` — remove (folder, not volume). - `POST /api/apps/remove` `{"name": "..."}` — remove (folder, not volume).
The UI has **no authentication**. It shouts the warning at the top. Authentik integration is the proper fix later. The UI has **no authentication**. It shouts the warning at the top. Authentik integration is the proper fix later.
@ -96,7 +136,7 @@ These are deliberate omissions, not forgotten work. Adding any of them is a disc
- TLS on `.local` (separate problem — see commit history around mDNS for the reasoning). - TLS on `.local` (separate problem — see commit history around mDNS for the reasoning).
- Catalog repo — `install <name>` only resolves bundled apps, no network catalog. - Catalog repo — `install <name>` only resolves bundled apps, no network catalog.
- Auto-updates of installed apps. - Auto-updates of installed apps.
- In-UI `.env` editor — re-install after editing currently needs SSH. - Free-form `.env` editor — the settings form only exposes fields declared in the manifest. Non-manifest keys in `.env` are preserved on edit but not editable through the UI.
--- ---