---
title: "ADR-013: Rust FFI preload-only mode"
manual: "nr-vault"
version: "1.0"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-vault:adr-013-rust-ffi-preload@1.0"
source: "Developer/Adr/ADR-013-RustFfiPreload.rst"
rendered: "2026-09-18T07:37:50+00:00"
---

# ADR-013: Rust FFI preload-only mode {#adr-013-rust-ffi-preload-only-mode}

**Table of contents**

-   [Status](https://docs.typo3.org/permalink/netresearch/nr-vault:status@1.0)
-   [Date](https://docs.typo3.org/permalink/netresearch/nr-vault:date@1.0)
-   [Context](https://docs.typo3.org/permalink/netresearch/nr-vault:context@1.0)
-   [Decision](https://docs.typo3.org/permalink/netresearch/nr-vault:decision@1.0)
-   [Consequences](https://docs.typo3.org/permalink/netresearch/nr-vault:consequences@1.0)
-   [Alternatives considered](https://docs.typo3.org/permalink/netresearch/nr-vault:alternatives-considered@1.0)
-   [Related decisions](https://docs.typo3.org/permalink/netresearch/nr-vault:related-decisions@1.0)

## Status {#status}

Accepted

## Date {#date}

2026-01-12

## Context {#context}

PHP FFI is powerful but increases attack surface if enabled broadly. Dynamic
`FFI::cdef()` at runtime allows binding arbitrary native symbols, which is
risky in web contexts.

We need a production-safe operational model that reduces risk and keeps behavior
predictable.

## Decision {#decision}

If we ship/use a Rust FFI transport, we require:

-   Production deployments run with `ffi.enable=preload` (or an equivalent
    hardened configuration)
-   FFI bindings are created in preload (e.g. `opcache.preload`) and not
    dynamically in request handling
-   The PHP layer exposes only a limited wrapper API (no arbitrary symbol access)
-   We provide a non-FFI fallback transport and keep it as the default

## Consequences {#consequences}

### Positive {#positive}

-   Smaller attack surface vs full runtime FFI
-   More predictable behavior and better operability
-   Easier to audit what native code is actually callable

### Negative {#negative}

-   Requires ops work (preload configuration)
-   Some hosting environments will still refuse FFI entirely → fallback must work

## Alternatives considered {#alternatives-considered}

### Enable full FFI at runtime {#enable-full-ffi-at-runtime}

Use `ffi.enable=true` to allow dynamic FFI calls.

**Rejected**: unacceptable risk in typical web hosting setups.

### Use ext-php-rs or custom PHP extension {#use-ext-php-rs-or-custom-php-extension}

Build a native PHP extension in Rust.

**Deferred**: could be considered later, but increases maintenance and build
complexity.

## Related decisions {#related-decisions}

-   [ADR-012: SecureHttpClient API and transports](https://docs.typo3.org/permalink/netresearch/nr-vault:adr-012-secure-http-transports@1.0) \- Transport abstraction that uses FFI
-   [ADR-014: Packaging native artifacts](https://docs.typo3.org/permalink/netresearch/nr-vault:adr-014-packaging-native@1.0) \- How Rust artifacts are distributed
