Skip to content

Feature: typisiertes Eingabefeld mit Datei-Write (inkl. secret-Modus) — QoL #135

Description

@iret77

Was & warum (QoL, nicht Security-Garantie)

Ein neues form-Feld bzw. eine Feld-Capability: ein typisiertes Eingabefeld, dessen Wert beim Submit (affirmativer Button, nicht Cancel) in eine vom Agent vorgegebene Datei geschrieben wird. secret ist einer der Eingabetypen.

Ziel ist Bequemlichkeit: dem Agent einen sauberen Weg geben, User-Input (besonders Secrets) an seinen Bestimmungsort zu bringen — statt fragiler, geratener Shell-Scripte (siehe Praxisbeispiel unten) oder Arbeitsverweigerung, weil ein Secret nicht in die Session darf. Kein garantierter Schutz vor Agent-Zugriff (wer das braucht, trägt das Secret selbst ein), sondern QoL mit ehrlich dokumentierten Grenzen.

Zwei orthogonale Achsen

Achse Werte
A: Eingabetyp string (maxlen, optional pattern), int (min/max), float (min/max), secret (maskiert), …
B: Disposition an Agent zurück / in Datei schreiben / beides

Constraint: secretnur schreiben, Wert wird nie an den Agent zurückgegeben. Für non-secret ist der File-Write reiner Komfort (der Agent hat den Wert ohnehin). → target ist eine optionale Eigenschaft pro Feld, orthogonal zum Typ.

Schreib-Modi (explizit, NICHT aus Datei-Existenz abgeleitet)

Modus aus „Datei existiert?" abzuleiten ist ein Footgun: bei Pfad-Tippfehler/Race kippt sonst still das Verhalten. Daher deklariert der Agent mode explizit; Existenz ist nur geprüfte Vorbedingung:

mode Vorbedingung Verhalten Fehlerfall
create Datei fehlt (oder overwrite:true) Datei = roher Eingabewert existiert ohne overwrite → Fehler
substitute Datei da, Platzhalter genau 1× ersetzt den Agent-vorgegebenen Platzhalter durch den Wert; format-agnostisch (YAML/TOML/JSON/INI/…), da reine String-Substitution 0 Treffer → Fehler; >1 → Fehler (oder explizit „all")

Zielort — IMMER der Host, auf dem der Agent läuft

Kein beliebiger Host. Das einzige zulässige Schreibziel ist die Maschine, auf der die Agent-Session läuft:

  • Lokale Session → lokaler Dateipfad auf dem Mac (aiui schreibt direkt).
  • Remote-Session (SSH) → genau der registrierte Remote, auf dem der Agent läuft (aiui schreibt via vorhandenem SSH-Pfad dorthin zurück; der Wert reist Keyboard → lokales aiui → scp → Remote-Datei, nie durch den Agent-Kontext).

Damit ist Exfiltration auf einen fremden Host strukturell ausgeschlossen — der Agent kann target nicht auf attacker@evil:/exfil zeigen.

Pflicht: User-Freigabe pro Dateioperation

Jede Schreiboperation wird dem User im Dialog zur Freigabe vorgelegt — der affirmative Button ist die Freigabe. Angezeigt wird das aufgelöste Ziel + Modus + Preview:

Schreibe secret nach byte5ai-host:~/.config/foo/key (mode: create, 0600) — [Speichern] [Abbrechen]

Begründung (der eigentliche Sicherheits-Gedanke): aiui ist ein privilegierter Mittelsmann. aiui schreibt mit der SSH-/Datei-Identität des Users; ein sandboxed Agent könnte über aiui Pfade erreichen, die sein eigener direkter Zugriff oder sein Approval-Gate blockiert — klassischer Confused-Deputy. Die zwingende User-Freigabe pro Op schließt diese Lücke: der User ist der Autorisierungs-Backstop und sieht zudem einen falschen Pfad, bevor er bestätigt.

Mechanik

  • Wert bei Submit in-memory unter opakem Handle, kein Plaintext-tmp-File.
  • aiui schreibt atomar (tmp + rename lokal; analog remote).
  • Tool-Result: {written:true, target, bytes} — bei secret ohne Wert; bei non-secret optional Wert + Status.
  • Fehler (Permission, Host weg, Platzhalter nicht gefunden, create auf existierende Datei ohne overwrite) → {written:false, error} an den Agent und sichtbar im Dialog. Kein stiller Erfolg.
  • In-memory-Eintrag verfällt direkt nach dem Write (oder per TTL) → kein Cleanup-Problem.

Umsetzung: form erweitern, kein neues Widget

Forms haben bereits Button(s), Validierung, Layout, Submit/Cancel. „Referenzierter Button" = affirmativer Submit (Cancel → kein Write). Neu: optionale Feld-Eigenschaft target + Eingabetyp secret. Spart eine parallele Code-Fläche.

{
  "kind": "secret",
  "name": "github_pat",
  "label": "GitHub PAT für byte5ai",
  "target": { "mode": "create", "path": "~/.github_tokens/byte5ai", "perm": "0600", "overwrite": true }
}

Praxisbeispiel (Motivation)

Ein Agent präsentierte dieses Snippet, um einen PAT „am Chat vorbei" zu speichern:

touch ~/.github_tokens && chmod 600 ~/.github_tokens
read -s -p "GitHub PAT für byte5ai: " PAT && echo
( grep -v '^byte5ai=' ~/.github_tokens 2>/dev/null; printf 'byte5ai=%s\n' "$PAT" ) > ~/.github_tokens.tmp
mv ~/.github_tokens.tmp ~/.github_tokens
chmod 600 ~/.github_tokens
unset PAT

Auf dem Host ist ~/.github_tokens aber ein Verzeichnis mit je einer Datei pro Token, keine flache key=value-Datei. Das Snippet hätte: chmod 600 aufs Verzeichnis (→ x-Bit weg → alle Token unlesbar), eine falsch formatierte stray-Datei im Verzeichnis angelegt und den PAT nie am richtigen Ort gespeichert. Der Agent hielt das Secret per read -s/unset bereits aus dem Kontext — das Problem ist nicht Geheimhaltung, sondern dass der Agent die Storage-Konvention raten muss und danebenliegt. Das secret-Feld ersetzt das durch einen nativen Dialog + korrekten, atomaren Write, dessen Ziel der User vorher sieht.

Phasing

  • v1: lokal create + substitute; Remote create (scp auf den Agent-Host).
  • v1.1: Remote substitute (read-modify-write über ssh) — der teurere, sensiblere Teil.

Offene Fragen

  • Session→Remote-Mapping: Bei Remote-Sessions kommt die /render-Anfrage über den Reverse-Tunnel von 127.0.0.1 — wie mappt aiui sie auf den richtigen registrierten Remote? Vermutlich muss das mcp-stdio-Child sich selbst identifizieren. Implementierungs-Detailfrage.
  • Host-Validierung: Remote-Ziel zwingend durch is_valid_host_alias + -- end-of-options (sonst kommt die SSH option injection via unvalidated host_alias from Settings UI #52-Option-Injection-Fläche zurück), auch bei Beschränkung auf registrierte Remotes.
  • Mehrere Felder mit target: mehrere Writes; bei selber Datei mehrere Platzhalter in einem Pass; Per-Target-Ergebnis + Reihenfolge/Atomarität über mehrere Dateien.
  • Validierung von int/float/string clientseitig vor Button-Enable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureConcrete user-facing or agent-facing featurev1Belongs to V1 (modal/short-lived paradigm)

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions