JDL — ContextBridge und Cross-Context-Transfer¶
Status: Draft, aus
spec-context-modelherausgelöst Geltung: Kommunikationsoberflächen zwischen isolierten Context-Domänen, Distanz/Transport, Gruppen/Richtung, Codec sowie Wert- und Resource-Transfer. Nicht hier: Was ein Context grundsätzlich ist, Parent-Expose als Aktivierungsbindung, allgemeine Resource-Autoritätssemantik.
Kennzeichnung
| Marke | Bedeutung |
|---|---|
[N] |
normativ, gilt |
[D] |
Entscheidung dieser Fassung |
[O] |
offen |
Inhaltsverzeichnis¶
- 0. Kernregel
- 1. ContextBridge — Torwächter dazwischen
[N] - 1.1 Die Distanzachse
[N] - 1.2 Eine Bridge ist ein Blueprint
[D] - 1.3 Richtung sitzt an der Gruppe
[D] - 1.4 Werte sind kodierbar; Resources werden übertragen
[D] - 1.5 Woran die Distanzprüfung arbeitet
[D] - 2. Resource-Handles an der Grenze
[D] - 3. Abgrenzung zu Parent-Expose
[N] - 4. Normative Invarianten
- 5. Offene Punkte
0. Kernregel¶
Ohne Bridge gibt es zwischen isolierten Context-Domänen keinen Kommunikationspfad. Gewöhnliche Werte und lebende Resources folgen dabei unterschiedlichen Transferregeln.
Parent-Expose ist keine Bridge: Es bindet beim Aufbau einer vertrauenswürdigen Child-Domäne Umgebung nach unten. Eine Bridge transportiert Nachrichten oder Resources zwischen bereits getrennten Domänen.
1. ContextBridge — Torwächter dazwischen [N]¶
Eine Bridge ist eine ausdrücklich deklarierte, typisierte und policy-geprüfte Kommunikationsoberfläche zwischen zwei isolierten Contexts.
Sie beschreibt die Oberfläche, nicht das Paar [D]. Welche zwei Contexts sie verbindet und über welche Distanz, steht nicht in ihr, sondern in der Bindung — dem Eintrag unter bridges im ContextDescriptor (spec-context-model §2). Dieselbe Oberfläche kann damit mehrfach gebunden werden.
Ohne Bridge gibt es keinen Pfad. Nicht „verboten, aber technisch möglich", sondern nicht adressierbar.
Eine Bridge exponiert nur ihre deklarierte Oberfläche: erlaubte Nachrichten, Queries und Commands, Codec, Resource-Transferregeln, Richtung, Lebensdauer, Backpressure, Fehlermodell und Trust-Policy. Wie das deklariert wird, steht in §1.2.
1.1 Die Distanzachse [N]¶
Die Mechanik variiert, die Semantik nicht:
local gleiche VM, billige Aufrufgrenze
thread Scheduler- und Mailboxgrenze
process Transport- und Codecgrenze
node Jade Node, Netzwerk
mock TestContext, deterministische Wiedergabe
Die Form ist Mechanik. Die erlaubte Oberfläche ist Descriptor-Semantik.
Die Achse benennt die Grenze, nicht das Transportmittel [D]. mock transportiert nichts, sondern zeichnet auf und spielt ab; local übergibt. Bei drei von fünf Werten braucht es einen Transport, bei zweien nicht.
Die Bridge holt sich keinen Transport, sie bekommt einen [D]. Ein Transport, den sie sich beschaffte, wäre ein Service und müsste in einer Row auftauchen (spec-context-model §4/§6). Er wird ihr aber durch die Bindung belegt — dieselbe Richtung wie bei Parent-Expose (spec-context-model § Parent-Expose): von außen hineingegeben, nicht von innen geholt. Was die Bridge beiträgt, sind Anforderungen: Ihre Operationsformen sagen, was ein Transport können muss. Bewegt wird die Nachricht von der TransportEngine (spec-context-model § Foundation Engine Set); der Abgleich läuft nach B-4.
Deployment-Transparenz heißt gleiche Semantik [D]. Sie heißt nicht, dass jede Distanz jede Bridge trägt. Eine Umstellung kann fehlschlagen, wenn die gewählte Distanz beziehungsweise der gebundene Transport eine Operationsform nicht abbilden kann.
1.2 Eine Bridge ist ein Blueprint [D]¶
Es gibt kein Schlüsselwort bridge. Es gibt blueprint; die Rolle ergibt sich aus Struktur, Refinements und Provides — dieselbe Begründung wie in spec-context-model §2 für den Context.
Beleg: BP-10 lässt runtime-orchestrierende Refinements nur auf Blueprint- und Engine-Subjects zu, und Backpressure, Timeout und ResourceLimits sind genau solche.
Zwei Ebenen. Der Blueprint trägt Gruppen; jede Gruppe ist eine Function Union über den Operationen, die sie enthält — ein Wert davon ist genau eine Operation samt ihrer Argumente. Die Auflösung liegt bei der DispatchEngine (spec-context-model § Foundation Engine Set).
// spekulativ — Refinement-Namen sind nicht vergeben, siehe B-O1
type PluginCommands : union {
def loadModule(ref: ModuleRef) ->{Throw[LoadError]} ModuleInfo
def unloadModule(ref: ModuleRef) ->{Throw[UnloadError]} Unit
} :> Timeout(5s)
type PluginEvents : union {
def logMessage(entry: LogEntry) ->{} Unit
def progress(pct: Percent) ->{} Unit
} :> OverrunPolicy(DropOldest(256))
type PluginBridge : blueprint {
commands: PluginCommands
events: PluginEvents
}
Oben ein Produkt, unten eine Summe. Eine Bridge hat alle Gruppen gleichzeitig; eine Nachricht ist immer genau eine Operation. Ein Blueprint kann das Zweite nicht ausdrücken, eine Union das Erste nicht.
Die Union ist damit kein wegoptimierbarer Zwischenschritt, sondern die einzige Form, in der eine noch unbekannte Operation ein Wert sein kann — also das, was kodiert und zugestellt wird.
Was daraus abgeleitet wird [D]. Aus jeder Gruppe entsteht ein Protocol mit einer Methode je Operation; Parameter, Row und Ergebnis werden unverändert übernommen und um den Empfänger ergänzt. Wer eine Gruppe bedient, liefert ein provide dafür; die Vollständigkeitsprüfung des Protocols ersetzt eine Prüfung über die Union. Welche Seite welches Protocol liefert, entscheidet die Richtung (§1.3).
Die Ableitung ist eine Meta-Funktion und gehört nach jdl-meta-phase. Hier wird nur vorausgesetzt, dass es sie gibt. Ihr Name ist nicht vergeben.
Die Gruppierung ist keine Ordnung fürs Auge. Drei unabhängige Gründe:
Policies Queries sind wiederholbar, Commands nicht, Events können überlaufen
Vollständig pro Gruppe ein Protocol, pro Seite eine eigene Prüfung
Codec jede Gruppe ist ein eigener Nachrichtentyp
Regel B-1. Eine Gruppierung, die eine eigene Policy tragen soll, muss ein Typ sein. Ein Feld kann keine tragen.
Folgt aus der TypeSubject-Liste in 00-refinements §1.1: Felder stehen nicht darin, also gibt es an einem Feld keinen Ort für ein Refinement. Die Kehrseite gehört dazu — wo keine Policy hinsoll, bleibt ein Feld richtig; Namen kosten.
Die Regel gilt über Bridges hinaus. Der Context-Blueprint befolgt sie bereits: services: ServiceSet, resourceBindings: ResourceBindingSpec, limits: ContextLimits sind benannte Typen und keine anonymen Listen.
Der Bridge-Blueprint beschreibt nur den Vertrag [D]. Er wird wie andere descriptor-getriebene Semantik materialisiert. Erst eine konkrete Bridge-Bindung beziehungsweise Context-Aktivierung erzeugt laufenden Zustand, typischerweise einen BridgeEndpoint als Resource.
Bridge Blueprint
↓ describe
BridgeDescriptor
+ Binding / Transport
↓ activate
BridgeEndpoint Resource
Damit braucht die Bridge keinen eigenen Sonderpfad instantiate neben dem Resource-/Context-Modell. Lebensdauer und Schließen gehören zur Endpoint-Resource und ihrer Binding-/Transportmechanik.
1.3 Richtung sitzt an der Gruppe [D]¶
Regel B-2. Die Richtung ist eine Eigenschaft der Gruppe, nicht der Bridge.
Ohne sie ist nicht entscheidbar, welche Seite welches Protocol implementieren muss: Zwei Gruppen sehen strukturell gleich aus, und dass events anders läuft als commands, wäre Konvention über einen Feldnamen.
Aus der Richtung folgt die Verteilung der abgeleiteten Protocols. Ein einziges Protocol über alle Gruppen ginge nicht — der Sender einer Gruppe müsste die Operationen implementieren, die er selbst schickt.
Damit ist auch der bidirektionale Fall beantwortet: kein eigener Rückkanal-Mechanismus, sondern eine Gruppe mit umgekehrter Richtung.
[O]Wie das Refinement heißt und welche Werte es kennt, ist offen. §1 nennt Richtung, Codec, Lebensdauer und Trust-Policy als Bestandteile der Oberfläche, ohne dass es Refinement-Namen dafür gäbe. Siehe B-O1.
1.4 Werte sind kodierbar; Resources werden übertragen [D]¶
Regel B-3. Jeder gewöhnliche Wert, der über eine Bridge geht, muss kodierbar sein. Eine Resource beziehungsweise ein Resource-Handle ist davon ausgenommen: Es darf die Grenze nur über eine ausdrücklich deklarierte Resource-Transferform überschreiten.
Für gewöhnliche Werte gilt die Anforderung unabhängig von der Distanz, auch bei local. Andernfalls kompiliert eine Bridge lokal und bricht, sobald jemand sie auf einen anderen Prozess legt — und genau das soll eine reine Deployment-Änderung sein.
Ausgeführt wird die Kodierung von der CodecEngine (spec-context-model § Foundation Engine Set). Zwei Dinge sind dabei zu trennen:
Anforderung Compile-Zeit, gilt für gewöhnliche Bridge-Werte immer
Kodieren nur dort, wo eine Grenze es mechanisch verlangt
Ob bei local tatsächlich kodiert wird, ist offen. Entscheidend ist nur, dass dieselbe semantische Wertgrenze erhalten bleibt; ein lokaler Fast Path darf die Wire-/Copy-Garantien nicht verändern.
Move-/Copy-Semantik gewöhnlicher Werte folgt den allgemeinen Label-/Ownership-Regeln und wird hier nicht neu definiert.
Der Fehlerkanal fällt unter dieselbe Regel: Throw[LoadError] verlangt, dass LoadError kodierbar ist.
Für Resources gilt dagegen eine andere Semantik:
gewöhnlicher Wert → Kopie / Move / Codec
Resource-Handle → Lease / Transfer / attenuiertes Handle / ausdrücklich geteiltes Handle
Ein Resource-Handle darf nie dadurch Autorität über eine Context-Grenze tragen, dass seine Bits wie gewöhnliche Nutzdaten kodiert werden. Die Bridge muss die Resource-Übertragung ausdrücklich autorisieren und die entstehende Autorität bestimmen (spec-resource-model §10).
Die Wire-Prüfung ist weiterhin der erste belegte Träger für die drei Wire-Regeln aus
04-qualifier§6.4 — jedes Feld genau ein Tag, Tags eindeutig, Feldtyp selbstWireTypeoder verlustfrei darauf abbildbar. Sie gelten für gewöhnliche Daten, nicht als Ersatz für Resource-Transfersemantik.
1.5 Woran die Distanzprüfung arbeitet [D]¶
Regel B-4. Der Abgleich zwischen Operationsformen und Distanz findet in
describestatt und arbeitet ausschließlich gegen materialisierte Werte, nie gegen die CompilerDB.
Der Zeitpunkt folgt der Staffelung aus spec-context-model §5 und ist hier nicht gesondert geregelt: Steht der Context statisch fest, läuft der Abgleich beim Übersetzen; wird der Context erst geladen, beim Laden; wird er erst zur Laufzeit adressiert, dort. Für ein nachgeladenes Plugin ist das der dritte Fall.
Genau deshalb darf die Prüfung nicht auf die Datenbank zugreifen. Was sie braucht, liest sie aus dem, was die Ableitung vorher in den Descriptor gelegt hat — und nur dann funktioniert sie an allen drei Zeitpunkten unverändert.
Damit zerfällt die Arbeit sauber:
Ableitung was aus der Beschreibung folgt — Kodierbarkeit, Tags, Operationsformen
describe was von der Bindung abhängt — Distanz, Erreichbarkeit, Fingerabdruck
Das ist spec-context-model §2.3 an einem konkreten Fall: Der Meta-Context endet nicht, er legt hin, was später gebraucht wird.
[O]Woran einer Signatur anzusehen ist, dass sie einen Strom beschreibt, ist nicht entschieden. Es hängt daran, ob Generatoren gewöhnliche Rückgabetypen sind; der bidirektionale Fall passt vermutlich nicht in eine Funktionssignatur.
2. Resource-Handles an der Grenze [D]¶
§1 behauptet: Ohne Bridge gibt es keinen Pfad, nicht adressierbar. Das ist eine stärkere Aussage als ein Policy-Verbot, und sie muss von Ring 0 getragen werden — sonst ist sie Prosa.
Der Fall, an dem sie sich entscheidet: Ein Handle aus Context A gelangt nach Context B. Wenn B damit auf dasselbe JME-Objekt zugreifen kann, existiert physisch ein Pfad, egal was der Descriptor sagt.
Regel B-5. Ein gewöhnliches Handle ist nur in seinem zulässigen Context adressierbar. Eine Bridge darf daraus ausschließlich eine Repräsentation erzeugen, die ihr BridgeDescriptor autorisiert.
Die Regel nennt die Eigenschaft, nicht den Mechanismus. Eine ContextId im Handle wäre eine mögliche Umsetzung, aber sie allein reicht nicht: Trüge ein von A nach B transportiertes Handle weiterhin ContextId = A, müsste B beim ersten Zugriff trappen — und dann funktionierte keine Bridge.
Was eine Bridge mit einem Handle tut, ist deshalb eine eigene Frage mit mindestens vier verschiedenen Antworten:
| Form | Was passiert |
|---|---|
| Kopie | aus gewöhnlichen Daten entsteht ein eigener Wert in B; keine Resource-Identität wandert |
| Lease | B bekommt kontrollierten, zeitlich oder logisch begrenzten Zugriff auf die Resource in A |
| Transfer | Eigentümerschaft und zulässige Adressierbarkeit der Resource wandern von A nach B |
| geteiltes Bridge-Handle | eine für beide ausdrücklich freigegebene Referenz mit eigener Transfer-/Lifetime-Policy |
Bei autoritätstragenden Resources ist Resource-Transfer zugleich Autoritätsübertragung. Eine Bridge darf deshalb aus einer Resource niemals stillschweigend stärkere Autorität erzeugen als die Quelle trägt.
Beispiel:
A besitzt:
DirectoryResource("/srv", read-write)
Bridge attenuiert und delegiert:
DirectoryResource("/srv/public", read-only)
B erhält:
read-only auf /srv/public
Nicht zulässig wäre eine Ableitung von read-only zu read-write oder von /srv/public zurück auf /srv.
Für process und node kann die mechanische Umsetzung über Proxies, Remote-Handles oder neu materialisierte Ressourcenrepräsentationen laufen; ein Resource-Handle selbst wird nicht als gewöhnlicher Wire-Wert serialisiert. Bei local und thread ist die konkrete Repräsentation weiterhin eine Entwurfsentscheidung.
Die schwächere Alternative wäre, „kein Pfad" als bloß semantische Adressierbarkeit zu lesen: Der Descriptor kennt keinen Weg, ein durchgereichtes Handle funktionierte aber. Für echte Isolation ist das zu wenig — eine Grenze darf nicht davon abhängen, dass fremder Code Handles freiwillig nicht weiterreicht.
Offen (B-O2 und
spec-resource-modelR-O2/R-O4): Transfersemantik pro Resource-Form, Repräsentation und die formale Attenuation der damit getragenen Autorität.
3. Abgrenzung zu Parent-Expose [N]¶
Parent-Expose
Aktivierungsbindung
Parent → vertrauenswürdiges Child
Services / ResourceBindingSpec / Policies
Bridge
Kommunikation nach Aktivierung
zwischen isolierten Domänen
Messages / Queries / Commands / expliziter Resource-Transfer
Regel B-6. Parent-Expose darf nicht als allgemeiner Runtime-Handle-Kanal verwendet werden.
4. Normative Invarianten¶
- Ohne Bridge gibt es zwischen isolierten Context-Domänen keinen Kommunikationspfad.
- Eine Bridge beschreibt eine Oberfläche; Endpunkte und Distanz gehören zur Bindung.
- Policy-tragende Gruppen sind Typen; Richtung sitzt an der Gruppe.
- Gewöhnliche Wire-Werte müssen kodierbar sein.
- Resource-Handles überschreiten die Grenze nur über eine explizite Transferform.
- Distanzprüfung arbeitet gegen materialisierte Descriptor-Semantik, nicht direkt gegen CompilerDB-Zustand.
- Parent-Expose ist kein allgemeiner Runtime-Transferkanal.
5. Offene Punkte¶
B-O1. BridgeDescriptor-Schema. Namen und genaue Felder für Richtung, Codec, Lifetime, Trust-Policy und Resource-Transfer.
B-O2. Resource-Transfer-Repräsentation. Konkrete Mechanik von Lease, Transfer und SharedHandle für local, thread, process und node.
B-O3. Streaming. Wie Stream-/bidirektionale Operationsformen aus Signaturen hervorgehen.
B-O4. Lokales Blockieren. Verbindung von local-Bridge, Timeout, Abbrechbarkeit und Scheduler.
B-O5. Endpoint-Provisionierung. Wer eine konkrete Bridge-Bindung öffnen und dafür Transport-/Endpoint-Resources provisionieren darf. Das Beschreiben eines Bridge-Vertrags ist davon getrennt und nicht selbst die privilegierte Operation.
B-O6. Lokaler Wert-Fast-Path. Ob local gewöhnliche Werte tatsächlich serialisiert oder nur eine semantisch äquivalente Kopie/Move-Operation ausführt.