[{"data":1,"prerenderedAt":533},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fplugin-sandboxed-jobs":4,"surround-de-\u002Fdeveloper\u002Fplugin-sandboxed-jobs":524},[],{"id":5,"title":6,"body":7,"description":516,"extension":517,"meta":518,"navigation":519,"path":520,"seo":521,"stem":522,"__hash__":523},"content_de\u002F3.developer\u002F47.plugin-sandboxed-jobs.md","Sandboxed Plugin-Jobs",{"type":8,"value":9,"toc":507},"minimark",[10,19,22,27,38,58,62,129,132,136,146,197,204,208,219,262,269,386,394,398,405,439,442,446,487,490,503],[11,12,13,14,18],"p",{},"Tier 3 spezifiziert Berechnungen, die nicht innerhalb von Convex laufen sollten: das Parsen nicht vertrauenswürdiger Eingaben, aufwendige Analysen, alles, was hängen bleiben oder explodieren könnte. Das Polling-, Lease- und Outcome-Protokoll auf Worker-Seite ist weiterhin implementiert, aber ",[15,16,17],"strong",{},"es wird weder ein Convex-Enqueue-Adapter noch eine Operator-UI für Jobs ausgeliefert",", sodass Plugins heute keine Jobs starten können.",[11,20,21],{},"Derselbe Container bedient aktiv die Queue des Coding-Agents. Sein ruhender Plugin-Poller nutzt denselben gehärteten Executor, statt eine parallele Sicherheitsgrenze zu schaffen.",[23,24,26],"h2",{"id":25},"was-ein-plugin-darf-und-was-nicht","Was ein Plugin darf und was nicht",[11,28,29,33,34,37],{},[30,31,32],"code",{},"worker:enqueue"," ist reserviert. Ein künftiger Host-Adapter darf ",[15,35,36],{},"ausschließlich Enqueue"," gewähren; Claiming, Cancelling, Reclaiming und Lesen müssen Host- und Operator-Operationen bleiben.",[11,39,40,41,44,45,48,49,53,54,57],{},"Eine Job-Art hat die Form ",[30,42,43],{},"plugin.\u003CpluginId>.\u003ClocalId>",". Der Host prüft die Zugehörigkeit der Art direkt am String selbst, sodass ein Plugin, das ",[30,46,47],{},"plugin.other-plugin.job"," einreihen will, abgewiesen wird. Das Plugin wählt, ",[50,51,52],"em",{},"welche"," eingebaute Job-Art ausgeführt wird; das ",[15,55,56],{},"Kommando ist host-kontrolliert"," und wird niemals aus der Payload zusammengebaut. Die nicht vertrauenswürdige Payload wird immer als eigenständiges argv-Element übergeben, niemals in einen Shell-String interpoliert.",[23,59,61],{"id":60},"enqueue-limits","Enqueue-Limits",[63,64,65,78],"table",{},[66,67,68],"thead",{},[69,70,71,75],"tr",{},[72,73,74],"th",{},"Limit",[72,76,77],{},"Wert",[79,80,81,90,98,106,114],"tbody",{},[69,82,83,87],{},[84,85,86],"td",{},"Payload",[84,88,89],{},"≤ 64 KiB (zu groß ⇒ beim Enqueue abgelehnt)",[69,91,92,95],{},[84,93,94],{},"Gespeichertes Ergebnis",[84,96,97],{},"≤ 64 KiB (zu groß ⇒ vor dem Persistieren abgeschnitten)",[69,99,100,103],{},[84,101,102],{},"Versuche",[84,104,105],{},"auf 1 … 5 begrenzt",[69,107,108,111],{},[84,109,110],{},"Wall Clock einer einzelnen Ausführung",[84,112,113],{},"auf 1 s … 15 min begrenzt",[69,115,116,119],{},[84,117,118],{},"In-Flight-Jobs pro (Organisation, Plugin)",[84,120,121,122,125,126],{},"100 ",[30,123,124],{},"queued"," + ",[30,127,128],{},"running",[11,130,131],{},"Ein künftiger Enqueue-Adapter muss fail-closed abbrechen bei einem deaktivierten, nicht gewährten oder nicht deklarierten Plugin, einer plugin-fremden Job-Art, einer zu großen Payload oder einem erschöpften In-Flight-Budget. Terminale Jobs zählen nicht gegen die In-Flight-Obergrenze.",[23,133,135],{"id":134},"job-lebenszyklus","Job-Lebenszyklus",[137,138,144],"pre",{"className":139,"code":141,"language":142,"meta":143},[140],"language-text","queued ──claim──▶ running ──complete──▶ succeeded\n   │                 │\n   │                 ├──fail (attempts \u003C max)──▶ queued\n   │                 ├──fail (attempts = max)──▶ failed\n   │                 └──cancel requested──────▶ cancelled\n   └──cancel requested, then claim───────────▶ cancelled (never runs)\n","text","",[30,145,141],{"__ignoreMap":143},[147,148,149,156,166,172,186],"ul",{},[150,151,152,155],"li",{},[15,153,154],{},"Heartbeats"," belegen Lebendigkeit und sind der Weg, über den ein Worker von einem Operator-Cancel erfährt. Der nächste Heartbeat eines abgebrochenen laufenden Jobs weist den Worker an, die gesamte Prozessgruppe zu töten.",[150,157,158,161,162,165],{},[15,159,160],{},"Ein Abbruch lässt sich nicht umgehen",", und ein abgebrochener Job wird ",[15,163,164],{},"niemals"," erneut versucht.",[150,167,168,171],{},[15,169,170],{},"Lease Reclaim"," holt Jobs zurück, die ein abgestürzter oder neu gestarteter Worker zurückgelassen hat. Das Lease-Fenster liegt großzügig über dem maximalen Ausführungsbudget, sodass ein langsamer, aber lebendiger Job, der weiter Heartbeats sendet, nie unter sich selbst weggezogen wird; jeder Sweep ist begrenzt.",[150,173,174,177,178,181,182,185],{},[15,175,176],{},"Retries sind begrenzt"," durch das host-geklemmte ",[30,179,180],{},"maxAttempts",", sodass ein Poison-Job als ",[30,183,184],{},"failed"," endet, statt endlos zu kreisen.",[150,187,188,189,192,193,196],{},"Fehlergründe sind die festen Codes ",[30,190,191],{},"worker_failed"," und ",[30,194,195],{},"worker_timeout","; jede Fehlermeldung wird gekürzt.",[11,198,199,200,203],{},"Enqueue und jedes terminale Ergebnis schreiben eine dem ",[30,201,202],{},"pluginId"," zugeordnete Audit-Zeile.",[23,205,207],{"id":206},"die-sandbox","Die Sandbox",[11,209,210,211,214,215,218],{},"Der Worker-Orchestrator hält den Deployment-Admin-Key (und, für die Coding-Agent-Queue, ",[30,212,213],{},"GITHUB_TOKEN"," sowie den LLM-Key) und läuft als ",[15,216,217],{},"eingeschränkter Root",", ausschließlich um Privilegien abgeben zu können. Jeder nicht vertrauenswürdige Kindprozess wird über eine einzige Naht gestartet, die immer:",[147,220,221,232,239,249,259],{},[150,222,223,224,227,228,231],{},"auf die unprivilegierte ",[30,225,226],{},"sandbox","-uid\u002Fgid (10001) herunterschaltet — eine uid-übergreifende Kernel-Grenze, die das Kind nicht überschreiten kann, um ",[30,229,230],{},"\u002Fproc\u002F\u003Corchestrator>\u002Fenviron"," zu lesen oder den Orchestrator zu ptracen;",[150,233,234,235,238],{},"argv mit ",[30,236,237],{},"shell: false"," verwendet — keine Shell-Interpolation;",[150,240,241,244,245,248],{},[30,242,243],{},"detached"," läuft, sodass das Kind seine eigene Prozessgruppe anführt und ein Timeout oder Abbruch ",[15,246,247],{},"die gesamte Gruppe"," abräumt, nicht nur das direkte Kind;",[150,250,251,252,255,256,258],{},"mit ",[15,253,254],{},"jedem Ambient-Credential aus der Job-Umgebung entfernt"," läuft. Ein Plugin-Job sieht niemals den Admin-Key, ",[30,257,213],{}," oder den LLM-Key. Jede Capability, die Zugangsdaten erfordert, wird vom Host über Convex vermittelt, niemals durch Leaken von Env in die Sandbox;",[150,260,261],{},"an der host-geklemmten Wall-Clock-Deadline getötet wird (fail-closed).",[11,263,264,265,268],{},"Obergrenzen auf Container-Ebene, aus ",[30,266,267],{},"docker-compose.yml",":",[63,270,271,281],{},[66,272,273],{},[69,274,275,278],{},[72,276,277],{},"Kontrolle",[72,279,280],{},"Default",[79,282,283,297,310,323,344,363,374],{},[69,284,285,290],{},[84,286,287],{},[30,288,289],{},"mem_limit",[84,291,292,293,296],{},"2 g (",[30,294,295],{},"CODE_WORKER_MEM_LIMIT",")",[69,298,299,304],{},[84,300,301],{},[30,302,303],{},"cpus",[84,305,306,307,296],{},"1.0 (",[30,308,309],{},"CODE_WORKER_CPUS",[69,311,312,317],{},[84,313,314],{},[30,315,316],{},"pids_limit",[84,318,319,320,296],{},"512 (",[30,321,322],{},"CODE_WORKER_PIDS_LIMIT",[69,324,325,328],{},[84,326,327],{},"Capabilities",[84,329,330,333,334,337,338,337,341],{},[30,331,332],{},"cap_drop: ALL",", danach nur ",[30,335,336],{},"SETUID",", ",[30,339,340],{},"SETGID",[30,342,343],{},"CHOWN",[69,345,346,349],{},[84,347,348],{},"Dateisystem",[84,350,351,354,355,358,359,362],{},[30,352,353],{},"read_only: true",", Schreibzugriffe beschränkt auf das Volume ",[30,356,357],{},"code-workspace"," und ein 256 MiB großes ",[30,360,361],{},"\u002Ftmp","-tmpfs",[69,364,365,368],{},[84,366,367],{},"Privilegien-Eskalation",[84,369,370,373],{},[30,371,372],{},"no-new-privileges: true","; das Standard-Seccomp-Profil von Docker bleibt aktiv",[69,375,376,379],{},[84,377,378],{},"Netzwerk",[84,380,381,382,385],{},"Die isolierte ",[30,383,384],{},"code-worker","-Bridge, ausschließlich mit Convex geteilt — keine Route zum MTA-Admin-Port, zu IMAP, Redis, mail-sync, clamd, dem Updater oder dem Docker-Socket-Proxy",[387,388,391],"callout",{"title":389,"type":390},"Legen Sie einen Plugin-Job nicht ins Default-Netzwerk","danger",[11,392,393],{},"Der Worker führt von Angreifern beeinflussten Code aus. Seine Netzwerkisolation ist genau das, was laterale Bewegung in die Mail- und Infrastrukturschicht unterbindet. Ihn der gemeinsamen Bridge hinzuzufügen würde die gesamte Grenze zunichtemachen.",[23,395,397],{"id":396},"eingebaute-job-arten","Eingebaute Job-Arten",[11,399,400,401,404],{},"Die Registry der Job-Arten liegt im Worker, ist host-kontrolliert und wird über die ",[15,402,403],{},"lokale"," Id adressiert:",[63,406,407,417],{},[66,408,409],{},[69,410,411,414],{},[72,412,413],{},"Lokale Id",[72,415,416],{},"Kommando",[79,418,419,429],{},[69,420,421,426],{},[84,422,423],{},[30,424,425],{},"selftest",[84,427,428],{},"Eine No-op, die mit 0 endet — belegt uid-Drop, Env-Stripping, Timeout und Cancel-Verdrahtung Ende zu Ende, ohne dass Plugin-Code im Image ist",[69,430,431,436],{},[84,432,433],{},[30,434,435],{},"seed-test",[84,437,438],{},"Der Seed-List-Platzierungstest des Deliverability Lab; das kompilierte Shim ist das Kommando und die nicht vertrauenswürdige Payload trifft als eigenständiges argv-Element ein",[11,440,441],{},"Eine neue Tier-3-Job-Art ist eine Änderung am Worker-Image und wird wie jeder andere Host-Code reviewt — nichts, was ein Plugin-Package im Alleingang einführen kann.",[23,443,445],{"id":444},"betrieb-des-workers","Betrieb des Workers",[137,447,451],{"className":448,"code":449,"language":450,"meta":143,"style":143},"language-sh shiki shiki-themes github-light github-dark-dimmed","COMPOSE_PROFILES=plugin-tasks docker compose up -d code-worker\n","sh",[30,452,453],{"__ignoreMap":143},[454,455,458,462,466,470,474,477,480,484],"span",{"class":456,"line":457},"line",1,[454,459,461],{"class":460},"sYgZi","COMPOSE_PROFILES",[454,463,465],{"class":464},"s7YZ4","=",[454,467,469],{"class":468},"s-HuK","plugin-tasks",[454,471,473],{"class":472},"sOLd2"," docker",[454,475,476],{"class":468}," compose",[454,478,479],{"class":468}," up",[454,481,483],{"class":482},"sviXB"," -d",[454,485,486],{"class":468}," code-worker\n",[11,488,489],{},"Der Worker kann sich mit dem Deployment-Admin-Key bei Convex authentifizieren und das ruhende Queue-Protokoll abfragen, aber der aktuelle Host-Code legt keine Zeilen an. Es gibt weder eine Query für kürzliche Jobs noch eine UI zum Abbrechen.",[387,491,494],{"title":492,"type":493},"Nur Protokoll: kein Enqueue-Adapter","warning",[11,495,496,497,499,500,502],{},"Das Aktivieren des Profils ",[30,498,469],{}," startet einen Poller, aber kein ausgelieferter Host-Pfad kann Arbeit einreihen. Die Deklaration von ",[30,501,32],{}," hat keine Laufzeitwirkung, bis ein Adapter mit konkretem Producer und Operator-Oberfläche landet.",[504,505,506],"style",{},"html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":143,"searchDepth":508,"depth":508,"links":509},2,[510,511,512,513,514,515],{"id":25,"depth":508,"text":26},{"id":60,"depth":508,"text":61},{"id":134,"depth":508,"text":135},{"id":206,"depth":508,"text":207},{"id":396,"depth":508,"text":397},{"id":444,"depth":508,"text":445},"Tier 3: nicht vertrauenswürdige oder rechenintensive Plugin-Berechnungen hinter einer OS-Prozessgrenze und ohne Ambient-Credentials.","md",{},true,"\u002Fdeveloper\u002Fplugin-sandboxed-jobs",{"title":6,"description":516},"3.developer\u002F47.plugin-sandboxed-jobs","AvvuYjySgvFrq2pLzzNIy97Uu91akVVySdgnVTPepvE",[525,529],{"title":526,"path":527,"stem":528,"children":-1},"Connected Apps & signierte Hooks","\u002Fdeveloper\u002Fplugin-connected-apps","3.developer\u002F46.plugin-connected-apps",{"title":530,"path":531,"stem":532,"children":-1},"Plugin-Troubleshooting","\u002Fdeveloper\u002Fplugin-troubleshooting","3.developer\u002F48.plugin-troubleshooting",1786915109693]