diff --git a/apps/README.md b/apps/README.md index 13e750f..b4ca1ce 100644 --- a/apps/README.md +++ b/apps/README.md @@ -47,10 +47,42 @@ Rules enforced by `furtka/manifest.py`: - `volumes` — short names, strings. Namespaced to `furtka__` at runtime. - `ports` — integers. Informational only; compose owns the actual port binding. - `settings[].name` — must match `^[A-Z_][A-Z0-9_]*$`. This name becomes both the env-var key and the form-field ID. -- `settings[].type` — one of `text`, `password`, `number`. +- `settings[].type` — one of `text`, `password`, `number`, `path`. - `settings[].required` — if true, the install refuses when the value is empty. - `settings[].default` — optional string. Used to pre-fill the form and the bootstrapped `.env`. +### Path-type settings (host bind mounts) + +Use `"type": "path"` when the app should point at an existing folder on the host — media libraries, document archives, photo backups. The value is written to `.env` like any other setting, and compose consumes it via `${VAR}` substitution as a bind mount. + +```json +{ + "name": "MEDIA_PATH", + "label": "Medienordner", + "description": "Absoluter Pfad zu deinem Medien-Ordner, z.B. /mnt/media.", + "type": "path", + "required": true +} +``` + +```yaml +services: + app: + volumes: + - ${MEDIA_PATH}:/media:ro +``` + +The installer (`install_from` and `update_env`) refuses values that: + +- aren't absolute (must start with `/`), +- don't exist on the host, +- aren't directories, +- resolve (after `Path.resolve()`) into a system-path deny-list: `/`, `/etc`, `/root`, `/boot`, `/proc`, `/sys`, `/dev`, `/bin`, `/sbin`, `/usr/bin`, `/usr/sbin`, `/var/lib/furtka`. + +Traversal like `/mnt/../etc` is caught too — the deny-list check runs on the resolved path. + +Path settings sit alongside manifest-declared volumes. Use `manifest.volumes` for internal state the app owns (databases, caches, config), and path settings for user data the container should mount and — usually — read without owning. Mounting read-only (`:ro`) is a good default for data the app only consumes. + ## `docker-compose.yaml` - File extension is `.yaml`. The compose runner hardcodes this — `.yml` will not be found.