Kapitel 5. Adapter-Schicht

Die Adapter-Schicht ist die Grenze zwischen Fayger und der Host_Environment. Sie empfängt Universal_Instructions von der Runtime-Schicht und übersetzt sie in Systemaufrufe, die der Host versteht; sie normalisiert Host-Ereignisse zu Universal_Events und sendet sie zurück an die Runtime-Schicht.

Einen neuen Host hinzuzufügen erfordert nur einen neuen Platform_Adapter — Runtime- und Loader-Schicht bleiben unverändert.

5.1 Datenform der Universal_Instruction

struct UniversalInstruction {
  category: InstructionCategory
  opcode: u16
  operands: TLV[]
  capabilities_required: Set<Capability>
}

struct UniversalEvent {
  category: EventCategory
  opcode: u16
  payload: TLV[]
}

Designnotizen:

  • category ist die grobe Kategorie (io / net / ui / time / random / crypto / proc / host).
  • opcode ist die Nummer innerhalb der Kategorie (2 Byte); das oberste Bit ist für experimentelle Anweisungen reserviert.
  • operands ist eine TLV-Sequenz (type-length-value) für sprachübergreifende Dekodierung und Erweiterbarkeit.
  • capabilities_required ist die Capability-Menge, die diese Anweisung benötigt; sie speist das Capability-Trimming.

Kategorien (erste Phase)

KategorieBeschreibungÜbernommen von
ioDateien / StandardströmePOSIX read / write
netNetzwerk-I/OWASI sock_*
uiUI-Rendering und EingabeDOM / Terminal-TUI / Canvas
timeUhren und Timerclock_time_get
randomZufallszahlenrandom_get
cryptoKryptografie-PrimitiveWASI crypto
procProzess / Thread / SignalPOSIX
hostHost-spezifische AufrufeJNI / extism

8 reservierte Bits stehen für künftige Kategorien bereit.

Encoding

Jede Anweisung wird so codiert:

+--------+--------+----------+-------------------+
| cat(1) | op(2)  | caps(2)  | operands(TLV...)  |
+--------+--------+----------+-------------------+
  • cat: 1 Byte; obere Bits für Flags reserviert.
  • op: 2 Byte.
  • caps: 2 Byte Capability-Bitmap (16 niedrige Bits in Phase 1; ein Erweiterungssegment für mehr).
  • operands: jeder Operand ist (type:1, length:varint, value).

5.2 Platform_Adapter-Abstraktion

interface Platform_Adapter {
  descriptor() -> AdapterDescriptor
  match(host: HostEnvironment) -> bool
  translate(instr: UniversalInstruction) -> Result<HostSyscall, AdapterError>
  normalize(raw: HostEvent) -> Result<UniversalEvent, AdapterError>
}

struct AdapterDescriptor {
  platform_id: String
  supported_capabilities: Set<Capability>
  supported_instructions: Set<InstructionId>
}

Jeder Adapter beschreibt seine unterstützten Capabilities und Anweisungen selbst, sodass Capability-Trimming und „nicht unterstützte Anweisung"-Entscheidungen lokale Tabellen-Lookups sind, ohne Trial-and-Error zur Laufzeit.

5.3 Host_Environment-Erkennung und Adapterauswahl

interface HostEnvironmentDetector {
  detect() -> HostEnvironment
}

struct HostEnvironment {
  platform_id: String
  os_kind: OsKind
  arch: Arch
  sandbox_kind: SandboxKind
  runtime_features: Set<Feature>
}

Auswahlfluss beim Start:

flowchart TB
    Start([Fayger-Start]) --> Detect[HostEnvironmentDetector.detect]
    Detect --> Match[Adapter filtern: a.match host = true]
    Match --> Empty{leer?}
    Empty -- ja --> Err[ADP_NO_MATCHING_PLATFORM<br/>nicht serviceable]
    Empty -- nein --> Pick[Deterministische Auswahl<br/>nach Reihenfolge / Priorität a*]
    Pick --> Ready[Fayger bereit]

Deterministische Auswahl: Bei gleicher Adaptermenge und gleicher Host_Environment liefert wiederholter Start denselben Adapter. So wird das „Phantom-Adapter"-Problem vermieden.

5.4 Capability-Trimming

interface CapabilityNegotiator {
  negotiate(
    requested: Set<Capability>,
    available: Set<Capability>,
    policy: HostPolicy
  ) -> NegotiationResult
}

struct NegotiationResult {
  granted: Set<Capability>
  denied: Set<Capability>
  warnings: List<Capability>
}

