Skip to content

Protocol and limits

PAM Native uses a versioned, bounded, little-endian binary protocol between persistent PHP, the Rust engine, Android, and iOS.

SDK Protocol PHP Android
pushinbr/pam-native 0.5.x 1 8.4–8.5 API 26–36

The Swift peer decodes protocol version 1 and rejects unknown node kinds, invalid property values, oversized frames, malformed lengths, and trailing bytes before UIKit mutation.

All three peers must support the exact protocol version. A mismatch is rejected before applying a node or mutation.

Protocol identifiers are sequential integers beginning at 1. Existing identifiers are never renamed, reused, or renumbered.

New optional properties, node kinds, events, and operations may be appended. Changing an existing field’s representation or meaning requires a new protocol version and an explicit compatibility adapter.

Protocol version 1 currently defines properties through ID 400 and events through ID 34.

Frame Magic Producer Consumer
Complete tree PNT1 PHP Rust
Incremental patch PNP1 PHP Rust
UI mutation batch PNB1 Rust Android or iOS

Each frame starts with a four-byte magic value, a u16 protocol version, and bounded payload counts. Strings and opaque values use a u32 byte length. Node IDs are non-zero u64 values.

Decoders reject:

  • duplicate node IDs or properties;
  • cycles and disconnected trees;
  • invalid enum values;
  • trailing bytes;
  • malformed lengths; and
  • payloads above the published bounds.

Patch application is transactional. If the Rust engine rejects a PNP1 incremental patch, the retained tree is rolled back to its last committed state. The PHP encoder keeps component and element identities, discards only its previous wire snapshot, and immediately retries with one complete PNT1 tree.

Android and iOS do not open an error overlay for this recoverable path. They write the precise engine failure to development logs through the stable pam_native_engine_last_error C API. A rejected complete tree remains a visible runtime error because it cannot be repaired safely.

Operation ID
Create subtree 1
Remove node 2
Update property 3
Move node 4
Set root 5
Resource Limit
Complete frame 16 MiB
Nodes 100,000
Tree depth 512
Properties per node 128
String or opaque property 1 MiB
Queued native event payload 1 MiB

These are part of protocol version 1. They protect memory use and decoder complexity and must not be raised silently for a single application.

Rust tests pin byte-for-byte tree, patch, and mutation frames. PHP tests pin sequential enums and deterministic encoding. Android and iOS check the protocol version before decoding.

Changing a golden frame under protocol version 1 is a release blocker, not a routine snapshot update.

The canonical tables live in:

  • PHP: NodeKind, PropKey, EventKind, and NativeOperation;
  • Rust: pam-native-protocol/src/lib.rs; and
  • Android: PamProtocol.kt, PamRenderer.kt, and NativeModuleRegistry.kt; and
  • iOS: PamProtocol.swift, PamRenderer.swift, and NativeModuleRegistry.swift.