Episode ini membahas cara menjaga batas antara business logic dan native modules, memodularisasi bundle serta memakai konteks ephemeral untuk isolasi, dan menjalankan skrip yang tidak dipercaya dengan aman di dalam embedding Hermes.

Episode 13 menutup dengan keyakinan: AOT tanpa JIT memberi Hermes profil keamanan yang menarik, asal dilengkapi signed bundles dan hardening. Episode 14 memperbesar fokus dari level runtime ke level arsitektur. Pertanyaannya bukan lagi bagaimana engine bekerja, melainkan bagaimana kita menata aplikasi agar batas kepercayaannya jelas.
Roadmap episode ini: menjaga boundary antara business logic dan native modules, modularisasi bundle dan ephemeral contexts, lalu handling untrusted scripts dengan aman pada embedding.
Di aplikasi Hermes ada dua dunia: JavaScript yang berjalan di atas engine, dan native code yang berkomunikasi dengannya lewat JSI. Batas di antara keduanya adalah garis kepercayaan. Aturannya sederhana — native module adalah wilayah tepercaya, JavaScript adalah wilayah yang bisa berubah-ubah. Semakin sedikit privilege yang dimiliki JavaScript, semakin kecil kerusakan kalau kodenya disusupi.
Artinya, logika sensitif seperti pembayaran, kriptografi, dan akses keychain jangan diimplementasikan di JavaScript. Pindahkan ke native module yang dibungkus API minimal:
class PaymentService {
constructor({ gateway }) {
this.gateway = gateway;
}
async charge(userId, amount) {
return this.gateway.charge(userId, amount);
}
}Business logic tetap boleh hidup di JavaScript — itu kelebihan engine — tetapi setiap operasi dengan efek samping penting harus melewati facade yang dipasok native. JavaScript tidak pernah memegang kunci, hanya meminta layanan.
Pola yang berguna adalah menaruh native module di balik antarmuka sendiri, supaya komponen UI tidak menyentuh native langsung. Kalau nanti implementasi native diganti, misalnya pindah dari modul lama ke modul baru, yang berubah cukup adapter:
const storage = {
async save(key, value) {
return NativeModules.KeyValueStore.set(key, value);
},
};
async function persistSession(session) {
await storage.save("session", JSON.stringify(session));
}Dengan pola ini, batas arsitektur menjadi nyata di kode, bukan sekadar janji di dokumen. Setiap lapisan tahu persis apa yang boleh dipanggilnya, dan setiap akses native bisa diaudit lewat satu titik.
Aplikasi yang sehat tidak memuat semua kode sekaligus. Metro mendukung pemecahan bundle, dan Hermes mengeksekusi modul dengan lazy require. Pola dasarnya: panggil require hanya ketika modul benar-benar dibutuhkan.
function openSettings() {
require("./screens/SettingsScreen").mount();
}Modularisasi memberi dua keuntungan: waktu startup lebih singkat karena lebih sedikit bytecode yang dimuat, dan permukaan eksekusi lebih kecil. Di embedding, pemecahan ini juga berarti kalian bisa memuat bundle pihak ketiga sebagai unit tersendiri — lalu mencabutnya lagi tanpa memengaruhi aplikasi inti.
Info
Modularisasi bukan hanya soal performa. Bundle yang terpisah lebih mudah diaudit dan bisa ditandatangani serta diverifikasi secara independen satu per satu.
Untuk kode yang tidak dipercaya — plugin, skrip user, atau ekstensi — isolasi penuh lebih penting daripada kecepatan. Strateginya adalah menjalankan skrip di runtime baru yang dibuat khusus, diberi API yang terbatas, lalu dibuang begitu selesai. Membuat runtime baru itu semudah memanggil HermesRuntime::make():
auto isolated = HermesRuntime::make();
isolated->global().setProperty(
*isolated,
"safeApi",
safeApi.getObject(*isolated));
auto result = isolated->evaluateJavaScript(
facebook::jsi::String::createFromUtf8(
*isolated, untrustedCode),
"plugin.js");
isolated.reset();Runtime yang di-reset akan membebaskan semua state-nya: objek, heap, dan scope global. Tidak ada state yang bocor ke eksekusi berikutnya. Inilah alasan konteks ephemeral aman — tidak peduli apa yang dilakukan skrip di dalamnya, dampaknya dikurung dalam batas hidup runtime tersebut.
Terakhir, beberapa aturan keras untuk embedding yang menjalankan kode asing:
const ALLOWED_HOSTS = ["log", "httpRequest"];
function evaluateUntrusted(code) {
const ctx = new HermesContext({
expose: (name) => ALLOWED_HOSTS.includes(name),
timeoutMs: 1000,
});
return ctx.evaluate(code);
}Prinsipnya sama di semua level: skrip asing berjalan di arena yang kecil, dengan aturan yang jelas, dan dievakuasi begitu selesai. Kalau kalian tidak bisa membatasi skrip, maka kalian tidak seharusnya menjalankannya.
Arsitektur yang baik untuk Hermes adalah arsitektur yang jujur tentang batas: native module tepercaya di belakang facade, JavaScript memegang business logic tanpa privilege berlebih, bundle dimodularisasi, dan kode asing dikurung dalam konteks ephemeral yang dibuang setelah dipakai.
Inti yang harus dibawa pulang:
Di episode 15 kita berbalik ke performa: Performance Optimization Strategies — pola kode JavaScript yang paling ramah Hermes, cara menghindari alokasi di hot path dan biaya runtime polymorphism, serta profiling hints khas Hermes. Sampai jumpa!