Mengenalgebra des Trimmings:

  • granted = requested ∩ available ∩ policy
  • denied = requested \ granted
  • warnings: Capabilities, die der Host eingeschränkt bereitstellt (z. B. net.http unter den Fetch-Beschränkungen des Browsers).

Wenn manifest.required_capabilities \ granted ≠ ∅, muss start() einen Fehler liefern; context.missing entspricht der Differenz, die Instanz geht nicht in Running. Das ist die harte Regel „Nicht starten, wenn Capabilities fehlen".

Nicht deklarierte Capabilities sind für BuF unsichtbar (Default-Deny), entsprechend WASIs expliziter Host-Import-Semantik.

5.5 Bidirektionaler Übersetzungsfluss

sequenceDiagram
    participant Runtime as Runtime_Implementation
    participant Bus as Universal_Instruction-Bus
    participant CapNeg as CapabilityNegotiator
    participant Adapter as Platform_Adapter
    participant Host as Host_Environment

    Runtime->>Bus: emit(UI)
    Bus->>CapNeg: check(UI.capabilities_required)
    alt unzureichend
        CapNeg-->>Runtime: CapabilityDenied
    else ausreichend
        Bus->>Adapter: translate(UI)
        alt nicht unterstützt
            Adapter-->>Runtime: UnsupportedInstruction
        else unterstützt
            Adapter->>Host: HostSyscall
            Host-->>Adapter: HostResult / HostEvent
            Adapter->>Bus: normalize(HostEvent) -> UniversalEvent
            Bus->>Runtime: on_event(UniversalEvent)
        end
    end

Hinweise:

  • Capability-Prüfung erfolgt vor translate, um unnötige Downstream-Aufrufe zu vermeiden.
  • „Nicht unterstützte Anweisung" ist eine deterministische lokale Entscheidung (Tabellen-Lookup), die keinen Hostaufruf auslöst.
  • Host-Ereignisse kommen über normalize zurück; die Runtime berührt nie rohe Host-Ereignisstrukturen.

5.6 Eingebaute Adapter der ersten Phase

AdapterAnwendungsfallCapability-Trimming
NativeDesktop_AdapterLinux / macOS / Windows DesktopNahezu vollständige Capabilities; ui / proc / io / net voll verfügbar
Server_AdapterHeadless-Serverui deaktiviert; proc policy-eingeschränkt
Browser_AdapterWeb-API-basiertproc und ein Großteil von io deaktiviert; net auf fetch / WebSocket beschränkt; starkes Trimming
InApp_AdapterEingebettet in einen Host-AnwendungsprozessCapabilities werden vom Host explizit injiziert; am restriktivsten; verfügbare host-Opcodes deklariert der Host

Diese Adapter sind Referenzimplementierungen. Dritte können eigene Adapter registrieren, etwa eingebettete Endgeräte oder spezifische IDE-Einbettungen.

5.7 Plattformübergreifende Konsistenz

Dasselbe BuF, geladen und ausgeführt auf verschiedenen Platform_Adaptern, muss der Runtime dieselbe Lifecycle_State-Sequenz und dieselbe Universal_Instruction-Sequenz zeigen:

  • Jegliche Differenz, die zur Sequenz-Divergenz führen würde, muss vor start durch Capability-Trimming abgelehnt werden, nicht stillschweigend zur Laufzeit divergiert werden.
  • Optimierungen mit „äquivalent, aber anderer Pfad" dürfen verschiedene Pfade nicht in der Universal_Instruction-Sequenz exponieren.

Diese Eigenschaft wird per PBT verifiziert, indem ein Paar Mock-Adapter mit gleichen Capabilities parallel läuft und die Sequenzen verglichen werden.

5.8 Fehlercodes

Stabile Fehlercodes der Adapter-Schicht:

FehlercodeAuslöser
ADP_NO_MATCHING_PLATFORMKein Adapter passt zum aktuellen Host
ADP_UNSUPPORTED_INSTRUCTIONAktueller Adapter unterstützt die Anweisung nicht
ADP_CAPABILITY_DENIEDNach Trimming reichen die Capabilities nicht für die Anweisung
ADP_HOST_CALL_FAILEDNach Übersetzung in Systemaufruf scheiterte die Hostseite

ADP_UNSUPPORTED_INSTRUCTION darf keinen Hostaufruf auslösen; nur der Fehler wird zurückgegeben. Das ist der „Default-Deny"-Stand auf Anweisungsebene.