Protocol and limits
PAM Native uses a versioned, bounded, little-endian binary protocol between persistent PHP, the Rust engine, Android, and iOS.
Compatibility
Section titled “Compatibility”| 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 types
Section titled “Frame types”| 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.
Transactional recovery
Section titled “Transactional recovery”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.
Patch operations
Section titled “Patch operations”| Operation | ID |
|---|---|
| Create subtree | 1 |
| Remove node | 2 |
| Update property | 3 |
| Move node | 4 |
| Set root | 5 |
Runtime limits
Section titled “Runtime limits”| 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.
Golden gates
Section titled “Golden gates”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.
Canonical enums
Section titled “Canonical enums”The canonical tables live in:
- PHP:
NodeKind,PropKey,EventKind, andNativeOperation; - Rust:
pam-native-protocol/src/lib.rs; and - Android:
PamProtocol.kt,PamRenderer.kt, andNativeModuleRegistry.kt; and - iOS:
PamProtocol.swift,PamRenderer.swift, andNativeModuleRegistry.swift.