furtka/docs/resource-manager.md
Daniel Maksymilian Syrnicki cfc4c0b9c1 feat(furtka): resource-manager skeleton — manifest, scanner, CLI
Slice 1 of the Resource Manager (see docs/resource-manager.md +
plan in ~/.claude/plans/stateful-juggling-pike.md). Lays down the
read-only half: a JSON manifest schema with namespacing, a scanner
that walks /var/lib/furtka/apps/, and a `furtka` CLI with
`app list` and `reconcile --dry-run`. Reconciler / volume creation
/ docker compose calls land in the next slice.

- furtka.manifest: dataclass + load_manifest with required-field +
  type validation. volume_name() injects the furtka_<app>_<vol>
  namespace so apps can each declare a "data" volume without colliding.
- furtka.scanner: tolerant — broken manifest = ScanResult with error,
  not an exception. Lets reconcile log + skip rather than abort.
- furtka.cli: text + --json output. argparse with `app list` and
  `reconcile --dry-run`. main() returns int for clean exit codes.
- furtka.paths: FURTKA_APPS_DIR env override so tests don't need root.
- 19 new tests covering valid manifests, every validation branch,
  scanner edge cases (missing root, broken manifest, sort order), and
  the CLI subcommands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:59:41 +02:00

126 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Resource Manager — Design Notes
Scaffold for the conversation between Daniel and Robert. Captures what Robert already sketched in chat on 2026-04-15 and lists the open questions that need an answer before we write code.
**Status:** Draft 2026-04-15. Nothing implemented. First app to consume this will be a LAN fileshare (SMB/NFS) — that's the forcing function.
---
## What's the Resource Manager?
The layer between Furtka apps and the underlying system (disk, Docker, network, users). Apps don't touch Docker or the filesystem directly — they declare what they need, the Resource Manager provisions and tracks it.
Robert's framing (2026-04-15): *"żeby później można było manipulować wszystkimi peryferiami"* — so we can manipulate all peripherals from one place later.
---
## Decided (Robert, 2026-04-15)
1. **An app is a folder.** A Furtka app is defined as a directory containing:
- a manifest
- a `docker-compose.yaml`
- a `.env` file
2. **A registration script** reads that folder and wires the app into the backend.
3. **Each app gets its own named Docker volume.** Sharing between apps is possible but opt-in, not default.
4. **Filesystem is the source of truth (for now).** No SQL database yet — the Resource Manager reads current state from Docker and the OS ad-hoc. DB gets added when we actually need to store intent the OS doesn't know (e.g. "this share belongs to App X, readable by User Y").
These are locked; don't re-litigate without Robert.
---
## Open questions
These need an answer before the first line of code.
### Q1 — What's in the manifest?
This is the contract between app authors and Furtka. Changing it later is expensive. Known candidates:
- `name` (machine id, must match folder name?)
- `display_name` (shown in UI)
- `version` (semver? calver?)
- `description`
- `volumes` (list of named volumes the app needs)
- `ports` (which ports the app wants exposed; needed for reverse proxy later)
- `icon` (path or URL)
- ... anything else?
**Format:** YAML / TOML / JSON — not decided.
### Q2 — Where do apps live on disk?
Suggested-but-not-decided: `/var/lib/furtka/apps/<app-name>/`. Robert: confirm path?
### Q3 — What does the registration script actually do?
Robert said "Skript do rejestrowania w backendzie". Concretely that means some subset of:
- Validate manifest (schema check, required fields, volume names unique)
- Create any declared named volumes that don't exist yet
- Run `docker compose up -d` inside the app folder
- Record the app in whatever passes for the backend index (right now: nothing — we'd just re-scan the folder each time)
**Sub-question:** is registration a one-shot on install, or does something (systemd unit?) re-scan `/var/lib/furtka/apps/` on every boot so the filesystem really is authoritative?
### Q4 — How does the user actually install an app?
Out of scope for the first cut, but needed to know the shape of the CLI:
- Admin UI with a button "Install Fileshare"?
- CLI: `furtka app install <tarball>` / `<git repo>` / `<name-from-catalog>`?
- Catalog = a separate repo with app folders?
### Q5 — Upgrades and removal
- Upgrade strategy: replace folder + re-run compose? Preserve volumes across upgrades (yes obviously, but needs stating)?
- Removal: does it delete the volume or keep it? Robert's "apki się dzielą" implies a volume can outlive its original app.
### Q6 — Volume sharing semantics
Robert: "jak będzie trzeba to będą mogły się dzielić". Open:
- Who requests the share — the sharing app's manifest, or an admin action?
- What's the permission model (read-only, read-write, per-user)?
- This is probably the *first* piece of "intent not reflected in OS state" — might be what finally motivates the DB.
### Q7 — Backend API surface
Once the filesystem model is clear, the Resource Manager's backend still needs *some* Python/whatever surface that the web UI and the register script both call. Rough shape:
- `list_apps()` — scan `/var/lib/furtka/apps/`, return manifests + running status
- `install_app(folder)` — validate + compose up
- `remove_app(name)` — compose down, optionally rm volume
- `list_volumes()` — from Docker
- `list_shares()` — ???
Not decided whether this lives in the existing `webinstaller` Flask app, a new backend service, or a thin CLI that the Flask app shells out to.
---
## First consumer — Fileshare
The point of doing this now is to unblock the fileshare app (see `../memory/project_first_app_fileshare.md` for the conversation context). Walking it through the model above:
```
/var/lib/furtka/apps/fileshare/
manifest.??? # see Q1 — what exactly goes here?
docker-compose.yaml
.env
```
- App declares a volume (e.g. `files`).
- Compose runs a Samba/NFS container mounting that volume.
- On Mac/Windows/Android you mount `smb://furtka.local/files`.
If we can get *this* end-to-end through the Resource Manager, we've validated the whole model with the simplest possible app. Nextcloud, Jellyfin, etc. are the same shape with more knobs.
---
## What we do NOT do yet
- No database. (Decided.)
- No user/permissions model beyond what Samba/OS already give us.
- No reverse proxy / TLS integration in the manifest. (Tracked separately — see `../memory/project_ssl_local_deferred.md`.)
- No app catalog / store UI. Manual install first.
- No auto-updates.
These all become easier once Q1Q7 are answered — adding them to an existing model is straightforward; guessing them now is expensive.