> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everabyte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP quickstart

> Encrypt files client-side in PHP through the C ABI of the Everabyte Crypto SDK (PHP 8 FFI) — a complete zero-knowledge round trip.

PHP integrates through the SDK's **C ABI** (`include/evb_e2ee.h`) using the PHP 8 FFI extension. The native library is `libevb_e2ee_ffi` (built from `bindings/dart/rust`, or fetched from the repository's GitHub Releases).

## 1. Install

```bash theme={null}
# Build the shared library once (Rust toolchain on a build machine)
git clone https://github.com/everabyte/crypto-sdk.git
cd crypto-sdk/bindings/dart/rust && cargo build --release
# → target/release/libevb_e2ee_ffi.so   (Windows: .dll, macOS: .dylib)
```

```php theme={null}
<?php
$ffi = FFI::cdef(
    file_get_contents(__DIR__ . '/evb_e2ee.h'),
    __DIR__ . '/libevb_e2ee_ffi.so'
);
```

`evb_e2ee.h` ships in the repository (`include/evb_e2ee.h`) — copy it next to your PHP file.

## 2. Account key (UMK) — once, on your server

```php theme={null}
$umk = $ffi->new("uint8_t[96]");
$len = $ffi->new("uint64_t");
assert($ffi->evb_generate_umk($umk, 96, $len) === 0);

// Public keys (JSON string) — the ONLY material registered with Everabyte.
$json = $ffi->new("char[2048]");
assert($ffi->evb_umk_public_keys($umk, $umk + 32, $json, 2048) === 0);
$publics = json_decode(FFI::string($json));

// Seal the UMK under a 12-word BIP-39 recovery phrase (shown once, stored
// outside Everabyte).
$salt  = $ffi->new("uint8_t[16]");
$nonce = $ffi->new("uint8_t[12]");
$ffi->evb_random(16, $salt, 16, $len);
$ffi->evb_random(12, $nonce, 12, $len);
$blob = $ffi->new("char[8192]");
$mnemonic = obtainFreshBip39PhraseFromYourRecoveryUi(); // never hard-code a published test phrase
assert($ffi->evb_wrap_umk_with_mnemonic(
    $umk, $umk + 32, $mnemonic, $salt, $nonce, $blob, 8192
) === 0);
// POST /e2ee/keys/register with publics + wraps:[{wrapKind:'mnemonic',wrappedBlob}]
```

Persist the raw `$umk` bytes in **your** secret store (KMS, encrypted vault). Everabyte never receives them.

## 3. Encrypt → upload → attach envelope

```php theme={null}
$plaintext = file_get_contents('contract.pdf');

$dek        = $ffi->new("uint8_t[32]");
$fileNonce  = $ffi->new("uint8_t[12]");
$len        = $ffi->new("uint64_t");
$ffi->evb_random(32, $dek, 32, $len);
$ffi->evb_random(12, $fileNonce, 12, $len);

$pt  = FFI::new(FFI::type('uint8_t[' . strlen($plaintext) . ']'), false);
FFI::memcpy($pt, $plaintext, strlen($plaintext));
$out = $ffi->new("uint8_t[" . (strlen($plaintext) + 65536 + 16) . "]");
assert($ffi->evb1_seal_all($dek, $fileNonce, $pt, strlen($plaintext), $out, FFI::sizeOf($out), $len) === 0);
$ciphertext = FFI::string($out, $len); // EVB1 payload

// Opaque wire name + encrypted display name.
$wireName = FFI::string($ffi->evb_derive_wire_alias($umk, $umk + 32, 'contract.pdf', FFI::new('char[128]'), 128));
$nameBlob = $ffi->new("char[512]");
$ffi->evb_encrypt_file_name($dek, 'contract.pdf', $fileNonce, $nameBlob, 512);

// DEK envelope (HPKE v2 JSON) — attach to the uploaded row.
$env  = $ffi->new("char[4096]");
$pkX  = FFI::new(FFI::type('uint8_t[32]'), false);
$pkK  = FFI::new(FFI::type('uint8_t[1184]'), false);
FFI::memcpy($pkX, base64_decode($publics->publicKeyX25519), 32);
FFI::memcpy($pkK, base64_decode($publics->publicKeyMlkem), 1184);
assert($ffi->evb_wrap_dek_to_umk($dek, $pkX, $pkK, $env, 4096) === 0);
$envelopeJson = FFI::string($env);
```

Upload with `POST /files/upload/{bucketId}` (multipart, filename = `$wireName`), then `POST /e2ee/files/by-path/envelope` (`bucketId`, `fileName`, `encryptedName`, envelope fields). Check both responses. On a timeout, reconcile before cleanup. After a confirmed failure, resolve the uploaded row by bucket and opaque wire name, confirm it has no envelope, and hard-delete it through the file API as required by the E2EE upload contract. Handle retention blocks explicitly.

## 4. Download → decrypt

```php theme={null}
// GET /e2ee/files/{id}/envelope + GET /files/{id}/stream?download=true
$dek = $ffi->new("uint8_t[32]");
assert($ffi->evb_unwrap_dek_from_umk($envelopeJson, $umk, $umk + 32, $dek, 32, $len) === 0);

$ct  = FFI::new(FFI::type('uint8_t[' . strlen($storedCiphertext) . ']'), false);
FFI::memcpy($ct, $storedCiphertext, strlen($storedCiphertext));
$pt2 = $ffi->new("uint8_t[" . strlen($storedCiphertext) . "]");
assert($ffi->evb1_open_all($dek, $ct, strlen($storedCiphertext), $pt2, FFI::sizeOf($pt2), $len) === 0);
$original = FFI::string($pt2, $len); // === the original plaintext
$realName = FFI::string($ffi->evb_decrypt_file_name($dek, $encryptedName, FFI::new('char[256]'), 256, $len));
```

Error handling: every call returns `0` on success, negative otherwise; read `evb_last_error(buf, cap)` (call once with `null` to get the required capacity).

## Next

* [C ABI reference](https://docs.everabyte.com/sdk/c-abi-reference.md) — every symbol and convention (shared by PHP / Java / C# / Go).
* [Node.js quickstart](https://docs.everabyte.com/sdk/quickstart-nodejs.md) — the same round trip without a native library.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.